1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24 package org.modeshape.repository;
25
26 import java.lang.reflect.InvocationTargetException;
27 import java.lang.reflect.Method;
28 import java.util.Map;
29 import java.util.concurrent.TimeUnit;
30 import java.util.concurrent.atomic.AtomicBoolean;
31 import net.jcip.annotations.ThreadSafe;
32 import org.modeshape.common.collection.Problems;
33 import org.modeshape.common.collection.SimpleProblems;
34 import org.modeshape.common.util.CheckArg;
35 import org.modeshape.common.util.Logger;
36 import org.modeshape.common.util.Reflection;
37 import org.modeshape.graph.ExecutionContext;
38 import org.modeshape.graph.Graph;
39 import org.modeshape.graph.JcrLexicon;
40 import org.modeshape.graph.Location;
41 import org.modeshape.graph.Node;
42 import org.modeshape.graph.Subgraph;
43 import org.modeshape.graph.connector.RepositorySource;
44 import org.modeshape.graph.observe.Changes;
45 import org.modeshape.graph.observe.NetChangeObserver;
46 import org.modeshape.graph.observe.ObservationBus;
47 import org.modeshape.graph.observe.Observer;
48 import org.modeshape.graph.property.Name;
49 import org.modeshape.graph.property.Path;
50 import org.modeshape.graph.property.PathFactory;
51 import org.modeshape.graph.property.PathNotFoundException;
52 import org.modeshape.graph.property.Property;
53 import org.modeshape.graph.property.PropertyType;
54 import org.modeshape.graph.property.ValueFactories;
55 import org.modeshape.graph.property.ValueFactory;
56 import org.modeshape.graph.request.ReadBranchRequest;
57 import org.modeshape.repository.service.AbstractServiceAdministrator;
58 import org.modeshape.repository.service.AdministeredService;
59 import org.modeshape.repository.service.ServiceAdministrator;
60
61
62
63
64 @ThreadSafe
65 public class RepositoryService implements AdministeredService, Observer {
66
67
68
69
70
71
72 protected class Administrator extends AbstractServiceAdministrator {
73
74 protected Administrator() {
75 super(RepositoryI18n.repositoryServiceName, State.PAUSED);
76 }
77
78
79
80
81 @Override
82 protected boolean doCheckIsTerminated() {
83 return true;
84 }
85
86
87
88
89 @Override
90 protected void doStart( State fromState ) {
91 super.doStart(fromState);
92 startService();
93 }
94
95
96
97
98
99
100 @Override
101 protected void doShutdown( State fromState ) {
102 super.doShutdown(fromState);
103 shutdownService();
104 }
105
106
107
108
109
110
111 public boolean awaitTermination( long timeout,
112 TimeUnit unit ) {
113 return true;
114 }
115 }
116
117 private final ExecutionContext context;
118 private final RepositoryLibrary sources;
119 private final String configurationSourceName;
120 private final String configurationWorkspaceName;
121 private final Path pathToConfigurationRoot;
122 private final ConfigurationChangeObserver configurationChangeObserver;
123 private final Administrator administrator = new Administrator();
124 private final AtomicBoolean started = new AtomicBoolean(false);
125
126 private final Problems problems;
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142 public RepositoryService( RepositorySource configurationSource,
143 String configurationWorkspaceName,
144 Path pathToConfigurationRoot,
145 ExecutionContext context,
146 ObservationBus observationBus,
147 Problems problems ) {
148 CheckArg.isNotNull(configurationSource, "configurationSource");
149 CheckArg.isNotNull(context, "context");
150 CheckArg.isNotNull(observationBus, "observationBus");
151 PathFactory pathFactory = context.getValueFactories().getPathFactory();
152 if (pathToConfigurationRoot == null) pathToConfigurationRoot = pathFactory.create("/dna:system");
153 if (problems == null) problems = new SimpleProblems();
154 Path sourcesPath = pathFactory.create(pathToConfigurationRoot, ModeShapeLexicon.SOURCES);
155
156 this.sources = new RepositoryLibrary(configurationSource, configurationWorkspaceName, sourcesPath, context,
157 observationBus);
158 this.sources.addSource(configurationSource);
159 this.pathToConfigurationRoot = pathToConfigurationRoot;
160 this.configurationSourceName = configurationSource.getName();
161 this.configurationWorkspaceName = configurationWorkspaceName;
162 this.context = context;
163 this.problems = problems;
164 this.configurationChangeObserver = new ConfigurationChangeObserver();
165 }
166
167
168
169
170 public final ServiceAdministrator getAdministrator() {
171 return this.administrator;
172 }
173
174
175
176
177 public final String getConfigurationSourceName() {
178 return configurationSourceName;
179 }
180
181
182
183
184 public final String getConfigurationWorkspaceName() {
185 return configurationWorkspaceName;
186 }
187
188
189
190
191
192
193 public final RepositoryLibrary getRepositoryLibrary() {
194 return sources;
195 }
196
197
198
199
200 protected final Path getPathToConfigurationRoot() {
201 return pathToConfigurationRoot;
202 }
203
204
205
206
207 public final ExecutionContext getExecutionEnvironment() {
208 return context;
209 }
210
211 public String getJndiName() {
212
213 return null;
214 }
215
216 protected synchronized void startService() {
217 if (this.started.get() == false) {
218
219
220
221
222
223 Graph graph = Graph.create(getConfigurationSourceName(), sources, context);
224 Path pathToSourcesNode = context.getValueFactories().getPathFactory().create(pathToConfigurationRoot,
225 ModeShapeLexicon.SOURCES);
226 try {
227 String workspaceName = getConfigurationWorkspaceName();
228 if (workspaceName != null) graph.useWorkspace(workspaceName);
229
230 Subgraph sourcesGraph = graph.getSubgraphOfDepth(ReadBranchRequest.NO_MAXIMUM_DEPTH).at(pathToSourcesNode);
231
232
233 for (Location location : sourcesGraph.getRoot().getChildren()) {
234 sources.addSource(createRepositorySource(sourcesGraph, location, problems));
235 }
236 } catch (PathNotFoundException e) {
237
238 } catch (Throwable err) {
239 throw new ModeShapeConfigurationException(RepositoryI18n.errorStartingRepositoryService.text(), err);
240 }
241
242 this.started.set(true);
243 }
244 }
245
246 protected synchronized void shutdownService() {
247
248 this.sources.getAdministrator().shutdown();
249 }
250
251
252
253
254
255
256
257
258
259 protected RepositorySource createRepositorySource( Subgraph subgraph,
260 Location location,
261 Problems problems ) {
262 return (RepositorySource)createInstanceFromProperties(subgraph, location, problems, true);
263 }
264
265
266
267
268
269
270
271
272
273
274
275 protected Object createInstanceFromProperties( Subgraph subgraph,
276 Location location,
277 Problems problems,
278 boolean mustHaveClassName ) {
279 ValueFactories valueFactories = context.getValueFactories();
280 ValueFactory<String> stringFactory = valueFactories.getStringFactory();
281
282 Node node = subgraph.getNode(location);
283 assert location.hasPath();
284 Path path = node.getLocation().getPath();
285 Map<Name, Property> properties = node.getPropertiesByName();
286
287
288 Property classnameProperty = properties.get(ModeShapeLexicon.CLASSNAME);
289 Property classpathProperty = properties.get(ModeShapeLexicon.CLASSPATH);
290 if (classnameProperty == null) {
291 if (mustHaveClassName) {
292 problems.addError(RepositoryI18n.requiredPropertyIsMissingFromNode, ModeShapeLexicon.CLASSNAME, path);
293 }
294 return null;
295 }
296
297 if (problems.hasErrors()) return null;
298
299
300 String classname = stringFactory.create(classnameProperty.getValues().next());
301 String[] classpath = classpathProperty == null ? new String[] {} : stringFactory.create(classpathProperty.getValuesAsArray());
302 ClassLoader classLoader = context.getClassLoader(classpath);
303 Object instance = null;
304 try {
305 Class<?> sourceClass = classLoader.loadClass(classname);
306 instance = sourceClass.newInstance();
307 } catch (ClassNotFoundException err) {
308 problems.addError(err, RepositoryI18n.unableToLoadClassUsingClasspath, classname, classpath);
309 } catch (IllegalAccessException err) {
310 problems.addError(err, RepositoryI18n.unableToAccessClassUsingClasspath, classname, classpath);
311 } catch (Throwable err) {
312 problems.addError(err, RepositoryI18n.unableToInstantiateClassUsingClasspath, classname, classpath);
313 }
314 if (instance == null) return null;
315
316
317 Property nameProperty = context.getPropertyFactory().create(JcrLexicon.NAME,
318 path.getLastSegment().getName().getLocalName());
319 properties.put(JcrLexicon.NAME, nameProperty);
320
321
322
323 setBeanPropertyIfExistsAndNotSet(instance, "configurationSourceName", getConfigurationSourceName());
324 setBeanPropertyIfExistsAndNotSet(instance, "configurationWorkspaceName", getConfigurationWorkspaceName());
325 setBeanPropertyIfExistsAndNotSet(instance, "configurationPath", stringFactory.create(path));
326
327
328 Reflection reflection = new Reflection(instance.getClass());
329 for (Map.Entry<Name, Property> entry : properties.entrySet()) {
330 Name propertyName = entry.getKey();
331 Property property = entry.getValue();
332 String javaPropertyName = propertyName.getLocalName();
333 if (property.isEmpty()) continue;
334
335 Object value = null;
336 Method setter = null;
337 try {
338 setter = reflection.findFirstMethod("set" + javaPropertyName, false);
339 if (setter == null) continue;
340
341 Class<?>[] parameterTypes = setter.getParameterTypes();
342 if (parameterTypes.length != 1) continue;
343 Class<?> paramType = parameterTypes[0];
344 PropertyType allowedType = PropertyType.discoverType(paramType);
345 if (allowedType == null) continue;
346 ValueFactory<?> factory = context.getValueFactories().getValueFactory(allowedType);
347 if (paramType.isArray()) {
348 if (paramType.getComponentType().isArray()) continue;
349 Object[] values = factory.create(property.getValuesAsArray());
350
351 Class<?> componentType = paramType.getComponentType();
352 if (Integer.TYPE.equals(componentType)) {
353 int[] primitiveValues = new int[values.length];
354 for (int i = 0; i != values.length; ++i) {
355 primitiveValues[i] = ((Long)values[i]).intValue();
356 }
357 value = primitiveValues;
358 } else if (Short.TYPE.equals(componentType)) {
359 short[] primitiveValues = new short[values.length];
360 for (int i = 0; i != values.length; ++i) {
361 primitiveValues[i] = ((Long)values[i]).shortValue();
362 }
363 value = primitiveValues;
364 } else if (Long.TYPE.equals(componentType)) {
365 long[] primitiveValues = new long[values.length];
366 for (int i = 0; i != values.length; ++i) {
367 primitiveValues[i] = ((Long)values[i]).longValue();
368 }
369 value = primitiveValues;
370 } else if (Double.TYPE.equals(componentType)) {
371 double[] primitiveValues = new double[values.length];
372 for (int i = 0; i != values.length; ++i) {
373 primitiveValues[i] = ((Double)values[i]).doubleValue();
374 }
375 value = primitiveValues;
376 } else if (Float.TYPE.equals(componentType)) {
377 float[] primitiveValues = new float[values.length];
378 for (int i = 0; i != values.length; ++i) {
379 primitiveValues[i] = ((Double)values[i]).floatValue();
380 }
381 value = primitiveValues;
382 } else if (Boolean.TYPE.equals(componentType)) {
383 boolean[] primitiveValues = new boolean[values.length];
384 for (int i = 0; i != values.length; ++i) {
385 primitiveValues[i] = ((Boolean)values[i]).booleanValue();
386 }
387 value = primitiveValues;
388 } else {
389 value = values;
390 }
391 } else {
392 value = factory.create(property.getFirstValue());
393
394 if (Integer.TYPE.equals(paramType)) {
395 value = new Integer(((Long)value).intValue());
396 } else if (Short.TYPE.equals(paramType)) {
397 value = new Short(((Long)value).shortValue());
398 } else if (Float.TYPE.equals(paramType)) {
399 value = new Float(((Double)value).floatValue());
400 }
401 }
402
403 String msg = "Setting property {0} to {1} on source at {2} in configuration repository {3} in workspace {4}";
404 Logger.getLogger(getClass()).trace(msg,
405 javaPropertyName,
406 value,
407 path,
408 configurationSourceName,
409 configurationWorkspaceName);
410 setter.invoke(instance, value);
411 } catch (SecurityException err) {
412 Logger.getLogger(getClass()).debug(err, "Error invoking {0}.{1}", instance.getClass(), setter);
413 } catch (IllegalArgumentException err) {
414
415 String msg = "Invalid argument invoking {0} with parameter {1} on source at {2} in configuration repository {3} in workspace {4}";
416 Logger.getLogger(getClass()).debug(err,
417 msg,
418 setter,
419 value,
420 path,
421 configurationSourceName,
422 configurationWorkspaceName);
423 } catch (IllegalAccessException err) {
424 Logger.getLogger(getClass()).debug(err, "Error invoking {0}.{1}", instance.getClass(), setter);
425 } catch (InvocationTargetException err) {
426
427 String msg = "Error invoking {0} with parameter {1} on source at {2} in configuration repository {3} in workspace {4}";
428 Logger.getLogger(getClass()).debug(err.getTargetException(),
429 msg,
430 setter,
431 value,
432 path,
433 configurationSourceName,
434 configurationWorkspaceName);
435 }
436 }
437
438
439 for (Location childLocation : node.getChildren()) {
440 assert childLocation.hasPath();
441 Path childPath = childLocation.getPath();
442 Name childName = childPath.getLastSegment().getName();
443
444 Object value = createInstanceFromProperties(subgraph, childLocation, problems, false);
445 if (problems.hasErrors()) {
446 return null;
447 }
448
449 String javaPropertyName = childName.getLocalName();
450 Method setter = reflection.findFirstMethod("set" + javaPropertyName, false);
451 if (setter == null) continue;
452
453 try {
454 setter.invoke(instance, value);
455
456 String msg = "Setting property {0} to {1} on object at {2} in configuration repository {3} in workspace {4}";
457 Logger.getLogger(getClass()).trace(msg,
458 javaPropertyName,
459 value,
460 childPath,
461 configurationSourceName,
462 configurationWorkspaceName);
463 setter.invoke(instance, value);
464 } catch (SecurityException err) {
465 Logger.getLogger(getClass()).debug(err, "Error invoking {0}.{1}", instance.getClass(), setter);
466 } catch (IllegalArgumentException err) {
467
468 String msg = "Invalid argument invoking {0} with parameter {1} on object at {2} in configuration repository {3} in workspace {4}";
469 Logger.getLogger(getClass()).debug(err,
470 msg,
471 setter,
472 value,
473 childPath,
474 configurationSourceName,
475 configurationWorkspaceName);
476 } catch (IllegalAccessException err) {
477 Logger.getLogger(getClass()).debug(err, "Error invoking {0}.{1}", instance.getClass(), setter);
478 } catch (InvocationTargetException err) {
479
480 String msg = "Error invoking {0} with parameter {1} on source at {2} in configuration repository {3} in workspace {4}";
481 Logger.getLogger(getClass()).debug(err.getTargetException(),
482 msg,
483 setter,
484 value,
485 childPath,
486 configurationSourceName,
487 configurationWorkspaceName);
488 }
489
490 }
491
492 return instance;
493
494 }
495
496 protected boolean setBeanPropertyIfExistsAndNotSet( Object target,
497 String propertyName,
498 Object value ) {
499 Reflection reflection = new Reflection(target.getClass());
500 try {
501 if (reflection.invokeGetterMethodOnTarget(propertyName, target) == null) {
502 reflection.invokeSetterMethodOnTarget(propertyName, target, value);
503 return true;
504 }
505 return false;
506 } catch (Exception e) {
507
508 Logger.getLogger(getClass())
509 .debug("Unknown property '{0}' on '{1}' class", propertyName, target.getClass().getName());
510 return false;
511 }
512 }
513
514
515
516
517 @Override
518 public boolean equals( Object obj ) {
519 if (obj == this) return true;
520 return false;
521 }
522
523
524
525
526
527
528 public void notify( Changes changes ) {
529
530 this.configurationChangeObserver.notify(changes);
531 }
532
533 protected class ConfigurationChangeObserver extends NetChangeObserver {
534
535
536
537
538
539
540 @Override
541 protected void notify( NetChanges netChanges ) {
542 if (getConfigurationWorkspaceName() == null) {
543
544 return;
545 }
546 if (!getConfigurationSourceName().equals(netChanges.getSourceName())) return;
547 for (NetChange change : netChanges.getNetChanges()) {
548 if (!getConfigurationWorkspaceName().equals(change.getRepositoryWorkspaceName())) return;
549 Path changedPath = change.getPath();
550 Path configPath = getPathToConfigurationRoot();
551 if (!changedPath.isAtOrBelow(getPathToConfigurationRoot())) return;
552 boolean changedNodeIsPotentiallySource = configPath.size() + 1 == changedPath.size();
553
554
555 if (changedNodeIsPotentiallySource && change.includes(ChangeType.NODE_REMOVED)) {
556
557 String sourceName = changedPath.getLastSegment().getName().getLocalName();
558 getRepositoryLibrary().removeSource(sourceName);
559 } else {
560
561 Path sourcePath = changedNodeIsPotentiallySource ? changedPath : changedPath.subpath(0, configPath.size() + 1);
562 Problems problems = new SimpleProblems();
563
564 Graph graph = Graph.create(getConfigurationSourceName(), getRepositoryLibrary(), getExecutionEnvironment());
565 try {
566 String workspaceName = getConfigurationWorkspaceName();
567 if (workspaceName != null) graph.useWorkspace(workspaceName);
568 Subgraph subgraph = graph.getSubgraphOfDepth(ReadBranchRequest.NO_MAXIMUM_DEPTH).at(sourcePath);
569 RepositorySource source = createRepositorySource(subgraph, Location.create(sourcePath), problems);
570 if (source != null) {
571
572 getRepositoryLibrary().addSource(source, true);
573 }
574 } catch (PathNotFoundException e) {
575
576 String sourceName = changedPath.getLastSegment().getName().getLocalName();
577 getRepositoryLibrary().removeSource(sourceName);
578 }
579 }
580 }
581 }
582 }
583 }