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 }