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 }