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