001 /*
002 * JBoss DNA (http://www.jboss.org/dna)
003 * See the COPYRIGHT.txt file distributed with this work for information
004 * regarding copyright ownership. Some portions may be licensed
005 * to Red Hat, Inc. under one or more contributor license agreements.
006 * See the AUTHORS.txt file in the distribution for a full listing of
007 * individual contributors.
008 *
009 * JBoss DNA is free software. Unless otherwise indicated, all code in JBoss DNA
010 * is licensed to you under the terms of the GNU Lesser General Public License as
011 * published by the Free Software Foundation; either version 2.1 of
012 * the License, or (at your option) any later version.
013 *
014 * JBoss DNA is distributed in the hope that it will be useful,
015 * but WITHOUT ANY WARRANTY; without even the implied warranty of
016 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
017 * Lesser General Public License for more details.
018 *
019 * You should have received a copy of the GNU Lesser General Public
020 * License along with this software; if not, write to the Free
021 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
022 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
023 */
024 package org.jboss.dna.connector.federation;
025
026 import java.util.Collections;
027 import java.util.HashMap;
028 import java.util.Map;
029 import java.util.concurrent.CountDownLatch;
030 import java.util.concurrent.TimeUnit;
031 import java.util.concurrent.atomic.AtomicBoolean;
032 import java.util.concurrent.atomic.AtomicInteger;
033 import net.jcip.annotations.ThreadSafe;
034 import org.jboss.dna.common.util.CheckArg;
035 import org.jboss.dna.graph.ExecutionContext;
036 import org.jboss.dna.graph.connector.RepositoryConnection;
037 import org.jboss.dna.graph.connector.RepositoryConnectionFactory;
038 import org.jboss.dna.graph.connector.RepositorySource;
039 import org.jboss.dna.graph.request.processor.RequestProcessor;
040
041 /**
042 * The component that represents a single federated repository. The federated repository uses a set of {@link RepositorySource
043 * federated connectionFactory} as designated by name through the {@link #getWorkspaceConfigurations() configurations}, and
044 * provides the logic of interacting with those connectionFactory and presenting a single unified graph.
045 *
046 * @author Randall Hauch
047 */
048 @ThreadSafe
049 public class FederatedRepository {
050
051 private final String name;
052 private final ExecutionContext context;
053 private final RepositoryConnectionFactory connectionFactory;
054 private final Map<String, FederatedWorkspace> workspaceConfigsByName;
055 private final FederatedWorkspace defaultWorkspace;
056 private final AtomicInteger openExecutors = new AtomicInteger(0);
057 private final CountDownLatch shutdownLatch = new CountDownLatch(1);
058 private final AtomicBoolean shutdownRequested = new AtomicBoolean(false);
059
060 /**
061 * Create a federated repository instance.
062 *
063 * @param repositoryName the name of the repository
064 * @param context the execution context
065 * @param connectionFactory the factory for {@link RepositoryConnection} instances that should be used
066 * @param workspaces the workspace configurations for this repository, with the default workspace being first; may not be null
067 * @throws IllegalArgumentException if any of the parameters are null, or if the name is blank
068 */
069 public FederatedRepository( String repositoryName,
070 ExecutionContext context,
071 RepositoryConnectionFactory connectionFactory,
072 Iterable<FederatedWorkspace> workspaces ) {
073 CheckArg.isNotEmpty(repositoryName, "repositoryName");
074 CheckArg.isNotNull(connectionFactory, "connectionFactory");
075 CheckArg.isNotNull(context, "context");
076 CheckArg.isNotNull(workspaces, "workspaces");
077 this.name = repositoryName;
078 this.context = context;
079 this.connectionFactory = connectionFactory;
080 FederatedWorkspace defaultWorkspace = null;
081 Map<String, FederatedWorkspace> configsByName = new HashMap<String, FederatedWorkspace>();
082 for (FederatedWorkspace workspace : workspaces) {
083 if (defaultWorkspace == null) defaultWorkspace = workspace;
084 configsByName.put(workspace.getName(), workspace);
085 }
086 this.workspaceConfigsByName = Collections.unmodifiableMap(configsByName);
087 this.defaultWorkspace = defaultWorkspace;
088 }
089
090 /**
091 * Get the name of this repository
092 *
093 * @return name
094 */
095 public String getName() {
096 return name;
097 }
098
099 /**
100 * @return the execution context
101 */
102 public ExecutionContext getExecutionContext() {
103 return context;
104 }
105
106 /**
107 * @return the connectionFactory
108 */
109 protected RepositoryConnectionFactory getConnectionFactory() {
110 return connectionFactory;
111 }
112
113 /**
114 * Utility method called by the administrator.
115 */
116 public synchronized void start() {
117 // Do not establish connections to the connectionFactory; these will be established as needed
118 }
119
120 /**
121 * Return true if this federated repository is running and ready for connections.
122 *
123 * @return true if running, or false otherwise
124 */
125 public boolean isRunning() {
126 return this.shutdownRequested.get() != true;
127 }
128
129 /**
130 * Utility method called by the administrator.
131 */
132 public synchronized void shutdown() {
133 this.shutdownRequested.set(true);
134 if (this.openExecutors.get() <= 0) shutdownLatch.countDown();
135 }
136
137 /**
138 * Utility method called by the administrator.
139 *
140 * @param timeout
141 * @param unit
142 * @return true if all connections open at the time this method is called were {@link RepositoryConnection#close() closed} in
143 * the supplied time, or false if the timeout occurred before all the connections were closed
144 * @throws InterruptedException
145 */
146 public boolean awaitTermination( long timeout,
147 TimeUnit unit ) throws InterruptedException {
148 // Await until all connections have been closed, or until the timeout occurs
149 return shutdownLatch.await(timeout, unit);
150 }
151
152 /**
153 * Return true if this federated repository has completed its termination and no longer has any open connections.
154 *
155 * @return true if terminated, or false otherwise
156 */
157 public boolean isTerminated() {
158 return this.openExecutors.get() != 0;
159 }
160
161 /**
162 * Authenticate the supplied username with the supplied credentials, and return whether authentication was successful.
163 *
164 * @param source the {@link RepositorySource} that should be affiliated with the resulting connection
165 * @param username the username
166 * @param credentials the credentials
167 * @return the repository connection if authentication succeeded, or null otherwise
168 */
169 public RepositoryConnection createConnection( RepositorySource source,
170 String username,
171 Object credentials ) {
172 return new FederatedRepositoryConnection(this, source.getName());
173 }
174
175 /**
176 * Get the configuration of this repository's workspaces. This set of configurations (as well as each configuration) is
177 * immutable. Therefore, when using a configuration and needing a consistent configuration, maintain a reference to the
178 * configuration during that time (as the actual configuration may be replaced at any time).
179 *
180 * @return the repository's worksapce configuration at the time this method is called.
181 */
182 public Map<String, FederatedWorkspace> getWorkspaceConfigurations() {
183 return workspaceConfigsByName;
184 }
185
186 /**
187 * Called by {@link FederatedRepositoryConnection#execute(ExecutionContext, org.jboss.dna.graph.request.Request)}.
188 *
189 * @param context the execution context in which the executor will be run; may not be null
190 * @param sourceName the name of the {@link RepositorySource} that is making use of this executor; may not be null or empty
191 * @return the executor
192 */
193 protected RequestProcessor getProcessor( ExecutionContext context,
194 String sourceName ) {
195 Map<String, FederatedWorkspace> workspaces = this.getWorkspaceConfigurations();
196 return new FederatingRequestProcessor(context, sourceName, workspaces, defaultWorkspace, getConnectionFactory());
197 }
198
199 /**
200 * Called by {@link FederatedRepositoryConnection#FederatedRepositoryConnection(FederatedRepository, String)}.
201 *
202 * @param connection the connection being opened
203 */
204 /*package*/void register( FederatedRepositoryConnection connection ) {
205 openExecutors.incrementAndGet();
206 }
207
208 /**
209 * Called by {@link FederatedRepositoryConnection#close()}.
210 *
211 * @param connection the connection being closed
212 */
213 /*package*/void unregister( FederatedRepositoryConnection connection ) {
214 if (openExecutors.decrementAndGet() <= 0 && shutdownRequested.get()) {
215 // Last connection, so turn out the lights ...
216 shutdownLatch.countDown();
217 }
218 }
219
220 }