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.repository; 023 024 import java.lang.reflect.InvocationTargetException; 025 import java.util.List; 026 import java.util.Map; 027 import java.util.concurrent.TimeUnit; 028 import java.util.concurrent.atomic.AtomicBoolean; 029 import net.jcip.annotations.ThreadSafe; 030 import org.jboss.dna.common.collection.Problems; 031 import org.jboss.dna.common.collection.SimpleProblems; 032 import org.jboss.dna.common.util.CheckArg; 033 import org.jboss.dna.common.util.Reflection; 034 import org.jboss.dna.connector.federation.FederationException; 035 import org.jboss.dna.graph.DnaLexicon; 036 import org.jboss.dna.graph.ExecutionContext; 037 import org.jboss.dna.graph.commands.GraphCommand; 038 import org.jboss.dna.graph.commands.basic.BasicCompositeCommand; 039 import org.jboss.dna.graph.commands.basic.BasicGetChildrenCommand; 040 import org.jboss.dna.graph.commands.basic.BasicGetNodeCommand; 041 import org.jboss.dna.graph.commands.executor.CommandExecutor; 042 import org.jboss.dna.graph.commands.executor.SingleSourceCommandExecutor; 043 import org.jboss.dna.graph.connectors.RepositorySource; 044 import org.jboss.dna.graph.properties.Name; 045 import org.jboss.dna.graph.properties.NameFactory; 046 import org.jboss.dna.graph.properties.Path; 047 import org.jboss.dna.graph.properties.PathFactory; 048 import org.jboss.dna.graph.properties.Property; 049 import org.jboss.dna.graph.properties.ValueFactories; 050 import org.jboss.dna.graph.properties.ValueFactory; 051 import org.jboss.dna.repository.services.AbstractServiceAdministrator; 052 import org.jboss.dna.repository.services.AdministeredService; 053 import org.jboss.dna.repository.services.ServiceAdministrator; 054 055 /** 056 * @author Randall Hauch 057 */ 058 @ThreadSafe 059 public class RepositoryService implements AdministeredService { 060 061 /** 062 * The administrative component for this service. 063 * 064 * @author Randall Hauch 065 */ 066 protected class Administrator extends AbstractServiceAdministrator { 067 068 protected Administrator() { 069 super(RepositoryI18n.federationServiceName, State.PAUSED); 070 } 071 072 /** 073 * {@inheritDoc} 074 */ 075 @Override 076 protected boolean doCheckIsTerminated() { 077 return true; 078 } 079 080 /** 081 * {@inheritDoc} 082 */ 083 @Override 084 protected void doStart( State fromState ) { 085 super.doStart(fromState); 086 startService(); 087 } 088 089 /** 090 * {@inheritDoc} 091 * 092 * @see org.jboss.dna.repository.services.ServiceAdministrator#awaitTermination(long, java.util.concurrent.TimeUnit) 093 */ 094 public boolean awaitTermination( long timeout, 095 TimeUnit unit ) { 096 return true; 097 } 098 } 099 100 private final ExecutionContext context; 101 private final RepositoryLibrary sources; 102 private final String configurationSourceName; 103 private final Path pathToConfigurationRoot; 104 private final Administrator administrator = new Administrator(); 105 private final AtomicBoolean started = new AtomicBoolean(false); 106 107 /** 108 * Create a service instance, reading the configuration describing new {@link RepositorySource} instances from the source with 109 * the supplied name. 110 * 111 * @param sources the source manager 112 * @param configurationSourceName the name of the {@link RepositorySource} that is the configuration repository 113 * @param context the execution context in which this service should run 114 * @throws IllegalArgumentException if the bootstrap source is null or the execution context is null 115 */ 116 public RepositoryService( RepositoryLibrary sources, 117 String configurationSourceName, 118 ExecutionContext context ) { 119 this(sources, configurationSourceName, null, context); 120 } 121 122 /** 123 * Create a service instance, reading the configuration describing new {@link RepositorySource} instances from the source with 124 * the supplied name and path within the repository. 125 * 126 * @param sources the source manager 127 * @param configurationSourceName the name of the {@link RepositorySource} that is the configuration repository 128 * @param pathToConfigurationRoot the path of the node in the configuration source repository that should be treated by this 129 * service as the root of the service's configuration; if null, then "/dna:system" is used 130 * @param context the execution context in which this service should run 131 * @throws IllegalArgumentException if the bootstrap source is null or the execution context is null 132 */ 133 public RepositoryService( RepositoryLibrary sources, 134 String configurationSourceName, 135 Path pathToConfigurationRoot, 136 ExecutionContext context ) { 137 CheckArg.isNotNull(configurationSourceName, "configurationSourceName"); 138 CheckArg.isNotNull(sources, "sources"); 139 CheckArg.isNotNull(context, "context"); 140 if (pathToConfigurationRoot == null) pathToConfigurationRoot = context.getValueFactories().getPathFactory().create("/dna:system"); 141 this.sources = sources; 142 this.pathToConfigurationRoot = pathToConfigurationRoot; 143 this.configurationSourceName = configurationSourceName; 144 this.context = context; 145 } 146 147 /** 148 * {@inheritDoc} 149 */ 150 public ServiceAdministrator getAdministrator() { 151 return this.administrator; 152 } 153 154 /** 155 * @return configurationSourceName 156 */ 157 public String getConfigurationSourceName() { 158 return configurationSourceName; 159 } 160 161 /** 162 * @return sources 163 */ 164 public RepositoryLibrary getRepositorySourceManager() { 165 return sources; 166 } 167 168 /** 169 * @return env 170 */ 171 public ExecutionContext getExecutionEnvironment() { 172 return context; 173 } 174 175 public String getJndiName() { 176 // TODO 177 return null; 178 } 179 180 protected synchronized void startService() { 181 if (this.started.get() == false) { 182 Problems problems = new SimpleProblems(); 183 184 // ------------------------------------------------------------------------------------ 185 // Read the configuration ... 186 // ------------------------------------------------------------------------------------ 187 ValueFactories valueFactories = context.getValueFactories(); 188 PathFactory pathFactory = valueFactories.getPathFactory(); 189 NameFactory nameFactory = valueFactories.getNameFactory(); 190 191 // Create a command executor to execute the commands. 192 CommandExecutor executor = new SingleSourceCommandExecutor(context, configurationSourceName, sources); 193 194 // Read the configuration and the repository sources, located as child nodes/branches under "/dna:sources", 195 // and then instantiate and register each in the "sources" manager 196 Path configurationRoot = this.pathToConfigurationRoot; 197 try { 198 Path sourcesNode = pathFactory.create(configurationRoot, nameFactory.create("dna:sources")); 199 BasicGetChildrenCommand getSources = new BasicGetChildrenCommand(sourcesNode); 200 executor.execute(getSources); 201 if (getSources.hasNoError()) { 202 203 // Build the commands to get each of the children ... 204 List<Path.Segment> children = getSources.getChildren(); 205 if (!children.isEmpty()) { 206 BasicCompositeCommand commands = new BasicCompositeCommand(); 207 for (Path.Segment child : getSources.getChildren()) { 208 final Path pathToSource = pathFactory.create(sourcesNode, child); 209 commands.add(new BasicGetNodeCommand(pathToSource)); 210 } 211 executor.execute(commands); 212 213 // Iterate over each source node obtained ... 214 for (GraphCommand command : commands) { 215 BasicGetNodeCommand getSourceCommand = (BasicGetNodeCommand)command; 216 if (getSourceCommand.hasNoError()) { 217 RepositorySource source = createRepositorySource(getSourceCommand.getPath(), 218 getSourceCommand.getPropertiesByName(), 219 problems); 220 if (source != null) sources.addSource(source); 221 } 222 } 223 } 224 } 225 } catch (Throwable err) { 226 throw new FederationException(RepositoryI18n.errorStartingRepositoryService.text()); 227 } finally { 228 executor.close(); 229 } 230 this.started.set(true); 231 } 232 } 233 234 /** 235 * Instantiate the {@link RepositorySource} described by the supplied properties. 236 * 237 * @param path the path to the node where these properties were found; never null 238 * @param properties the properties; never null 239 * @param problems the problems container in which any problems should be reported; never null 240 * @return the repository source instance, or null if it could not be created 241 */ 242 @SuppressWarnings( "null" ) 243 protected RepositorySource createRepositorySource( Path path, 244 Map<Name, Property> properties, 245 Problems problems ) { 246 ValueFactories valueFactories = context.getValueFactories(); 247 ValueFactory<String> stringFactory = valueFactories.getStringFactory(); 248 249 // Get the classname and classpath ... 250 Property classnameProperty = properties.get(DnaLexicon.CLASSNAME); 251 Property classpathProperty = properties.get(DnaLexicon.CLASSPATH); 252 if (classnameProperty == null) { 253 problems.addError(RepositoryI18n.requiredPropertyIsMissingFromNode, DnaLexicon.CLASSNAME, path); 254 } 255 // If the classpath property is null or empty, the default classpath will be used 256 if (problems.hasErrors()) return null; 257 258 // Create the instance ... 259 String classname = stringFactory.create(classnameProperty.getValues().next()); 260 String[] classpath = classpathProperty == null ? new String[] {} : stringFactory.create(classpathProperty.getValuesAsArray()); 261 ClassLoader classLoader = context.getClassLoader(classpath); 262 RepositorySource source = null; 263 try { 264 Class<?> sourceClass = classLoader.loadClass(classname); 265 source = (RepositorySource)sourceClass.newInstance(); 266 } catch (ClassNotFoundException err) { 267 problems.addError(err, RepositoryI18n.unableToLoadClassUsingClasspath, classname, classpath); 268 } catch (IllegalAccessException err) { 269 problems.addError(err, RepositoryI18n.unableToAccessClassUsingClasspath, classname, classpath); 270 } catch (Throwable err) { 271 problems.addError(err, RepositoryI18n.unableToInstantiateClassUsingClasspath, classname, classpath); 272 } 273 274 // Try to set the name property to the local name of the node... 275 Reflection reflection = new Reflection(source.getClass()); 276 try { 277 reflection.invokeSetterMethodOnTarget("name", source, path.getLastSegment().getName().getLocalName()); 278 } catch (SecurityException err) { 279 // Do nothing ... assume not a JavaBean property 280 } catch (NoSuchMethodException err) { 281 // Do nothing ... assume not a JavaBean property 282 } catch (IllegalArgumentException err) { 283 // Do nothing ... assume not a JavaBean property 284 } catch (IllegalAccessException err) { 285 // Do nothing ... assume not a JavaBean property 286 } catch (InvocationTargetException err) { 287 // Do nothing ... assume not a JavaBean property 288 } 289 290 // Now set all the properties that we can, ignoring any property that doesn't fit pattern ... 291 for (Map.Entry<Name, Property> entry : properties.entrySet()) { 292 Name propertyName = entry.getKey(); 293 Property property = entry.getValue(); 294 String javaPropertyName = propertyName.getLocalName(); 295 if (property.isEmpty()) continue; 296 Object value = null; 297 if (property.isSingle()) { 298 value = property.getValues().next(); 299 } else if (property.isMultiple()) { 300 value = property.getValuesAsArray(); 301 } 302 try { 303 reflection.invokeSetterMethodOnTarget(javaPropertyName, source, value); 304 } catch (SecurityException err) { 305 // Do nothing ... assume not a JavaBean property 306 } catch (NoSuchMethodException err) { 307 // Do nothing ... assume not a JavaBean property 308 } catch (IllegalArgumentException err) { 309 // Do nothing ... assume not a JavaBean property 310 } catch (IllegalAccessException err) { 311 // Do nothing ... assume not a JavaBean property 312 } catch (InvocationTargetException err) { 313 // Do nothing ... assume not a JavaBean property 314 } 315 } 316 return source; 317 } 318 319 /** 320 * {@inheritDoc} 321 */ 322 @Override 323 public boolean equals( Object obj ) { 324 if (obj == this) return true; 325 return false; 326 } 327 }