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.util.ArrayList;
025 import java.util.List;
026 import net.jcip.annotations.Immutable;
027 import org.jboss.dna.common.component.ClassLoaderFactory;
028 import org.jboss.dna.common.i18n.I18n;
029 import org.jboss.dna.common.text.Inflector;
030 import org.jboss.dna.common.util.CheckArg;
031 import org.jboss.dna.common.util.Reflection;
032 import org.jboss.dna.graph.ExecutionContext;
033 import org.jboss.dna.graph.Graph;
034 import org.jboss.dna.graph.connector.RepositorySource;
035 import org.jboss.dna.graph.connector.inmemory.InMemoryRepositorySource;
036 import org.jboss.dna.graph.mimetype.MimeTypeDetector;
037 import org.jboss.dna.graph.property.Name;
038 import org.jboss.dna.graph.property.Path;
039 import org.jboss.dna.graph.property.PathExpression;
040 import org.jboss.dna.graph.property.PathFactory;
041 import org.jboss.dna.graph.property.ValueFormatException;
042 import org.jboss.dna.graph.property.basic.RootPath;
043 import org.jboss.dna.repository.sequencer.Sequencer;
044
045 /**
046 * @param <BuilderType>
047 */
048 public abstract class Configurator<BuilderType> {
049
050 /**
051 * Interface used to configure a sequencer.
052 *
053 * @param <ReturnType> the type of interface to return after the sequencer's configuration is completed
054 */
055 public interface SequencerConfigurator<ReturnType> {
056
057 /**
058 * Add a new {@link Sequencer sequencer} to this configuration. The new sequencer will have the supplied name, and if the
059 * name of an existing sequencer is used, this will replace the existing sequencer configuration.
060 *
061 * @param id the identifier of the new sequencer
062 * @return the interface for choosing the class, which returns the interface used to configure the sequencer; never null
063 * @throws IllegalArgumentException if the sequencer name is null, empty, or otherwise invalid
064 */
065 public ChooseClass<Sequencer, SequencerDetails<ReturnType>> addSequencer( final String id );
066 }
067
068 /**
069 * Interface used to initialize the configurator to use a specific repository containing configuration information.
070 *
071 * @param <ReturnType> the configurator type returned after the configuration repository is defined
072 */
073 public interface Initializer<ReturnType> {
074 /**
075 * Specify that this configuration should use a particular {@link RepositorySource} for its configuration repository. By
076 * default each configuration uses an internal transient repository for its configuration, but using this method will make
077 * the configuration use a different repository (that is perhaps shared with other processes).
078 *
079 * @return the interface for choosing the class, which returns the interface used to configure the repository source that
080 * will be used for the configuration repository; never null
081 */
082 public ChooseClass<RepositorySource, ConfigRepositoryDetails<ReturnType>> withConfigurationRepository();
083 }
084
085 /**
086 * Interface used to configure a repository source.
087 *
088 * @param <ReturnType> the type of interface to return after the repository source's configuration is completed
089 */
090 public interface RepositoryConfigurator<ReturnType> {
091 /**
092 * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name,
093 * and if the name of an existing repository is used, this will replace the existing repository configuration.
094 *
095 * @param id the id of the new repository that is to be added
096 * @return the interface for choosing the class, which returns the interface used to configure the repository source;
097 * never null
098 * @throws IllegalArgumentException if the repository name is null, empty, or otherwise invalid
099 * @see #addRepository(RepositorySource)
100 */
101 public ChooseClass<RepositorySource, RepositoryDetails<ReturnType>> addRepository( final String id );
102
103 /**
104 * Add a new {@link RepositorySource repository} for this configuration. The new repository will have the supplied name,
105 * and if the name of an existing repository is used, this will replace the existing repository configuration.
106 *
107 * @param source the {@link RepositorySource} instance that should be used
108 * @return this configuration object, for method-chaining purposes
109 * @throws IllegalArgumentException if the repository source reference is null
110 * @see #addRepository(String)
111 */
112 public ReturnType addRepository( RepositorySource source );
113 }
114
115 /**
116 * Interface used to configure a MIME type detector.
117 *
118 * @param <ReturnType> the type of interface to return after the detector's configuration is completed
119 */
120 public interface MimeDetectorConfigurator<ReturnType> {
121 /**
122 * Add a new {@link MimeTypeDetector MIME type detector} to this configuration. The new detector will have the supplied
123 * name, and if the name of an existing detector is used, this will replace the existing detector configuration.
124 *
125 * @param id the id of the new detector
126 * @return the interface for choosing the class, which returns the interface used to configure the detector; never null
127 * @throws IllegalArgumentException if the detector name is null, empty, or otherwise invalid
128 */
129 public ChooseClass<MimeTypeDetector, MimeTypeDetectorDetails<ReturnType>> addMimeTypeDetector( final String id );
130 }
131
132 /**
133 * Interface used to build the configured component.
134 *
135 * @param <ReturnType> the type of component that this configuration builds
136 */
137 public interface Builder<ReturnType> {
138 /**
139 * Complete this configuration and create the corresponding engine.
140 *
141 * @return the new engine configured by this instance
142 * @throws DnaConfigurationException if the engine cannot be created from this configuration.
143 */
144 public ReturnType build() throws DnaConfigurationException;
145 }
146
147 /**
148 * Interface used to configure a {@link RepositorySource repository}.
149 *
150 * @param <ReturnType>
151 */
152 public interface RepositoryDetails<ReturnType>
153 extends SetName<RepositoryDetails<ReturnType>>, SetDescription<RepositoryDetails<ReturnType>>,
154 SetProperties<RepositoryDetails<ReturnType>>, And<ReturnType> {
155 }
156
157 /**
158 * Interface used to define the configuration repository.
159 *
160 * @param <ReturnType>
161 */
162 public interface ConfigRepositoryDetails<ReturnType>
163 extends SetDescription<ConfigRepositoryDetails<ReturnType>>, SetProperties<ConfigRepositoryDetails<ReturnType>>,
164 And<ReturnType> {
165 /**
166 * Specify the path under which the configuration content is to be found. This path is assumed to be "/" by default.
167 *
168 * @param path the path to the configuration content in the configuration source; may not be null
169 * @return this instance for method chaining purposes; never null
170 */
171 public ConfigRepositoryDetails<ReturnType> under( String path );
172 }
173
174 /**
175 * Interface used to configure a {@link Sequencer sequencer}.
176 *
177 * @param <ReturnType>
178 */
179 public interface SequencerDetails<ReturnType>
180 extends SetName<SequencerDetails<ReturnType>>, SetDescription<SequencerDetails<ReturnType>>, And<ReturnType> {
181
182 /**
183 * Specify the input {@link PathExpression path expression} represented as a string, which determines when this sequencer
184 * will be executed.
185 *
186 * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
187 * sequencer
188 * @return the interface used to specify the output path expression; never null
189 */
190 PathExpressionOutput<ReturnType> sequencingFrom( String inputPathExpression );
191
192 /**
193 * Specify the input {@link PathExpression path expression}, which determines when this sequencer will be executed.
194 *
195 * @param inputPathExpression the path expression for nodes that, when they change, will be passed as an input to the
196 * sequencer
197 * @return the interface used to continue specifying the configuration of the sequencer
198 */
199 SequencerDetails<ReturnType> sequencingFrom( PathExpression inputPathExpression );
200 }
201
202 /**
203 * Interface used to specify the output path expression for a
204 * {@link Configurator.SequencerDetails#sequencingFrom(PathExpression) sequencer configuration}.
205 *
206 * @param <ReturnType>
207 */
208 public interface PathExpressionOutput<ReturnType> {
209 /**
210 * Specify the output {@link PathExpression path expression}, which determines where this sequencer's output will be
211 * placed.
212 *
213 * @param outputExpression the path expression for the location(s) where output generated by the sequencer is to be placed
214 * @return the interface used to continue specifying the configuration of the sequencer
215 */
216 SequencerDetails<ReturnType> andOutputtingTo( String outputExpression );
217 }
218
219 /**
220 * Interface used to configure a {@link MimeTypeDetector MIME type detector}.
221 *
222 * @param <ReturnType>
223 */
224 public interface MimeTypeDetectorDetails<ReturnType>
225 extends SetName<MimeTypeDetectorDetails<ReturnType>>, SetDescription<MimeTypeDetectorDetails<ReturnType>>,
226 SetProperties<MimeTypeDetectorDetails<ReturnType>>, And<ReturnType> {
227 }
228
229 /**
230 * Interface for configuring the JavaBean-style properties of an object.
231 *
232 * @param <ReturnType> the interface returned after the property has been set.
233 * @author Randall Hauch
234 */
235 public interface SetProperties<ReturnType> {
236 /**
237 * Specify the name of the JavaBean-style property that is to be set. The value may be set using the interface returned by
238 * this method.
239 *
240 * @param beanPropertyName the name of the JavaBean-style property (e.g., "retryLimit")
241 * @return the interface used to set the value for the property; never null
242 */
243 PropertySetter<ReturnType> with( String beanPropertyName );
244 }
245
246 /**
247 * The interface used to set the value for a JavaBean-style property.
248 *
249 * @param <ReturnType> the interface returned from these methods
250 * @author Randall Hauch
251 * @see Configurator.SetProperties#with(String)
252 */
253 public interface PropertySetter<ReturnType> {
254 /**
255 * Set the property value to an integer.
256 *
257 * @param value the new value for the property
258 * @return the next component to continue configuration; never null
259 */
260 ReturnType setTo( int value );
261
262 /**
263 * Set the property value to a long number.
264 *
265 * @param value the new value for the property
266 * @return the next component to continue configuration; never null
267 */
268 ReturnType setTo( long value );
269
270 /**
271 * Set the property value to a short.
272 *
273 * @param value the new value for the property
274 * @return the next component to continue configuration; never null
275 */
276 ReturnType setTo( short value );
277
278 /**
279 * Set the property value to a boolean.
280 *
281 * @param value the new value for the property
282 * @return the next component to continue configuration; never null
283 */
284 ReturnType setTo( boolean value );
285
286 /**
287 * Set the property value to a float.
288 *
289 * @param value the new value for the property
290 * @return the next component to continue configuration; never null
291 */
292 ReturnType setTo( float value );
293
294 /**
295 * Set the property value to a double.
296 *
297 * @param value the new value for the property
298 * @return the next component to continue configuration; never null
299 */
300 ReturnType setTo( double value );
301
302 /**
303 * Set the property value to a string.
304 *
305 * @param value the new value for the property
306 * @return the next component to continue configuration; never null
307 */
308 ReturnType setTo( String value );
309
310 /**
311 * Set the property value to an object.
312 *
313 * @param value the new value for the property
314 * @return the next component to continue configuration; never null
315 */
316 ReturnType setTo( Object value );
317 }
318
319 /**
320 * The interface used to configure the class used for a component.
321 *
322 * @param <ComponentClassType> the class or interface that the component is to implement
323 * @param <ReturnType> the interface returned from these methods
324 */
325 public interface ChooseClass<ComponentClassType, ReturnType> {
326
327 /**
328 * Specify the name of the class that should be instantiated for the instance. The classpath information will need to be
329 * defined using the returned interface.
330 *
331 * @param classname the name of the class that should be instantiated
332 * @return the interface used to define the classpath information; never null
333 * @throws IllegalArgumentException if the class name is null, empty, blank, or not a valid class name
334 */
335 LoadedFrom<ReturnType> usingClass( String classname );
336
337 /**
338 * Specify the class that should be instantiated for the instance. Because the class is already available to this class
339 * loader, there is no need to specify the classloader information.
340 *
341 * @param clazz the class that should be instantiated
342 * @return the next component to continue configuration; never null
343 * @throws DnaConfigurationException if the class could not be accessed and instantiated (if needed)
344 * @throws IllegalArgumentException if the class reference is null
345 */
346 ReturnType usingClass( Class<? extends ComponentClassType> clazz );
347 }
348
349 /**
350 * The interface used to set a description on a component.
351 *
352 * @param <ReturnType> the interface returned from these methods
353 */
354 public interface SetDescription<ReturnType> {
355 /**
356 * Specify the description of this component.
357 *
358 * @param description the description; may be null or empty
359 * @return the next component to continue configuration; never null
360 */
361 ReturnType describedAs( String description );
362 }
363
364 /**
365 * The interface used to set a human readable name on a component.
366 *
367 * @param <ReturnType> the interface returned from these methods
368 */
369 public interface SetName<ReturnType> {
370 /**
371 * Specify the human-readable name for this component.
372 *
373 * @param description the description; may be null or empty
374 * @return the next component to continue configuration; never null
375 */
376 ReturnType named( String description );
377 }
378
379 /**
380 * Interface for specifying from where the component's class is to be loaded.
381 *
382 * @param <ReturnType> the interface returned from these methods
383 */
384 public interface LoadedFrom<ReturnType> {
385 /**
386 * Specify the names of the classloaders that form the classpath for the component, from which the component's class (and
387 * its dependencies) can be loaded. The names correspond to the names supplied to the
388 * {@link ExecutionContext#getClassLoader(String...)} methods.
389 *
390 * @param classPathNames the names for the classloaders, as passed to the {@link ClassLoaderFactory} implementation (e.g.,
391 * the {@link ExecutionContext}).
392 * @return the next component to continue configuration; never null
393 * @see #loadedFromClasspath()
394 * @see ExecutionContext#getClassLoader(String...)
395 */
396 ReturnType loadedFrom( String... classPathNames );
397
398 /**
399 * Specify that the component (and its dependencies) will be found on the current (or
400 * {@link Thread#getContextClassLoader() current context}) classloader.
401 *
402 * @return the next component to continue configuration; never null
403 * @see #loadedFrom(String...)
404 * @see ExecutionContext#getClassLoader(String...)
405 */
406 ReturnType loadedFromClasspath();
407 }
408
409 /**
410 * Continue with another aspect of configuration.
411 *
412 * @param <ReturnType>
413 */
414 public interface And<ReturnType> {
415
416 /**
417 * Return a reference to the next configuration interface for additional operations.
418 *
419 * @return a reference to the next configuration interface
420 */
421 ReturnType and();
422 }
423
424 protected final BuilderType builder;
425 protected final ExecutionContext context;
426 protected ConfigurationRepository configurationSource;
427 private Graph graph;
428 private Graph.Batch batch;
429
430 /**
431 * Specify a new {@link ExecutionContext} that should be used for this DNA instance.
432 *
433 * @param context the new context, or null if a default-constructed execution context should be used
434 * @param builder the builder
435 * @throws IllegalArgumentException if the supplied context reference is null
436 */
437 protected Configurator( ExecutionContext context,
438 BuilderType builder ) {
439 CheckArg.isNotNull(context, "context");
440 CheckArg.isNotNull(builder, "builder");
441 this.context = context;
442 this.builder = builder;
443
444 // Set up the default configuration repository ...
445 this.configurationSource = createDefaultConfigurationSource();
446 }
447
448 /**
449 * Method that is used to set up the default configuration repository source. By default, this method sets up the
450 * {@link InMemoryRepositorySource} loaded from the classpath.
451 *
452 * @return the default repository source
453 */
454 protected ConfigurationRepository createDefaultConfigurationSource() {
455 InMemoryRepositorySource defaultSource = new InMemoryRepositorySource();
456 defaultSource.setName("Configuration");
457 ConfigurationRepository result = new ConfigurationRepository(defaultSource, "Configuration Repository", null);
458 return result;
459 }
460
461 /**
462 * Get the execution context used by this configurator.
463 *
464 * @return the execution context; never null
465 */
466 public final ExecutionContext getExecutionContext() {
467 return this.context;
468 }
469
470 protected final PathFactory pathFactory() {
471 return getExecutionContext().getValueFactories().getPathFactory();
472 }
473
474 /**
475 * Get the graph containing the configuration information.
476 *
477 * @return the configuration repository graph; never null
478 * @see #graph()
479 */
480 protected final Graph graph() {
481 if (this.graph == null) {
482 this.graph = Graph.create(configurationSource.getRepositorySource(), context);
483 }
484 return this.graph;
485 }
486
487 /**
488 * Get the graph batch that can be used to change the configuration, where the changes are enqueued until {@link #save()
489 * saved}.
490 *
491 * @return the latest batch for changes to the configuration repository; never null
492 * @see #graph()
493 */
494 protected final Graph.Batch configuration() {
495 if (this.batch == null) {
496 this.batch = graph().batch();
497 }
498 return this.batch;
499 }
500
501 /**
502 * Save any changes that have been made so far to the configuration. This method does nothing if no changes have been made.
503 *
504 * @return this configuration object for method chaining purposes; never null
505 */
506 public BuilderType save() {
507 if (this.batch != null) {
508 this.batch.execute();
509 this.batch = this.graph.batch();
510 }
511 return this.builder;
512 }
513
514 protected abstract Name nameFor( String name );
515
516 protected Path createOrReplaceNode( Path parentPath,
517 String id ) {
518 Path path = pathFactory().create(parentPath, id);
519 configuration().create(path).with(DnaLexicon.READABLE_NAME, id).and();
520 return path;
521 }
522
523 protected void recordBeanPropertiesInGraph( Path path,
524 Object javaBean ) {
525 Reflection reflector = new Reflection(javaBean.getClass());
526 for (String propertyName : reflector.findGetterPropertyNames()) {
527 Object value;
528 try {
529 value = reflector.invokeGetterMethodOnTarget(propertyName, javaBean);
530 if (value == null) continue;
531 propertyName = Inflector.getInstance().lowerCamelCase(propertyName);
532 configuration().set(nameFor(propertyName)).to(value).on(path);
533 } catch (ValueFormatException err) {
534 throw err;
535 } catch (Throwable err) {
536 // Unable to call getter and set property
537 }
538 }
539 }
540
541 protected class ConfigurationRepositoryClassChooser<ReturnType>
542 implements ChooseClass<RepositorySource, ConfigRepositoryDetails<ReturnType>> {
543
544 private final ReturnType returnObject;
545
546 protected ConfigurationRepositoryClassChooser( ReturnType returnObject ) {
547 assert returnObject != null;
548 this.returnObject = returnObject;
549 }
550
551 public LoadedFrom<ConfigRepositoryDetails<ReturnType>> usingClass( final String className ) {
552 return new LoadedFrom<ConfigRepositoryDetails<ReturnType>>() {
553 @SuppressWarnings( "unchecked" )
554 public ConfigRepositoryDetails loadedFrom( String... classpath ) {
555 ClassLoader classLoader = getExecutionContext().getClassLoader(classpath);
556 Class<? extends RepositorySource> clazz = null;
557 try {
558 clazz = (Class<? extends RepositorySource>)classLoader.loadClass(className);
559 } catch (ClassNotFoundException err) {
560 throw new DnaConfigurationException(RepositoryI18n.unableToLoadClassUsingClasspath.text(className,
561 classpath));
562 }
563 return usingClass(clazz);
564 }
565
566 @SuppressWarnings( "unchecked" )
567 public ConfigRepositoryDetails loadedFromClasspath() {
568 Class<? extends RepositorySource> clazz = null;
569 try {
570 clazz = (Class<? extends RepositorySource>)Class.forName(className);
571 } catch (ClassNotFoundException err) {
572 throw new DnaConfigurationException(RepositoryI18n.unableToLoadClass.text(className));
573 }
574 return usingClass(clazz);
575 }
576 };
577 }
578
579 public ConfigRepositoryDetails<ReturnType> usingClass( Class<? extends RepositorySource> repositorySource ) {
580 try {
581 Configurator.this.configurationSource = new ConfigurationRepository(repositorySource.newInstance());
582 } catch (InstantiationException err) {
583 I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
584 throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
585 } catch (IllegalAccessException err) {
586 I18n msg = RepositoryI18n.errorCreatingInstanceOfClass;
587 throw new DnaConfigurationException(msg.text(repositorySource.getName(), err.getLocalizedMessage()), err);
588 }
589 return new ConfigurationSourceDetails<ReturnType>(returnObject);
590 }
591 }
592
593 protected class ConfigurationSourceDetails<ReturnType> implements ConfigRepositoryDetails<ReturnType> {
594 private final ReturnType returnObject;
595
596 protected ConfigurationSourceDetails( ReturnType returnObject ) {
597 assert returnObject != null;
598 this.returnObject = returnObject;
599 }
600
601 /**
602 * {@inheritDoc}
603 *
604 * @see org.jboss.dna.repository.Configurator.SetDescription#describedAs(java.lang.String)
605 */
606 public ConfigRepositoryDetails<ReturnType> describedAs( String description ) {
607 Configurator.this.configurationSource = Configurator.this.configurationSource.with(description);
608 return this;
609 }
610
611 /**
612 * {@inheritDoc}
613 *
614 * @see org.jboss.dna.repository.Configurator.SetProperties#with(java.lang.String)
615 */
616 public PropertySetter<ConfigRepositoryDetails<ReturnType>> with( String propertyName ) {
617 return new BeanPropertySetter<ConfigRepositoryDetails<ReturnType>>(
618 Configurator.this.configurationSource.getRepositorySource(),
619 propertyName, this);
620 }
621
622 /**
623 * {@inheritDoc}
624 *
625 * @see org.jboss.dna.repository.Configurator.ConfigRepositoryDetails#under(java.lang.String)
626 */
627 public ConfigRepositoryDetails<ReturnType> under( String path ) {
628 CheckArg.isNotNull(path, "path");
629 Path newPath = getExecutionContext().getValueFactories().getPathFactory().create(path);
630 Configurator.this.configurationSource = Configurator.this.configurationSource.with(newPath);
631 return null;
632 }
633
634 /**
635 * {@inheritDoc}
636 *
637 * @see org.jboss.dna.repository.Configurator.And#and()
638 */
639 public ReturnType and() {
640 return returnObject;
641 }
642 }
643
644 /**
645 * Reusable implementation of {@link Configurator.ChooseClass} that can be used to obtain the name of a class and how its
646 * class loader is defined.
647 *
648 * @param <ComponentClass> the type of the component that is being chosen
649 * @param <ReturnType> the interface that should be returned when the class name and classpath have been chosen.
650 */
651 protected class ClassChooser<ComponentClass, ReturnType> implements Configurator.ChooseClass<ComponentClass, ReturnType> {
652 protected final Path pathOfComponentNode;
653 protected final ReturnType returnObject;
654
655 protected ClassChooser( Path pathOfComponentNode,
656 ReturnType returnObject ) {
657 assert pathOfComponentNode != null;
658 assert returnObject != null;
659 this.pathOfComponentNode = pathOfComponentNode;
660 this.returnObject = returnObject;
661 }
662
663 /**
664 * {@inheritDoc}
665 *
666 * @see Configurator.ChooseClass#usingClass(java.lang.String)
667 */
668 public Configurator.LoadedFrom<ReturnType> usingClass( final String classname ) {
669 CheckArg.isNotEmpty(classname, "classname");
670 configuration().set(DnaLexicon.CLASSNAME).to(classname).on(pathOfComponentNode);
671 return new Configurator.LoadedFrom<ReturnType>() {
672 public ReturnType loadedFromClasspath() {
673 return returnObject;
674 }
675
676 public ReturnType loadedFrom( String... classpath ) {
677 CheckArg.isNotEmpty(classpath, "classpath");
678 if (classpath.length == 1 && classpath[0] != null) {
679 configuration().set(DnaLexicon.CLASSPATH).to(classpath[0]).on(pathOfComponentNode);
680 } else {
681 Object[] remaining = new String[classpath.length - 1];
682 System.arraycopy(classpath, 1, remaining, 0, remaining.length);
683 configuration().set(DnaLexicon.CLASSPATH).to(classpath[0], remaining).on(pathOfComponentNode);
684 }
685 return returnObject;
686 }
687 };
688 }
689
690 /**
691 * {@inheritDoc}
692 *
693 * @see Configurator.ChooseClass#usingClass(java.lang.Class)
694 */
695 public ReturnType usingClass( Class<? extends ComponentClass> clazz ) {
696 CheckArg.isNotNull(clazz, "clazz");
697 return usingClass(clazz.getName()).loadedFromClasspath();
698 }
699 }
700
701 /**
702 * Reusable implementation of {@link Configurator.PropertySetter} that sets the JavaBean-style property using reflection.
703 *
704 * @param <ReturnType>
705 */
706 protected class BeanPropertySetter<ReturnType> implements Configurator.PropertySetter<ReturnType> {
707 private final Object javaBean;
708 private final String beanPropertyName;
709 private final ReturnType returnObject;
710
711 protected BeanPropertySetter( Object javaBean,
712 String beanPropertyName,
713 ReturnType returnObject ) {
714 assert javaBean != null;
715 assert beanPropertyName != null;
716 assert returnObject != null;
717 this.javaBean = javaBean;
718 this.beanPropertyName = beanPropertyName;
719 this.returnObject = returnObject;
720 }
721
722 public ReturnType setTo( boolean value ) {
723 return setTo((Object)value);
724 }
725
726 public ReturnType setTo( int value ) {
727 return setTo((Object)value);
728 }
729
730 public ReturnType setTo( long value ) {
731 return setTo((Object)value);
732 }
733
734 public ReturnType setTo( short value ) {
735 return setTo((Object)value);
736 }
737
738 public ReturnType setTo( float value ) {
739 return setTo((Object)value);
740 }
741
742 public ReturnType setTo( double value ) {
743 return setTo((Object)value);
744 }
745
746 public ReturnType setTo( String value ) {
747 return setTo((Object)value);
748 }
749
750 public ReturnType setTo( Object value ) {
751 // Set the JavaBean-style property on the RepositorySource instance ...
752 Reflection reflection = new Reflection(javaBean.getClass());
753 try {
754 reflection.invokeSetterMethodOnTarget(beanPropertyName, javaBean, value);
755 } catch (Throwable err) {
756 I18n msg = RepositoryI18n.errorSettingJavaBeanPropertyOnInstanceOfClass;
757 throw new DnaConfigurationException(msg.text(beanPropertyName, javaBean.getClass(), err.getMessage()), err);
758 }
759 return returnObject;
760 }
761 }
762
763 /**
764 * Reusable implementation of {@link Configurator.PropertySetter} that sets the property on the specified node in the
765 * configuration graph.
766 *
767 * @param <ReturnType>
768 */
769 protected class GraphPropertySetter<ReturnType> implements Configurator.PropertySetter<ReturnType> {
770 private final Path path;
771 private final String beanPropertyName;
772 private final ReturnType returnObject;
773
774 protected GraphPropertySetter( Path path,
775 String beanPropertyName,
776 ReturnType returnObject ) {
777 assert path != null;
778 assert beanPropertyName != null;
779 assert returnObject != null;
780 this.path = path;
781 this.beanPropertyName = Inflector.getInstance().lowerCamelCase(beanPropertyName);
782 this.returnObject = returnObject;
783 }
784
785 public ReturnType setTo( boolean value ) {
786 configuration().set(nameFor(beanPropertyName)).to(value).on(path);
787 return returnObject;
788 }
789
790 public ReturnType setTo( int value ) {
791 configuration().set(nameFor(beanPropertyName)).to(value).on(path);
792 return returnObject;
793 }
794
795 public ReturnType setTo( long value ) {
796 configuration().set(nameFor(beanPropertyName)).to(value).on(path);
797 return returnObject;
798 }
799
800 public ReturnType setTo( short value ) {
801 configuration().set(nameFor(beanPropertyName)).to(value).on(path);
802 return returnObject;
803 }
804
805 public ReturnType setTo( float value ) {
806 configuration().set(nameFor(beanPropertyName)).to(value).on(path);
807 return returnObject;
808 }
809
810 public ReturnType setTo( double value ) {
811 configuration().set(nameFor(beanPropertyName)).to(value).on(path);
812 return returnObject;
813 }
814
815 public ReturnType setTo( String value ) {
816 configuration().set(nameFor(beanPropertyName)).to(value).on(path);
817 return returnObject;
818 }
819
820 public ReturnType setTo( Object value ) {
821 configuration().set(nameFor(beanPropertyName)).to(value).on(path);
822 return returnObject;
823 }
824 }
825
826 protected class GraphRepositoryDetails<ReturnType> implements RepositoryDetails<ReturnType> {
827 private final Path path;
828 private final ReturnType returnObject;
829
830 protected GraphRepositoryDetails( Path path,
831 ReturnType returnObject ) {
832 assert path != null;
833 assert returnObject != null;
834 this.path = path;
835 this.returnObject = returnObject;
836 }
837
838 /**
839 * {@inheritDoc}
840 *
841 * @see org.jboss.dna.repository.Configurator.SetName#named(java.lang.String)
842 */
843 public RepositoryDetails<ReturnType> named( String name ) {
844 configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
845 return this;
846 }
847
848 /**
849 * {@inheritDoc}
850 *
851 * @see org.jboss.dna.repository.Configurator.SetDescription#describedAs(java.lang.String)
852 */
853 public RepositoryDetails<ReturnType> describedAs( String description ) {
854 configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
855 return this;
856 }
857
858 /**
859 * {@inheritDoc}
860 *
861 * @see org.jboss.dna.repository.Configurator.SetProperties#with(java.lang.String)
862 */
863 public PropertySetter<RepositoryDetails<ReturnType>> with( String propertyName ) {
864 return new GraphPropertySetter<RepositoryDetails<ReturnType>>(path, propertyName, this);
865 }
866
867 /**
868 * {@inheritDoc}
869 *
870 * @see org.jboss.dna.repository.Configurator.And#and()
871 */
872 public ReturnType and() {
873 return returnObject;
874 }
875 }
876
877 protected class GraphSequencerDetails<ReturnType> implements SequencerDetails<ReturnType> {
878 private final Path path;
879 private final List<String> compiledExpressions = new ArrayList<String>();
880 private final ReturnType returnObject;
881
882 protected GraphSequencerDetails( Path path,
883 ReturnType returnObject ) {
884 assert path != null;
885 assert returnObject != null;
886 this.path = path;
887 this.returnObject = returnObject;
888 }
889
890 /**
891 * {@inheritDoc}
892 *
893 * @see org.jboss.dna.repository.Configurator.SequencerDetails#sequencingFrom(java.lang.String)
894 */
895 public PathExpressionOutput<ReturnType> sequencingFrom( final String from ) {
896 CheckArg.isNotEmpty(from, "from");
897 return new PathExpressionOutput<ReturnType>() {
898 /**
899 * {@inheritDoc}
900 *
901 * @see org.jboss.dna.repository.Configurator.PathExpressionOutput#andOutputtingTo(java.lang.String)
902 */
903 public SequencerDetails<ReturnType> andOutputtingTo( String into ) {
904 CheckArg.isNotEmpty(into, "into");
905 return sequencingFrom(PathExpression.compile(from + " => " + into));
906 }
907 };
908 }
909
910 /**
911 * {@inheritDoc}
912 *
913 * @see org.jboss.dna.repository.Configurator.SetName#named(java.lang.String)
914 */
915 public SequencerDetails<ReturnType> named( String name ) {
916 configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
917 return this;
918 }
919
920 /**
921 * {@inheritDoc}
922 *
923 * @see org.jboss.dna.repository.Configurator.SequencerDetails#sequencingFrom(org.jboss.dna.graph.property.PathExpression)
924 */
925 public SequencerDetails<ReturnType> sequencingFrom( PathExpression expression ) {
926 CheckArg.isNotNull(expression, "expression");
927 String compiledExpression = expression.getExpression();
928 if (!compiledExpressions.contains(compiledExpression)) compiledExpressions.add(compiledExpression);
929 configuration().set(DnaLexicon.PATH_EXPRESSIONS).on(path).to(compiledExpressions);
930 return this;
931 }
932
933 /**
934 * {@inheritDoc}
935 *
936 * @see org.jboss.dna.repository.Configurator.SetDescription#describedAs(java.lang.String)
937 */
938 public SequencerDetails<ReturnType> describedAs( String description ) {
939 configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
940 return this;
941 }
942
943 /**
944 * {@inheritDoc}
945 *
946 * @see org.jboss.dna.repository.Configurator.And#and()
947 */
948 public ReturnType and() {
949 return returnObject;
950 }
951 }
952
953 protected class GraphMimeTypeDetectorDetails<ReturnType> implements MimeTypeDetectorDetails<ReturnType> {
954 private final Path path;
955 private final ReturnType returnObject;
956
957 protected GraphMimeTypeDetectorDetails( Path path,
958 ReturnType returnObject ) {
959 assert path != null;
960 assert returnObject != null;
961 this.path = path;
962 this.returnObject = returnObject;
963 }
964
965 /**
966 * {@inheritDoc}
967 *
968 * @see org.jboss.dna.repository.Configurator.SetName#named(java.lang.String)
969 */
970 public MimeTypeDetectorDetails<ReturnType> named( String name ) {
971 configuration().set(DnaLexicon.READABLE_NAME).to(name).on(path);
972 return this;
973 }
974
975 /**
976 * {@inheritDoc}
977 *
978 * @see org.jboss.dna.repository.Configurator.SetProperties#with(java.lang.String)
979 */
980 public PropertySetter<MimeTypeDetectorDetails<ReturnType>> with( String propertyName ) {
981 return new GraphPropertySetter<MimeTypeDetectorDetails<ReturnType>>(path, propertyName, this);
982 }
983
984 /**
985 * {@inheritDoc}
986 *
987 * @see org.jboss.dna.repository.Configurator.SetDescription#describedAs(java.lang.String)
988 */
989 public MimeTypeDetectorDetails<ReturnType> describedAs( String description ) {
990 configuration().set(DnaLexicon.DESCRIPTION).to(description).on(path);
991 return this;
992 }
993
994 /**
995 * {@inheritDoc}
996 *
997 * @see org.jboss.dna.repository.Configurator.And#and()
998 */
999 public ReturnType and() {
1000 return returnObject;
1001 }
1002 }
1003
1004 @Immutable
1005 public static class ConfigurationRepository {
1006 private final RepositorySource source;
1007 private final String description;
1008 private final Path path;
1009
1010 protected ConfigurationRepository( RepositorySource source ) {
1011 this(source, null, null);
1012 }
1013
1014 protected ConfigurationRepository( RepositorySource source,
1015 String description,
1016 Path path ) {
1017 this.source = source;
1018 this.description = description != null ? description : "";
1019 this.path = path != null ? path : RootPath.INSTANCE;
1020 }
1021
1022 /**
1023 * @return source
1024 */
1025 public RepositorySource getRepositorySource() {
1026 return source;
1027 }
1028
1029 /**
1030 * @return description
1031 */
1032 public String getDescription() {
1033 return description;
1034 }
1035
1036 /**
1037 * @return path
1038 */
1039 public Path getPath() {
1040 return path;
1041 }
1042
1043 public ConfigurationRepository with( String description ) {
1044 return new ConfigurationRepository(source, description, path);
1045 }
1046
1047 public ConfigurationRepository with( Path path ) {
1048 return new ConfigurationRepository(source, description, path);
1049 }
1050 }
1051 }