001 /* 002 * JBoss, Home of Professional Open Source. 003 * Copyright 2008, Red Hat Middleware LLC, and individual contributors 004 * as indicated by the @author tags. See the copyright.txt file in the 005 * distribution for a full listing of individual contributors. 006 * 007 * This is free software; you can redistribute it and/or modify it 008 * under the terms of the GNU Lesser General Public License as 009 * published by the Free Software Foundation; either version 2.1 of 010 * the License, or (at your option) any later version. 011 * 012 * This software is distributed in the hope that it will be useful, 013 * but WITHOUT ANY WARRANTY; without even the implied warranty of 014 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 015 * Lesser General Public License for more details. 016 * 017 * You should have received a copy of the GNU Lesser General Public 018 * License along with this software; if not, write to the Free 019 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 020 * 02110-1301 USA, or see the FSF site: http://www.fsf.org. 021 */ 022 package org.jboss.dna.connector.federation; 023 024 import java.util.List; 025 import java.util.concurrent.CopyOnWriteArrayList; 026 import java.util.concurrent.CountDownLatch; 027 import java.util.concurrent.TimeUnit; 028 import java.util.concurrent.atomic.AtomicBoolean; 029 import java.util.concurrent.atomic.AtomicInteger; 030 import net.jcip.annotations.ThreadSafe; 031 import org.jboss.dna.common.util.CheckArg; 032 import org.jboss.dna.connector.federation.executor.FederatingCommandExecutor; 033 import org.jboss.dna.graph.ExecutionContext; 034 import org.jboss.dna.graph.connectors.RepositoryConnection; 035 import org.jboss.dna.graph.connectors.RepositoryConnectionFactory; 036 import org.jboss.dna.graph.connectors.RepositorySource; 037 import org.jboss.dna.graph.connectors.RepositorySourceListener; 038 import org.jboss.dna.graph.requests.processor.RequestProcessor; 039 040 /** 041 * The component that represents a single federated repository. The federated repository uses a set of {@link RepositorySource 042 * federated connectionFactory} as designated by name through the {@link #getConfiguration() configuration}, and provides the 043 * logic of interacting with those connectionFactory and presenting a single unified graph. 044 * 045 * @author Randall Hauch 046 */ 047 @ThreadSafe 048 public class FederatedRepository { 049 050 private final ExecutionContext context; 051 private final RepositoryConnectionFactory connectionFactory; 052 private FederatedRepositoryConfig config; 053 private final AtomicInteger openExecutors = new AtomicInteger(0); 054 private final CountDownLatch shutdownLatch = new CountDownLatch(1); 055 private final AtomicBoolean shutdownRequested = new AtomicBoolean(false); 056 private final CopyOnWriteArrayList<RepositorySourceListener> listeners = new CopyOnWriteArrayList<RepositorySourceListener>(); 057 058 /** 059 * Create a federated repository instance. 060 * 061 * @param context the execution context 062 * @param connectionFactory the factory for {@link RepositoryConnection} instances that should be used 063 * @param config the configuration for this repository 064 * @throws IllegalArgumentException if any of the parameters are null, or if the name is blank 065 */ 066 public FederatedRepository( ExecutionContext context, 067 RepositoryConnectionFactory connectionFactory, 068 FederatedRepositoryConfig config ) { 069 CheckArg.isNotNull(connectionFactory, "connectionFactory"); 070 CheckArg.isNotNull(context, "context"); 071 CheckArg.isNotNull(config, "config"); 072 this.context = context; 073 this.connectionFactory = connectionFactory; 074 this.config = config; 075 } 076 077 /** 078 * Get the name of this repository 079 * 080 * @return name 081 */ 082 public String getName() { 083 return this.config.getName(); 084 } 085 086 /** 087 * @return the execution context 088 */ 089 public ExecutionContext getExecutionContext() { 090 return context; 091 } 092 093 /** 094 * @return the connectionFactory 095 */ 096 protected RepositoryConnectionFactory getConnectionFactory() { 097 return connectionFactory; 098 } 099 100 /** 101 * Utility method called by the administrator. 102 */ 103 public synchronized void start() { 104 // Do not establish connections to the connectionFactory; these will be established as needed 105 } 106 107 /** 108 * Return true if this federated repository is running and ready for connections. 109 * 110 * @return true if running, or false otherwise 111 */ 112 public boolean isRunning() { 113 return this.shutdownRequested.get() != true; 114 } 115 116 /** 117 * Utility method called by the administrator. 118 */ 119 public synchronized void shutdown() { 120 this.shutdownRequested.set(true); 121 if (this.openExecutors.get() <= 0) shutdownLatch.countDown(); 122 } 123 124 /** 125 * Utility method called by the administrator. 126 * 127 * @param timeout 128 * @param unit 129 * @return true if all connections open at the time this method is called were {@link RepositoryConnection#close() closed} in 130 * the supplied time, or false if the timeout occurred before all the connections were closed 131 * @throws InterruptedException 132 */ 133 public boolean awaitTermination( long timeout, 134 TimeUnit unit ) throws InterruptedException { 135 // Await until all connections have been closed, or until the timeout occurs 136 return shutdownLatch.await(timeout, unit); 137 } 138 139 /** 140 * Return true if this federated repository has completed its termination and no longer has any open connections. 141 * 142 * @return true if terminated, or false otherwise 143 */ 144 public boolean isTerminated() { 145 return this.openExecutors.get() != 0; 146 } 147 148 /** 149 * Add a listener that is to receive notifications to changes to content within this repository. This method does nothing if 150 * the supplied listener is null. 151 * 152 * @param listener the new listener 153 * @return true if the listener was added, or false if the listener was not added (if reference is null, or if non-null 154 * listener is already an existing listener) 155 */ 156 public boolean addListener( RepositorySourceListener listener ) { 157 if (listener == null) return false; 158 return this.listeners.addIfAbsent(listener); 159 } 160 161 /** 162 * Remove the supplied listener. This method does nothing if the supplied listener is null. 163 * <p> 164 * This method can safely be called while the federation repository is in use. 165 * </p> 166 * 167 * @param listener the listener to remove 168 * @return true if the listener was removed, or false if the listener was not registered 169 */ 170 public boolean removeListener( RepositorySourceListener listener ) { 171 if (listener == null) return false; 172 return this.listeners.remove(listener); 173 } 174 175 /** 176 * Get the list of listeners, which is the actual list used by the repository. 177 * 178 * @return the listeners 179 */ 180 public List<RepositorySourceListener> getListeners() { 181 return this.listeners; 182 } 183 184 /** 185 * Authenticate the supplied username with the supplied credentials, and return whether authentication was successful. 186 * 187 * @param source the {@link RepositorySource} that should be affiliated with the resulting connection 188 * @param username the username 189 * @param credentials the credentials 190 * @return the repository connection if authentication succeeded, or null otherwise 191 */ 192 public RepositoryConnection createConnection( RepositorySource source, 193 String username, 194 Object credentials ) { 195 return new FederatedRepositoryConnection(this, source.getName()); 196 } 197 198 /** 199 * Get the configuration of this repository. This configuration is immutable and may be 200 * {@link #setConfiguration(FederatedRepositoryConfig) changed} as needed. Therefore, when using a configuration and needing a 201 * consistent configuration, maintain a reference to the configuration during that time (as the actual configuration may be 202 * replaced at any time). 203 * 204 * @return the repository's configuration at the time this method is called. 205 */ 206 public FederatedRepositoryConfig getConfiguration() { 207 return config; 208 } 209 210 /** 211 * Set the configuration for this repository. The configuration is immutable and therefore may be replaced using this method. 212 * All interaction with the configuration is done in a thread-safe and concurrent manner, and as such only valid 213 * configurations should be used. 214 * 215 * @param config the new configuration 216 * @throws IllegalArgumentException if the configuration is null 217 */ 218 public void setConfiguration( FederatedRepositoryConfig config ) { 219 CheckArg.isNotNull(config, "config"); 220 this.config = config; 221 } 222 223 /** 224 * Called by {@link FederatedRepositoryConnection#execute(ExecutionContext, org.jboss.dna.graph.requests.Request)}. 225 * 226 * @param context the execution context in which the executor will be run; may not be null 227 * @param sourceName the name of the {@link RepositorySource} that is making use of this executor; may not be null or empty 228 * @return the executor 229 */ 230 protected RequestProcessor getProcessor( ExecutionContext context, 231 String sourceName ) { 232 FederatedRepositoryConfig config = this.getConfiguration(); 233 return new FederatingCommandExecutor(context, sourceName, config.getCacheProjection(), config.getDefaultCachePolicy(), 234 config.getSourceProjections(), getConnectionFactory()); 235 } 236 237 /** 238 * Called by {@link FederatedRepositoryConnection#FederatedRepositoryConnection(FederatedRepository, String)}. 239 * 240 * @param connection the connection being opened 241 */ 242 /*package*/void register( FederatedRepositoryConnection connection ) { 243 openExecutors.incrementAndGet(); 244 } 245 246 /** 247 * Called by {@link FederatedRepositoryConnection#close()}. 248 * 249 * @param connection the connection being closed 250 */ 251 /*package*/void unregister( FederatedRepositoryConnection connection ) { 252 if (openExecutors.decrementAndGet() <= 0 && shutdownRequested.get()) { 253 // Last connection, so turn out the lights ... 254 shutdownLatch.countDown(); 255 } 256 } 257 258 }