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 }