001/* 002 * Copyright (C) 2005 Christian Schulte <cs@schulte.it> 003 * All rights reserved. 004 * 005 * Redistribution and use in source and binary forms, with or without 006 * modification, are permitted provided that the following conditions 007 * are met: 008 * 009 * o Redistributions of source code must retain the above copyright 010 * notice, this list of conditions and the following disclaimer. 011 * 012 * o Redistributions in binary form must reproduce the above copyright 013 * notice, this list of conditions and the following disclaimer in 014 * the documentation and/or other materials provided with the 015 * distribution. 016 * 017 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, 018 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY 019 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 020 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT, 021 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT 022 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 023 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 024 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 025 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF 026 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 027 * 028 * $JOMC: DefaultModelContext.java 5305 2016-08-30 21:46:23Z schulte $ 029 * 030 */ 031package org.jomc.modlet; 032 033import java.io.BufferedReader; 034import java.io.File; 035import java.io.FileInputStream; 036import java.io.IOException; 037import java.io.InputStream; 038import java.io.InputStreamReader; 039import java.io.Reader; 040import java.lang.reflect.UndeclaredThrowableException; 041import java.net.URI; 042import java.net.URISyntaxException; 043import java.net.URL; 044import java.text.MessageFormat; 045import java.util.ArrayList; 046import java.util.Collection; 047import java.util.Collections; 048import java.util.Comparator; 049import java.util.Enumeration; 050import java.util.HashSet; 051import java.util.LinkedList; 052import java.util.List; 053import java.util.Map; 054import java.util.ResourceBundle; 055import java.util.Set; 056import java.util.StringTokenizer; 057import java.util.TreeMap; 058import java.util.concurrent.Callable; 059import java.util.concurrent.CancellationException; 060import java.util.concurrent.CopyOnWriteArrayList; 061import java.util.concurrent.ExecutionException; 062import java.util.concurrent.Future; 063import java.util.jar.Attributes; 064import java.util.jar.Manifest; 065import java.util.logging.Level; 066import javax.xml.XMLConstants; 067import javax.xml.bind.JAXBContext; 068import javax.xml.bind.JAXBException; 069import javax.xml.bind.Marshaller; 070import javax.xml.bind.Unmarshaller; 071import javax.xml.transform.Source; 072import javax.xml.transform.sax.SAXSource; 073import javax.xml.validation.SchemaFactory; 074import javax.xml.validation.Validator; 075import org.w3c.dom.ls.LSInput; 076import org.w3c.dom.ls.LSResourceResolver; 077import org.xml.sax.EntityResolver; 078import org.xml.sax.ErrorHandler; 079import org.xml.sax.InputSource; 080import org.xml.sax.SAXException; 081import org.xml.sax.SAXParseException; 082import org.xml.sax.helpers.DefaultHandler; 083 084/** 085 * Default {@code ModelContext} implementation. 086 * 087 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 088 * @version $JOMC: DefaultModelContext.java 5305 2016-08-30 21:46:23Z schulte $ 089 * @see ModelContextFactory 090 */ 091public class DefaultModelContext extends ModelContext 092{ 093 094 /** 095 * Constant for the name of the model context attribute backing property {@code providerLocation}. 096 * 097 * @see #getProviderLocation() 098 * @see ModelContext#getAttribute(java.lang.String) 099 * @since 1.2 100 */ 101 public static final String PROVIDER_LOCATION_ATTRIBUTE_NAME = 102 "org.jomc.modlet.DefaultModelContext.providerLocationAttribute"; 103 104 /** 105 * Constant for the name of the model context attribute backing property {@code platformProviderLocation}. 106 * 107 * @see #getPlatformProviderLocation() 108 * @see ModelContext#getAttribute(java.lang.String) 109 * @since 1.2 110 */ 111 public static final String PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME = 112 "org.jomc.modlet.DefaultModelContext.platformProviderLocationAttribute"; 113 114 /** 115 * Supported schema name extensions. 116 */ 117 private static final String[] SCHEMA_EXTENSIONS = new String[] 118 { 119 "xsd" 120 }; 121 122 /** 123 * Class path location searched for providers by default. 124 * 125 * @see #getDefaultProviderLocation() 126 */ 127 private static final String DEFAULT_PROVIDER_LOCATION = "META-INF/services"; 128 129 /** 130 * Location searched for platform providers by default. 131 * 132 * @see #getDefaultPlatformProviderLocation() 133 */ 134 private static final String DEFAULT_PLATFORM_PROVIDER_LOCATION = 135 new StringBuilder( 255 ).append( System.getProperty( "java.home" ) ).append( File.separator ).append( "lib" ). 136 append( File.separator ).append( "jomc.properties" ).toString(); 137 138 /** 139 * Constant for the service identifier of marshaller listener services. 140 * 141 * @since 1.2 142 */ 143 private static final String MARSHALLER_LISTENER_SERVICE = "javax.xml.bind.Marshaller.Listener"; 144 145 /** 146 * Constant for the service identifier of unmarshaller listener services. 147 * 148 * @since 1.2 149 */ 150 private static final String UNMARSHALLER_LISTENER_SERVICE = "javax.xml.bind.Unmarshaller.Listener"; 151 152 /** 153 * Default provider location. 154 */ 155 private static volatile String defaultProviderLocation; 156 157 /** 158 * Default platform provider location. 159 */ 160 private static volatile String defaultPlatformProviderLocation; 161 162 /** 163 * Provider location of the instance. 164 */ 165 private volatile String providerLocation; 166 167 /** 168 * Platform provider location of the instance. 169 */ 170 private volatile String platformProviderLocation; 171 172 /** 173 * Creates a new {@code DefaultModelContext} instance. 174 * 175 * @since 1.2 176 */ 177 public DefaultModelContext() 178 { 179 super(); 180 } 181 182 /** 183 * Creates a new {@code DefaultModelContext} instance taking a class loader. 184 * 185 * @param classLoader The class loader of the context. 186 */ 187 public DefaultModelContext( final ClassLoader classLoader ) 188 { 189 super( classLoader ); 190 } 191 192 /** 193 * Gets the default location searched for provider resources. 194 * <p> 195 * The default provider location is controlled by system property 196 * {@code org.jomc.modlet.DefaultModelContext.defaultProviderLocation} holding the location to search 197 * for provider resources by default. If that property is not set, the {@code META-INF/services} default is 198 * returned. 199 * </p> 200 * 201 * @return The location searched for provider resources by default. 202 * 203 * @see #setDefaultProviderLocation(java.lang.String) 204 */ 205 public static String getDefaultProviderLocation() 206 { 207 if ( defaultProviderLocation == null ) 208 { 209 defaultProviderLocation = System.getProperty( 210 "org.jomc.modlet.DefaultModelContext.defaultProviderLocation", DEFAULT_PROVIDER_LOCATION ); 211 212 } 213 214 return defaultProviderLocation; 215 } 216 217 /** 218 * Sets the default location searched for provider resources. 219 * 220 * @param value The new default location to search for provider resources or {@code null}. 221 * 222 * @see #getDefaultProviderLocation() 223 */ 224 public static void setDefaultProviderLocation( final String value ) 225 { 226 defaultProviderLocation = value; 227 } 228 229 /** 230 * Gets the location searched for provider resources. 231 * 232 * @return The location searched for provider resources. 233 * 234 * @see #getDefaultProviderLocation() 235 * @see #setProviderLocation(java.lang.String) 236 * @see #PROVIDER_LOCATION_ATTRIBUTE_NAME 237 */ 238 public final String getProviderLocation() 239 { 240 if ( this.providerLocation == null ) 241 { 242 this.providerLocation = getDefaultProviderLocation(); 243 244 if ( DEFAULT_PROVIDER_LOCATION.equals( this.providerLocation ) 245 && this.getAttribute( PROVIDER_LOCATION_ATTRIBUTE_NAME ) instanceof String ) 246 { 247 final String contextProviderLocation = (String) this.getAttribute( PROVIDER_LOCATION_ATTRIBUTE_NAME ); 248 249 if ( this.isLoggable( Level.CONFIG ) ) 250 { 251 this.log( Level.CONFIG, getMessage( "contextProviderLocationInfo", 252 contextProviderLocation ), null ); 253 } 254 255 this.providerLocation = null; 256 return contextProviderLocation; 257 } 258 else if ( this.isLoggable( Level.CONFIG ) ) 259 { 260 this.log( Level.CONFIG, getMessage( "defaultProviderLocationInfo", this.providerLocation ), null ); 261 } 262 } 263 264 return this.providerLocation; 265 } 266 267 /** 268 * Sets the location searched for provider resources. 269 * 270 * @param value The new location to search for provider resources or {@code null}. 271 * 272 * @see #getProviderLocation() 273 */ 274 public final void setProviderLocation( final String value ) 275 { 276 this.providerLocation = value; 277 } 278 279 /** 280 * Gets the default location searched for platform provider resources. 281 * <p> 282 * The default platform provider location is controlled by system property 283 * {@code org.jomc.modlet.DefaultModelContext.defaultPlatformProviderLocation} holding the location to 284 * search for platform provider resources by default. If that property is not set, the 285 * {@code <java-home>/lib/jomc.properties} default is returned. 286 * </p> 287 * 288 * @return The location searched for platform provider resources by default. 289 * 290 * @see #setDefaultPlatformProviderLocation(java.lang.String) 291 */ 292 public static String getDefaultPlatformProviderLocation() 293 { 294 if ( defaultPlatformProviderLocation == null ) 295 { 296 defaultPlatformProviderLocation = System.getProperty( 297 "org.jomc.modlet.DefaultModelContext.defaultPlatformProviderLocation", 298 DEFAULT_PLATFORM_PROVIDER_LOCATION ); 299 300 } 301 302 return defaultPlatformProviderLocation; 303 } 304 305 /** 306 * Sets the default location searched for platform provider resources. 307 * 308 * @param value The new default location to search for platform provider resources or {@code null}. 309 * 310 * @see #getDefaultPlatformProviderLocation() 311 */ 312 public static void setDefaultPlatformProviderLocation( final String value ) 313 { 314 defaultPlatformProviderLocation = value; 315 } 316 317 /** 318 * Gets the location searched for platform provider resources. 319 * 320 * @return The location searched for platform provider resources. 321 * 322 * @see #getDefaultPlatformProviderLocation() 323 * @see #setPlatformProviderLocation(java.lang.String) 324 * @see #PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME 325 */ 326 public final String getPlatformProviderLocation() 327 { 328 if ( this.platformProviderLocation == null ) 329 { 330 this.platformProviderLocation = getDefaultPlatformProviderLocation(); 331 332 if ( DEFAULT_PLATFORM_PROVIDER_LOCATION.equals( this.platformProviderLocation ) 333 && this.getAttribute( PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME ) instanceof String ) 334 { 335 final String contextPlatformProviderLocation = 336 (String) this.getAttribute( PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME ); 337 338 if ( this.isLoggable( Level.CONFIG ) ) 339 { 340 this.log( Level.CONFIG, getMessage( "contextPlatformProviderLocationInfo", 341 contextPlatformProviderLocation ), null ); 342 343 } 344 345 this.platformProviderLocation = null; 346 return contextPlatformProviderLocation; 347 } 348 else if ( this.isLoggable( Level.CONFIG ) ) 349 { 350 this.log( Level.CONFIG, 351 getMessage( "defaultPlatformProviderLocationInfo", this.platformProviderLocation ), null ); 352 353 } 354 } 355 356 return this.platformProviderLocation; 357 } 358 359 /** 360 * Sets the location searched for platform provider resources. 361 * 362 * @param value The new location to search for platform provider resources or {@code null}. 363 * 364 * @see #getPlatformProviderLocation() 365 */ 366 public final void setPlatformProviderLocation( final String value ) 367 { 368 this.platformProviderLocation = value; 369 } 370 371 /** 372 * {@inheritDoc} 373 * <p> 374 * This method loads {@code ModletProvider} classes setup via the platform provider configuration file and 375 * {@code <provider-location>/org.jomc.modlet.ModletProvider} resources to return a list of {@code Modlets}. 376 * </p> 377 * 378 * @see #getProviderLocation() 379 * @see #getPlatformProviderLocation() 380 * @see ModletProvider#findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets) 381 * @deprecated As of JOMC 1.6, replaced by {@link #findModlets(org.jomc.modlet.Modlets)}. This method will be 382 * removed in JOMC 2.0. 383 */ 384 @Override 385 @Deprecated 386 public Modlets findModlets() throws ModelException 387 { 388 return this.findModlets( new Modlets() ); 389 } 390 391 /** 392 * {@inheritDoc} 393 * <p> 394 * This method loads {@code ModletProvider} classes setup via the platform provider configuration file and 395 * {@code <provider-location>/org.jomc.modlet.ModletProvider} resources to return a list of {@code Modlets}. 396 * </p> 397 * 398 * @see #getProviderLocation() 399 * @see #getPlatformProviderLocation() 400 * @see ModletProvider#findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets) 401 * @since 1.6 402 */ 403 @Override 404 public Modlets findModlets( final Modlets modlets ) throws ModelException 405 { 406 if ( modlets == null ) 407 { 408 throw new NullPointerException( "modlets" ); 409 } 410 411 Modlets found = modlets.clone(); 412 final Collection<ModletProvider> providers = this.loadModletServices( ModletProvider.class ); 413 414 for ( final ModletProvider provider : providers ) 415 { 416 if ( this.isLoggable( Level.FINER ) ) 417 { 418 this.log( Level.FINER, getMessage( "creatingModlets", provider.toString() ), null ); 419 } 420 421 final Modlets provided = provider.findModlets( this, found ); 422 423 if ( provided != null ) 424 { 425 found = provided; 426 } 427 } 428 429 if ( this.isLoggable( Level.FINEST ) ) 430 { 431 for ( final Modlet m : found.getModlet() ) 432 { 433 this.log( Level.FINEST, 434 getMessage( "modletInfo", m.getName(), m.getModel(), 435 m.getVendor() != null 436 ? m.getVendor() : getMessage( "noVendor" ), 437 m.getVersion() != null 438 ? m.getVersion() : getMessage( "noVersion" ) ), null ); 439 440 if ( m.getSchemas() != null ) 441 { 442 for ( final Schema s : m.getSchemas().getSchema() ) 443 { 444 this.log( Level.FINEST, 445 getMessage( "modletSchemaInfo", m.getName(), s.getPublicId(), s.getSystemId(), 446 s.getContextId() != null 447 ? s.getContextId() : getMessage( "noContext" ), 448 s.getClasspathId() != null 449 ? s.getClasspathId() : getMessage( "noClasspathId" ) ), null ); 450 451 } 452 } 453 454 if ( m.getServices() != null ) 455 { 456 for ( final Service s : m.getServices().getService() ) 457 { 458 this.log( Level.FINEST, getMessage( "modletServiceInfo", m.getName(), s.getOrdinal(), 459 s.getIdentifier(), s.getClazz() ), null ); 460 461 } 462 } 463 } 464 } 465 466 return found; 467 } 468 469 /** 470 * {@inheritDoc} 471 * <p> 472 * This method loads {@code ModletProcessor} classes setup via the platform provider configuration file and 473 * {@code <provider-location>/org.jomc.modlet.ModletProcessor} resources to process a list of {@code Modlets}. 474 * </p> 475 * 476 * @see #getProviderLocation() 477 * @see #getPlatformProviderLocation() 478 * @see ModletProcessor#processModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets) 479 * @since 1.6 480 */ 481 @Override 482 public Modlets processModlets( final Modlets modlets ) throws ModelException 483 { 484 if ( modlets == null ) 485 { 486 throw new NullPointerException( "modlets" ); 487 } 488 489 Modlets result = modlets.clone(); 490 final Collection<ModletProcessor> processors = this.loadModletServices( ModletProcessor.class ); 491 492 for ( final ModletProcessor processor : processors ) 493 { 494 if ( this.isLoggable( Level.FINER ) ) 495 { 496 this.log( Level.FINER, getMessage( "processingModlets", processor.toString() ), null ); 497 } 498 499 final Modlets processed = processor.processModlets( this, result ); 500 501 if ( processed != null ) 502 { 503 result = processed; 504 } 505 } 506 507 return result; 508 } 509 510 /** 511 * {@inheritDoc} 512 * <p> 513 * This method loads {@code ModletValidator} classes setup via the platform provider configuration file and 514 * {@code <provider-location>/org.jomc.modlet.ModletValidator} resources to validate a list of {@code Modlets}. 515 * </p> 516 * 517 * @see #getProviderLocation() 518 * @see #getPlatformProviderLocation() 519 * @see ModletValidator#validateModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets) 520 * @since 1.9 521 */ 522 @Override 523 public ModelValidationReport validateModlets( final Modlets modlets ) throws ModelException 524 { 525 if ( modlets == null ) 526 { 527 throw new NullPointerException( "modlets" ); 528 } 529 530 final ModelValidationReport report = new ModelValidationReport(); 531 532 for ( final ModletValidator modletValidator 533 : this.loadModletServices( ModletValidator.class ) ) 534 { 535 if ( this.isLoggable( Level.FINER ) ) 536 { 537 this.log( Level.FINER, getMessage( "validatingModlets", modletValidator.toString() ), null ); 538 } 539 540 final ModelValidationReport current = modletValidator.validateModlets( this, modlets ); 541 542 if ( current != null ) 543 { 544 report.getDetails().addAll( current.getDetails() ); 545 } 546 } 547 548 return report; 549 } 550 551 /** 552 * {@inheritDoc} 553 * <p> 554 * This method creates all {@code ModelProvider} service objects of the model identified by {@code model} to create 555 * a new {@code Model} instance. 556 * </p> 557 * 558 * @see #findModel(org.jomc.modlet.Model) 559 * @see #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class) createServiceObjects( model, ModelProvider.class.getName(), ModelProvider.class ) 560 * @see ModelProvider#findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 561 */ 562 @Override 563 public Model findModel( final String model ) throws ModelException 564 { 565 if ( model == null ) 566 { 567 throw new NullPointerException( "model" ); 568 } 569 570 final Model m = new Model(); 571 m.setIdentifier( model ); 572 573 return this.findModel( m ); 574 } 575 576 /** 577 * {@inheritDoc} 578 * <p> 579 * This method creates all {@code ModelProvider} service objects of the given model to populate the given model 580 * instance. 581 * </p> 582 * 583 * @see #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class) createServiceObjects( model, ModelProvider.class.getName(), ModelProvider.class ) 584 * @see ModelProvider#findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 585 * 586 * @since 1.2 587 */ 588 @Override 589 public Model findModel( final Model model ) throws ModelException 590 { 591 if ( model == null ) 592 { 593 throw new NullPointerException( "model" ); 594 } 595 596 Model m = model.clone(); 597 final long t0 = System.nanoTime(); 598 599 for ( final ModelProvider provider 600 : this.createServiceObjects( model.getIdentifier(), ModelProvider.class.getName(), 601 ModelProvider.class ) ) 602 { 603 if ( this.isLoggable( Level.FINER ) ) 604 { 605 this.log( Level.FINER, getMessage( "creatingModel", m.getIdentifier(), provider.toString() ), null ); 606 } 607 608 final Model provided = provider.findModel( this, m ); 609 610 if ( provided != null ) 611 { 612 m = provided; 613 } 614 } 615 616 if ( this.isLoggable( Level.FINE ) ) 617 { 618 this.log( Level.FINE, getMessage( "findModelReport", m.getIdentifier(), System.nanoTime() - t0 ), null ); 619 } 620 621 return m; 622 } 623 624 /** 625 * {@inheritDoc} 626 * <p> 627 * This method creates all {@code ModelProcessor} service objects of {@code model} to process the given 628 * {@code Model}. 629 * </p> 630 * 631 * @see #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class) createServiceObjects( model, ModelProcessor.class.getName(), ModelProcessor.class ) 632 * @see ModelProcessor#processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 633 */ 634 @Override 635 public Model processModel( final Model model ) throws ModelException 636 { 637 if ( model == null ) 638 { 639 throw new NullPointerException( "model" ); 640 } 641 642 Model processed = model; 643 final long t0 = System.nanoTime(); 644 645 for ( final ModelProcessor processor 646 : this.createServiceObjects( model.getIdentifier(), ModelProcessor.class.getName(), 647 ModelProcessor.class ) ) 648 { 649 if ( this.isLoggable( Level.FINER ) ) 650 { 651 this.log( Level.FINER, getMessage( "processingModel", model.getIdentifier(), 652 processor.toString() ), null ); 653 654 } 655 656 final Model current = processor.processModel( this, processed ); 657 658 if ( current != null ) 659 { 660 processed = current; 661 } 662 } 663 664 if ( this.isLoggable( Level.FINE ) ) 665 { 666 this.log( Level.FINE, getMessage( "processModelReport", model.getIdentifier(), System.nanoTime() - t0 ), 667 null ); 668 669 } 670 671 return processed; 672 } 673 674 /** 675 * {@inheritDoc} 676 * <p> 677 * This method creates all {@code ModelValidator} service objects of {@code model} to validate the given 678 * {@code Model}. 679 * </p> 680 * 681 * @see #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class) createServiceObjects( model, ModelValidator.class.getName(), ModelValidator.class ) 682 * @see ModelValidator#validateModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model) 683 */ 684 @Override 685 public ModelValidationReport validateModel( final Model model ) throws ModelException 686 { 687 if ( model == null ) 688 { 689 throw new NullPointerException( "model" ); 690 } 691 692 try 693 { 694 final long t0 = System.nanoTime(); 695 final ModelValidationReport resultReport = new ModelValidationReport(); 696 697 final Collection<? extends ModelValidator> modelValidators = 698 this.createServiceObjects( model.getIdentifier(), ModelValidator.class.getName(), 699 ModelValidator.class ); 700 701 if ( this.getExecutorService() != null && modelValidators.size() > 1 ) 702 { 703 final List<Callable<ModelValidationReport>> tasks = 704 new ArrayList<Callable<ModelValidationReport>>( modelValidators.size() ); 705 706 for ( final ModelValidator validator : modelValidators ) 707 { 708 tasks.add( new Callable<ModelValidationReport>() 709 { 710 711 public ModelValidationReport call() throws ModelException 712 { 713 if ( isLoggable( Level.FINER ) ) 714 { 715 log( Level.FINER, getMessage( "validatingModel", model.getIdentifier(), 716 validator.toString() ), null ); 717 718 } 719 720 return validator.validateModel( DefaultModelContext.this, model ); 721 } 722 723 } ); 724 725 } 726 727 for ( final Future<ModelValidationReport> task : this.getExecutorService().invokeAll( tasks ) ) 728 { 729 final ModelValidationReport currentReport = task.get(); 730 731 if ( currentReport != null ) 732 { 733 resultReport.getDetails().addAll( currentReport.getDetails() ); 734 } 735 } 736 } 737 else 738 { 739 for ( final ModelValidator modelValidator : modelValidators ) 740 { 741 final ModelValidationReport currentReport = modelValidator.validateModel( this, model ); 742 743 if ( currentReport != null ) 744 { 745 resultReport.getDetails().addAll( currentReport.getDetails() ); 746 } 747 } 748 } 749 750 if ( this.isLoggable( Level.FINE ) ) 751 { 752 this.log( Level.FINE, getMessage( "validateModelReport", model.getIdentifier(), 753 System.nanoTime() - t0 ), null ); 754 755 } 756 757 return resultReport; 758 } 759 catch ( final CancellationException e ) 760 { 761 throw new ModelException( getMessage( "failedValidatingModel", model.getIdentifier() ), e ); 762 } 763 catch ( final InterruptedException e ) 764 { 765 throw new ModelException( getMessage( "failedValidatingModel", model.getIdentifier() ), e ); 766 } 767 catch ( final ExecutionException e ) 768 { 769 if ( e.getCause() instanceof ModelException ) 770 { 771 throw (ModelException) e.getCause(); 772 } 773 else if ( e.getCause() instanceof RuntimeException ) 774 { 775 // The fork-join framework breaks the exception handling contract of Callable by re-throwing any 776 // exception caught using a runtime exception. 777 if ( e.getCause().getCause() instanceof ModelException ) 778 { 779 throw (ModelException) e.getCause().getCause(); 780 } 781 else if ( e.getCause().getCause() instanceof RuntimeException ) 782 { 783 throw (RuntimeException) e.getCause().getCause(); 784 } 785 else if ( e.getCause().getCause() instanceof Error ) 786 { 787 throw (Error) e.getCause().getCause(); 788 } 789 else if ( e.getCause().getCause() instanceof Exception ) 790 { 791 // Checked exception not declared to be thrown by the Callable's 'call' method. 792 throw new UndeclaredThrowableException( e.getCause().getCause() ); 793 } 794 else 795 { 796 throw (RuntimeException) e.getCause(); 797 } 798 } 799 else if ( e.getCause() instanceof Error ) 800 { 801 throw (Error) e.getCause(); 802 } 803 else 804 { 805 // Checked exception not declared to be thrown by the Callable's 'call' method. 806 throw new UndeclaredThrowableException( e.getCause() ); 807 } 808 } 809 } 810 811 /** 812 * {@inheritDoc} 813 * 814 * @see #createSchema(java.lang.String) 815 */ 816 @Override 817 public ModelValidationReport validateModel( final String model, final Source source ) throws ModelException 818 { 819 if ( model == null ) 820 { 821 throw new NullPointerException( "model" ); 822 } 823 if ( source == null ) 824 { 825 throw new NullPointerException( "source" ); 826 } 827 828 final long t0 = System.nanoTime(); 829 final javax.xml.validation.Schema schema = this.createSchema( model ); 830 final Validator validator = schema.newValidator(); 831 final ModelErrorHandler modelErrorHandler = new ModelErrorHandler( this ); 832 validator.setErrorHandler( modelErrorHandler ); 833 834 try 835 { 836 validator.validate( source ); 837 } 838 catch ( final SAXException e ) 839 { 840 String message = getMessage( e ); 841 if ( message == null && e.getException() != null ) 842 { 843 message = getMessage( e.getException() ); 844 } 845 846 if ( this.isLoggable( Level.FINE ) ) 847 { 848 this.log( Level.FINE, message, e ); 849 } 850 851 if ( modelErrorHandler.getReport().isModelValid() ) 852 { 853 throw new ModelException( message, e ); 854 } 855 } 856 catch ( final IOException e ) 857 { 858 throw new ModelException( getMessage( e ), e ); 859 } 860 861 if ( this.isLoggable( Level.FINE ) ) 862 { 863 this.log( Level.FINE, getMessage( "validateModelReport", model, System.nanoTime() - t0 ), null ); 864 } 865 866 return modelErrorHandler.getReport(); 867 } 868 869 @Override 870 public EntityResolver createEntityResolver( final String model ) throws ModelException 871 { 872 if ( model == null ) 873 { 874 throw new NullPointerException( "model" ); 875 } 876 877 return this.createEntityResolver( this.getModlets().getSchemas( model ) ); 878 } 879 880 @Override 881 @Deprecated 882 public EntityResolver createEntityResolver( final URI publicId ) throws ModelException 883 { 884 if ( publicId == null ) 885 { 886 throw new NullPointerException( "publicId" ); 887 } 888 889 return this.createEntityResolver( this.getModlets().getSchemas( publicId ) ); 890 } 891 892 @Override 893 public LSResourceResolver createResourceResolver( final String model ) throws ModelException 894 { 895 if ( model == null ) 896 { 897 throw new NullPointerException( "model" ); 898 } 899 900 return this.createResourceResolver( this.createEntityResolver( model ) ); 901 } 902 903 @Override 904 @Deprecated 905 public LSResourceResolver createResourceResolver( final URI publicId ) throws ModelException 906 { 907 if ( publicId == null ) 908 { 909 throw new NullPointerException( "publicId" ); 910 } 911 912 return this.createResourceResolver( this.createEntityResolver( publicId ) ); 913 } 914 915 @Override 916 public javax.xml.validation.Schema createSchema( final String model ) throws ModelException 917 { 918 if ( model == null ) 919 { 920 throw new NullPointerException( "model" ); 921 } 922 923 return this.createSchema( this.getModlets().getSchemas( model ), this.createEntityResolver( model ), 924 this.createResourceResolver( model ), model, null ); 925 926 } 927 928 @Override 929 @Deprecated 930 public javax.xml.validation.Schema createSchema( final URI publicId ) throws ModelException 931 { 932 if ( publicId == null ) 933 { 934 throw new NullPointerException( "publicId" ); 935 } 936 937 return this.createSchema( this.getModlets().getSchemas( publicId ), this.createEntityResolver( publicId ), 938 this.createResourceResolver( publicId ), null, publicId ); 939 940 } 941 942 @Override 943 public JAXBContext createContext( final String model ) throws ModelException 944 { 945 if ( model == null ) 946 { 947 throw new NullPointerException( "model" ); 948 } 949 950 return this.createContext( this.getModlets().getSchemas( model ), model, null ); 951 } 952 953 @Override 954 @Deprecated 955 public JAXBContext createContext( final URI publicId ) throws ModelException 956 { 957 if ( publicId == null ) 958 { 959 throw new NullPointerException( "publicId" ); 960 } 961 962 return this.createContext( this.getModlets().getSchemas( publicId ), null, publicId ); 963 } 964 965 @Override 966 public Marshaller createMarshaller( final String model ) throws ModelException 967 { 968 if ( model == null ) 969 { 970 throw new NullPointerException( "model" ); 971 } 972 973 return this.createMarshaller( model, null ); 974 } 975 976 @Override 977 @Deprecated 978 public Marshaller createMarshaller( final URI publicId ) throws ModelException 979 { 980 if ( publicId == null ) 981 { 982 throw new NullPointerException( "publicId" ); 983 } 984 985 return this.createMarshaller( null, publicId ); 986 } 987 988 @Override 989 public Unmarshaller createUnmarshaller( final String model ) throws ModelException 990 { 991 if ( model == null ) 992 { 993 throw new NullPointerException( "model" ); 994 } 995 996 return this.createUnmarshaller( model, null ); 997 } 998 999 @Override 1000 @Deprecated 1001 public Unmarshaller createUnmarshaller( final URI publicId ) throws ModelException 1002 { 1003 if ( publicId == null ) 1004 { 1005 throw new NullPointerException( "publicId" ); 1006 } 1007 1008 return this.createUnmarshaller( null, publicId ); 1009 } 1010 1011 /** 1012 * {@inheritDoc} 1013 * <p> 1014 * This method loads {@code ServiceFactory} classes setup via the platform provider configuration file and 1015 * {@code <provider-location>/org.jomc.modlet.ServiceFactory} resources to create a new service object. 1016 * </p> 1017 * 1018 * @see #getProviderLocation() 1019 * @see #getPlatformProviderLocation() 1020 * @see ServiceFactory#createServiceObject(org.jomc.modlet.ModelContext, org.jomc.modlet.Service, java.lang.Class) 1021 * @since 1.2 1022 * @deprecated As of JOMC 1.9, please use method {@link #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class)}. 1023 * This method will be removed in JOMC 2.0. 1024 */ 1025 @Override 1026 @Deprecated 1027 public <T> T createServiceObject( final Service service, final Class<T> type ) throws ModelException 1028 { 1029 if ( service == null ) 1030 { 1031 throw new NullPointerException( "service" ); 1032 } 1033 if ( type == null ) 1034 { 1035 throw new NullPointerException( "type" ); 1036 } 1037 1038 return this.createServiceObject( service, type, this.loadModletServices( ServiceFactory.class ) ); 1039 } 1040 1041 private EntityResolver createEntityResolver( final Schemas schemas ) 1042 { 1043 return new DefaultHandler() 1044 { 1045 1046 @Override 1047 public InputSource resolveEntity( final String publicId, final String systemId ) 1048 throws SAXException, IOException 1049 { 1050 InputSource schemaSource = null; 1051 1052 try 1053 { 1054 Schema s = null; 1055 1056 if ( schemas != null ) 1057 { 1058 if ( systemId != null && !"".equals( systemId ) ) 1059 { 1060 s = schemas.getSchemaBySystemId( systemId ); 1061 } 1062 else if ( publicId != null ) 1063 { 1064 s = schemas.getSchemaByPublicId( publicId ); 1065 } 1066 } 1067 1068 if ( s != null ) 1069 { 1070 schemaSource = new InputSource(); 1071 schemaSource.setPublicId( s.getPublicId() ); 1072 schemaSource.setSystemId( s.getSystemId() ); 1073 1074 if ( s.getClasspathId() != null ) 1075 { 1076 final URL resource = findResource( s.getClasspathId() ); 1077 1078 if ( resource != null ) 1079 { 1080 schemaSource.setSystemId( resource.toExternalForm() ); 1081 } 1082 else if ( isLoggable( Level.WARNING ) ) 1083 { 1084 log( Level.WARNING, getMessage( "resourceNotFound", s.getClasspathId() ), null ); 1085 } 1086 } 1087 1088 if ( isLoggable( Level.FINEST ) ) 1089 { 1090 log( Level.FINEST, getMessage( "resolutionInfo", publicId + ", " + systemId, 1091 schemaSource.getPublicId() + ", " 1092 + schemaSource.getSystemId() ), null ); 1093 1094 } 1095 } 1096 1097 if ( schemaSource == null && systemId != null && !"".equals( systemId ) ) 1098 { 1099 final URI systemUri = new URI( systemId ); 1100 String schemaName = systemUri.getPath(); 1101 1102 if ( schemaName != null ) 1103 { 1104 final int lastIndexOfSlash = schemaName.lastIndexOf( '/' ); 1105 if ( lastIndexOfSlash != -1 && lastIndexOfSlash < schemaName.length() ) 1106 { 1107 schemaName = schemaName.substring( lastIndexOfSlash + 1 ); 1108 } 1109 1110 for ( final URI uri : getSchemaResources() ) 1111 { 1112 if ( uri.getSchemeSpecificPart() != null 1113 && uri.getSchemeSpecificPart().endsWith( schemaName ) ) 1114 { 1115 schemaSource = new InputSource(); 1116 schemaSource.setPublicId( publicId ); 1117 schemaSource.setSystemId( uri.toASCIIString() ); 1118 1119 if ( isLoggable( Level.FINEST ) ) 1120 { 1121 log( Level.FINEST, getMessage( "resolutionInfo", systemUri.toASCIIString(), 1122 schemaSource.getSystemId() ), null ); 1123 1124 } 1125 1126 break; 1127 } 1128 } 1129 } 1130 else 1131 { 1132 if ( isLoggable( Level.WARNING ) ) 1133 { 1134 log( Level.WARNING, getMessage( "unsupportedIdUri", systemId, 1135 systemUri.toASCIIString() ), null ); 1136 1137 } 1138 1139 schemaSource = null; 1140 } 1141 } 1142 } 1143 catch ( final URISyntaxException e ) 1144 { 1145 if ( isLoggable( Level.WARNING ) ) 1146 { 1147 log( Level.WARNING, getMessage( "unsupportedIdUri", systemId, getMessage( e ) ), null ); 1148 } 1149 1150 schemaSource = null; 1151 } 1152 catch ( final ModelException e ) 1153 { 1154 String message = getMessage( e ); 1155 if ( message == null ) 1156 { 1157 message = ""; 1158 } 1159 else if ( message.length() > 0 ) 1160 { 1161 message = " " + message; 1162 } 1163 1164 String resource = ""; 1165 if ( publicId != null ) 1166 { 1167 resource = publicId + ", "; 1168 } 1169 resource += systemId; 1170 1171 // JDK: As of JDK 6, "new IOException( message, cause )". 1172 throw (IOException) new IOException( getMessage( 1173 "failedResolving", resource, message ) ).initCause( e ); 1174 1175 } 1176 1177 return schemaSource; 1178 } 1179 1180 }; 1181 } 1182 1183 private LSResourceResolver createResourceResolver( final EntityResolver entityResolver ) 1184 { 1185 if ( entityResolver == null ) 1186 { 1187 throw new NullPointerException( "entityResolver" ); 1188 } 1189 1190 return new LSResourceResolver() 1191 { 1192 1193 public LSInput resolveResource( final String type, final String namespaceURI, final String publicId, 1194 final String systemId, final String baseURI ) 1195 { 1196 final String resolvePublicId = namespaceURI == null ? publicId : namespaceURI; 1197 final String resolveSystemId = systemId == null ? "" : systemId; 1198 1199 try 1200 { 1201 if ( XMLConstants.W3C_XML_SCHEMA_NS_URI.equals( type ) ) 1202 { 1203 final InputSource schemaSource = 1204 entityResolver.resolveEntity( resolvePublicId, resolveSystemId ); 1205 1206 if ( schemaSource != null ) 1207 { 1208 return new LSInput() 1209 { 1210 1211 public Reader getCharacterStream() 1212 { 1213 return schemaSource.getCharacterStream(); 1214 } 1215 1216 public void setCharacterStream( final Reader characterStream ) 1217 { 1218 if ( isLoggable( Level.WARNING ) ) 1219 { 1220 log( Level.WARNING, getMessage( 1221 "unsupportedOperation", "setCharacterStream", 1222 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1223 1224 } 1225 } 1226 1227 public InputStream getByteStream() 1228 { 1229 return schemaSource.getByteStream(); 1230 } 1231 1232 public void setByteStream( final InputStream byteStream ) 1233 { 1234 if ( isLoggable( Level.WARNING ) ) 1235 { 1236 log( Level.WARNING, getMessage( 1237 "unsupportedOperation", "setByteStream", 1238 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1239 1240 } 1241 } 1242 1243 public String getStringData() 1244 { 1245 return null; 1246 } 1247 1248 public void setStringData( final String stringData ) 1249 { 1250 if ( isLoggable( Level.WARNING ) ) 1251 { 1252 log( Level.WARNING, getMessage( 1253 "unsupportedOperation", "setStringData", 1254 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1255 1256 } 1257 } 1258 1259 public String getSystemId() 1260 { 1261 return schemaSource.getSystemId(); 1262 } 1263 1264 public void setSystemId( final String systemId ) 1265 { 1266 if ( isLoggable( Level.WARNING ) ) 1267 { 1268 log( Level.WARNING, getMessage( 1269 "unsupportedOperation", "setSystemId", 1270 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1271 1272 } 1273 } 1274 1275 public String getPublicId() 1276 { 1277 return schemaSource.getPublicId(); 1278 } 1279 1280 public void setPublicId( final String publicId ) 1281 { 1282 if ( isLoggable( Level.WARNING ) ) 1283 { 1284 log( Level.WARNING, getMessage( 1285 "unsupportedOperation", "setPublicId", 1286 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1287 1288 } 1289 } 1290 1291 public String getBaseURI() 1292 { 1293 return baseURI; 1294 } 1295 1296 public void setBaseURI( final String baseURI ) 1297 { 1298 if ( isLoggable( Level.WARNING ) ) 1299 { 1300 log( Level.WARNING, getMessage( 1301 "unsupportedOperation", "setBaseURI", 1302 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1303 1304 } 1305 } 1306 1307 public String getEncoding() 1308 { 1309 return schemaSource.getEncoding(); 1310 } 1311 1312 public void setEncoding( final String encoding ) 1313 { 1314 if ( isLoggable( Level.WARNING ) ) 1315 { 1316 log( Level.WARNING, getMessage( 1317 "unsupportedOperation", "setEncoding", 1318 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1319 1320 } 1321 } 1322 1323 public boolean getCertifiedText() 1324 { 1325 return false; 1326 } 1327 1328 public void setCertifiedText( final boolean certifiedText ) 1329 { 1330 if ( isLoggable( Level.WARNING ) ) 1331 { 1332 log( Level.WARNING, getMessage( 1333 "unsupportedOperation", "setCertifiedText", 1334 DefaultModelContext.class.getName() + ".LSResourceResolver" ), null ); 1335 1336 } 1337 } 1338 1339 }; 1340 } 1341 1342 } 1343 else if ( isLoggable( Level.WARNING ) ) 1344 { 1345 log( Level.WARNING, getMessage( "unsupportedResourceType", type ), null ); 1346 } 1347 } 1348 catch ( final SAXException e ) 1349 { 1350 String message = getMessage( e ); 1351 if ( message == null && e.getException() != null ) 1352 { 1353 message = getMessage( e.getException() ); 1354 } 1355 if ( message == null ) 1356 { 1357 message = ""; 1358 } 1359 else if ( message.length() > 0 ) 1360 { 1361 message = " " + message; 1362 } 1363 1364 String resource = ""; 1365 if ( resolvePublicId != null ) 1366 { 1367 resource = resolvePublicId + ", "; 1368 } 1369 resource += resolveSystemId; 1370 1371 if ( isLoggable( Level.SEVERE ) ) 1372 { 1373 log( Level.SEVERE, getMessage( "failedResolving", resource, message ), e ); 1374 } 1375 } 1376 catch ( final IOException e ) 1377 { 1378 String message = getMessage( e ); 1379 if ( message == null ) 1380 { 1381 message = ""; 1382 } 1383 else if ( message.length() > 0 ) 1384 { 1385 message = " " + message; 1386 } 1387 1388 String resource = ""; 1389 if ( resolvePublicId != null ) 1390 { 1391 resource = resolvePublicId + ", "; 1392 } 1393 resource += resolveSystemId; 1394 1395 if ( isLoggable( Level.SEVERE ) ) 1396 { 1397 log( Level.SEVERE, getMessage( "failedResolving", resource, message ), e ); 1398 } 1399 } 1400 1401 return null; 1402 } 1403 1404 }; 1405 } 1406 1407 private javax.xml.validation.Schema createSchema( final Schemas schemas, final EntityResolver entityResolver, 1408 final LSResourceResolver resourceResolver, final String model, 1409 final URI publicId ) throws ModelException 1410 { 1411 if ( entityResolver == null ) 1412 { 1413 throw new NullPointerException( "entityResolver" ); 1414 } 1415 if ( model != null && publicId != null ) 1416 { 1417 throw new IllegalArgumentException( "model=" + model + ", publicId=" + publicId.toASCIIString() ); 1418 } 1419 1420 try 1421 { 1422 final long t0 = System.nanoTime(); 1423 final SchemaFactory f = SchemaFactory.newInstance( XMLConstants.W3C_XML_SCHEMA_NS_URI ); 1424 final List<Source> sources = new ArrayList<Source>( schemas != null ? schemas.getSchema().size() : 0 ); 1425 1426 if ( schemas != null ) 1427 { 1428 for ( final Schema s : schemas.getSchema() ) 1429 { 1430 final InputSource inputSource = entityResolver.resolveEntity( s.getPublicId(), s.getSystemId() ); 1431 1432 if ( inputSource != null ) 1433 { 1434 sources.add( new SAXSource( inputSource ) ); 1435 } 1436 } 1437 } 1438 1439 if ( sources.isEmpty() ) 1440 { 1441 if ( model != null ) 1442 { 1443 throw new ModelException( getMessage( "missingSchemasForModel", model ) ); 1444 } 1445 if ( publicId != null ) 1446 { 1447 throw new ModelException( getMessage( "missingSchemasForPublicId", publicId ) ); 1448 } 1449 } 1450 1451 f.setResourceResolver( resourceResolver ); 1452 f.setErrorHandler( new ErrorHandler() 1453 { 1454 // See http://java.net/jira/browse/JAXP-66 1455 1456 public void warning( final SAXParseException e ) throws SAXException 1457 { 1458 String message = getMessage( e ); 1459 if ( message == null && e.getException() != null ) 1460 { 1461 message = getMessage( e.getException() ); 1462 } 1463 1464 if ( isLoggable( Level.WARNING ) ) 1465 { 1466 log( Level.WARNING, message, e ); 1467 } 1468 } 1469 1470 public void error( final SAXParseException e ) throws SAXException 1471 { 1472 throw e; 1473 } 1474 1475 public void fatalError( final SAXParseException e ) throws SAXException 1476 { 1477 throw e; 1478 } 1479 1480 } ); 1481 1482 final javax.xml.validation.Schema schema = f.newSchema( sources.toArray( new Source[ sources.size() ] ) ); 1483 1484 if ( this.isLoggable( Level.FINE ) ) 1485 { 1486 final StringBuilder schemaInfo = new StringBuilder( sources.size() * 50 ); 1487 1488 for ( final Source s : sources ) 1489 { 1490 schemaInfo.append( ", " ).append( s.getSystemId() ); 1491 } 1492 1493 this.log( Level.FINE, getMessage( "creatingSchema", schemaInfo.substring( 2 ), System.nanoTime() - t0 ), 1494 null ); 1495 1496 } 1497 1498 return schema; 1499 } 1500 catch ( final IOException e ) 1501 { 1502 throw new ModelException( getMessage( e ), e ); 1503 } 1504 catch ( final SAXException e ) 1505 { 1506 String message = getMessage( e ); 1507 if ( message == null && e.getException() != null ) 1508 { 1509 message = getMessage( e.getException() ); 1510 } 1511 1512 throw new ModelException( message, e ); 1513 } 1514 } 1515 1516 private JAXBContext createContext( final Schemas schemas, final String model, final URI publicId ) 1517 throws ModelException 1518 { 1519 if ( model != null && publicId != null ) 1520 { 1521 throw new IllegalArgumentException( "model=" + model + ", publicId=" + publicId.toASCIIString() ); 1522 } 1523 1524 try 1525 { 1526 StringBuilder packageNames = null; 1527 final long t0 = System.nanoTime(); 1528 1529 if ( schemas != null ) 1530 { 1531 packageNames = new StringBuilder( schemas.getSchema().size() * 25 ); 1532 1533 for ( final Schema schema : schemas.getSchema() ) 1534 { 1535 if ( schema.getContextId() != null ) 1536 { 1537 packageNames.append( ':' ).append( schema.getContextId() ); 1538 } 1539 } 1540 } 1541 1542 if ( packageNames == null || packageNames.length() == 0 ) 1543 { 1544 if ( model != null ) 1545 { 1546 throw new ModelException( getMessage( "missingSchemasForModel", model ) ); 1547 } 1548 if ( publicId != null ) 1549 { 1550 throw new ModelException( getMessage( "missingSchemasForPublicId", publicId ) ); 1551 } 1552 } 1553 1554 final JAXBContext context = JAXBContext.newInstance( packageNames.substring( 1 ), this.getClassLoader() ); 1555 1556 if ( this.isLoggable( Level.FINE ) ) 1557 { 1558 this.log( Level.FINE, getMessage( "creatingContext", packageNames.substring( 1 ), 1559 System.nanoTime() - t0 ), null ); 1560 1561 } 1562 1563 return context; 1564 } 1565 catch ( final JAXBException e ) 1566 { 1567 String message = getMessage( e ); 1568 if ( message == null && e.getLinkedException() != null ) 1569 { 1570 message = getMessage( e.getLinkedException() ); 1571 } 1572 1573 throw new ModelException( message, e ); 1574 } 1575 } 1576 1577 private Marshaller createMarshaller( final String model, final URI publicId ) 1578 throws ModelException 1579 { 1580 if ( model != null && publicId != null ) 1581 { 1582 throw new IllegalArgumentException( "model=" + model + ", publicId=" + publicId.toASCIIString() ); 1583 } 1584 1585 Schemas schemas = null; 1586 1587 if ( model != null ) 1588 { 1589 schemas = this.getModlets().getSchemas( model ); 1590 } 1591 1592 if ( publicId != null ) 1593 { 1594 schemas = this.getModlets().getSchemas( publicId ); 1595 } 1596 1597 try 1598 { 1599 StringBuilder packageNames = null; 1600 StringBuilder schemaLocation = null; 1601 final long t0 = System.nanoTime(); 1602 1603 if ( schemas != null ) 1604 { 1605 packageNames = new StringBuilder( schemas.getSchema().size() * 25 ); 1606 schemaLocation = new StringBuilder( schemas.getSchema().size() * 50 ); 1607 1608 for ( final Schema schema : schemas.getSchema() ) 1609 { 1610 if ( schema.getContextId() != null ) 1611 { 1612 packageNames.append( ':' ).append( schema.getContextId() ); 1613 } 1614 if ( schema.getPublicId() != null && schema.getSystemId() != null ) 1615 { 1616 schemaLocation.append( ' ' ).append( schema.getPublicId() ).append( ' ' ). 1617 append( schema.getSystemId() ); 1618 1619 } 1620 } 1621 } 1622 1623 if ( packageNames == null || packageNames.length() == 0 ) 1624 { 1625 if ( model != null ) 1626 { 1627 throw new ModelException( getMessage( "missingSchemasForModel", model ) ); 1628 } 1629 if ( publicId != null ) 1630 { 1631 throw new ModelException( getMessage( "missingSchemasForPublicId", publicId ) ); 1632 } 1633 } 1634 1635 final Marshaller m = 1636 JAXBContext.newInstance( packageNames.substring( 1 ), this.getClassLoader() ).createMarshaller(); 1637 1638 if ( schemaLocation != null && schemaLocation.length() != 0 ) 1639 { 1640 m.setProperty( Marshaller.JAXB_SCHEMA_LOCATION, schemaLocation.substring( 1 ) ); 1641 } 1642 1643 MarshallerListenerList listenerList = null; 1644 1645 if ( model != null ) 1646 { 1647 final Collection<? extends Marshaller.Listener> listeners = 1648 this.createServiceObjects( model, MARSHALLER_LISTENER_SERVICE, Marshaller.Listener.class ); 1649 1650 if ( !listeners.isEmpty() ) 1651 { 1652 listenerList = new MarshallerListenerList(); 1653 listenerList.getListeners().addAll( listeners ); 1654 m.setListener( listenerList ); 1655 } 1656 } 1657 1658 if ( this.isLoggable( Level.FINE ) ) 1659 { 1660 if ( listenerList == null ) 1661 { 1662 this.log( Level.FINE, getMessage( "creatingMarshaller", packageNames.substring( 1 ), 1663 schemaLocation.substring( 1 ), 1664 System.nanoTime() - t0 ), null ); 1665 1666 } 1667 else 1668 { 1669 final StringBuilder b = new StringBuilder( listenerList.getListeners().size() * 100 ); 1670 1671 for ( int i = 0, s0 = listenerList.getListeners().size(); i < s0; i++ ) 1672 { 1673 b.append( ',' ).append( listenerList.getListeners().get( i ) ); 1674 } 1675 1676 this.log( Level.FINE, getMessage( "creatingMarshallerWithListeners", packageNames.substring( 1 ), 1677 schemaLocation.substring( 1 ), b.substring( 1 ), 1678 System.nanoTime() - t0 ), null ); 1679 1680 } 1681 } 1682 1683 return m; 1684 } 1685 catch ( final JAXBException e ) 1686 { 1687 String message = getMessage( e ); 1688 if ( message == null && e.getLinkedException() != null ) 1689 { 1690 message = getMessage( e.getLinkedException() ); 1691 } 1692 1693 throw new ModelException( message, e ); 1694 } 1695 } 1696 1697 private Unmarshaller createUnmarshaller( final String model, final URI publicId ) 1698 throws ModelException 1699 { 1700 if ( model != null && publicId != null ) 1701 { 1702 throw new IllegalArgumentException( "model=" + model + ", publicId=" + publicId.toASCIIString() ); 1703 } 1704 1705 Schemas schemas = null; 1706 1707 if ( model != null ) 1708 { 1709 schemas = this.getModlets().getSchemas( model ); 1710 } 1711 1712 if ( publicId != null ) 1713 { 1714 schemas = this.getModlets().getSchemas( publicId ); 1715 } 1716 1717 try 1718 { 1719 StringBuilder packageNames = null; 1720 final long t0 = System.nanoTime(); 1721 1722 if ( schemas != null ) 1723 { 1724 packageNames = new StringBuilder( schemas.getSchema().size() * 25 ); 1725 1726 for ( final Schema schema : schemas.getSchema() ) 1727 { 1728 if ( schema.getContextId() != null ) 1729 { 1730 packageNames.append( ':' ).append( schema.getContextId() ); 1731 } 1732 } 1733 } 1734 1735 if ( packageNames == null || packageNames.length() == 0 ) 1736 { 1737 if ( model != null ) 1738 { 1739 throw new ModelException( getMessage( "missingSchemasForModel", model ) ); 1740 } 1741 if ( publicId != null ) 1742 { 1743 throw new ModelException( getMessage( "missingSchemasForPublicId", publicId ) ); 1744 } 1745 } 1746 1747 final Unmarshaller u = 1748 JAXBContext.newInstance( packageNames.substring( 1 ), this.getClassLoader() ).createUnmarshaller(); 1749 1750 UnmarshallerListenerList listenerList = null; 1751 1752 if ( model != null ) 1753 { 1754 final Collection<? extends Unmarshaller.Listener> listeners = 1755 this.createServiceObjects( model, UNMARSHALLER_LISTENER_SERVICE, Unmarshaller.Listener.class ); 1756 1757 if ( !listeners.isEmpty() ) 1758 { 1759 listenerList = new UnmarshallerListenerList(); 1760 listenerList.getListeners().addAll( listeners ); 1761 u.setListener( listenerList ); 1762 } 1763 } 1764 1765 if ( this.isLoggable( Level.FINE ) ) 1766 { 1767 if ( listenerList == null ) 1768 { 1769 this.log( Level.FINE, getMessage( "creatingUnmarshaller", packageNames.substring( 1 ), 1770 System.nanoTime() - t0 ), null ); 1771 1772 } 1773 else 1774 { 1775 final StringBuilder b = new StringBuilder( listenerList.getListeners().size() * 100 ); 1776 1777 for ( int i = 0, s0 = listenerList.getListeners().size(); i < s0; i++ ) 1778 { 1779 b.append( ',' ).append( listenerList.getListeners().get( i ) ); 1780 } 1781 1782 this.log( Level.FINE, getMessage( "creatingUnmarshallerWithListeners", 1783 packageNames.substring( 1 ), b.substring( 1 ), 1784 System.nanoTime() - t0 ), null ); 1785 1786 } 1787 } 1788 1789 return u; 1790 } 1791 catch ( final JAXBException e ) 1792 { 1793 String message = getMessage( e ); 1794 if ( message == null && e.getLinkedException() != null ) 1795 { 1796 message = getMessage( e.getLinkedException() ); 1797 } 1798 1799 throw new ModelException( message, e ); 1800 } 1801 } 1802 1803 /** 1804 * {@inheritDoc} 1805 * <p> 1806 * This method loads {@code ServiceFactory} classes setup via the platform provider configuration file and 1807 * {@code <provider-location>/org.jomc.modlet.ServiceFactory} resources to create new service objects. 1808 * </p> 1809 * 1810 * @since 1.9 1811 */ 1812 @Override 1813 public <T> Collection<? extends T> createServiceObjects( final String model, final String service, 1814 final Class<T> type ) 1815 throws ModelException 1816 { 1817 if ( model == null ) 1818 { 1819 throw new NullPointerException( "model" ); 1820 } 1821 if ( service == null ) 1822 { 1823 throw new NullPointerException( "service" ); 1824 } 1825 if ( type == null ) 1826 { 1827 throw new NullPointerException( "type" ); 1828 } 1829 1830 final Services modelServices = this.getModlets().getServices( model ); 1831 final Collection<T> serviceObjects = 1832 new ArrayList<T>( modelServices != null ? modelServices.getService().size() : 0 ); 1833 1834 if ( modelServices != null ) 1835 { 1836 final Collection<ServiceFactory> factories = this.loadModletServices( ServiceFactory.class ); 1837 1838 for ( final Service s : modelServices.getServices( service ) ) 1839 { 1840 serviceObjects.add( this.createServiceObject( s, type, factories ) ); 1841 } 1842 } 1843 1844 return Collections.unmodifiableCollection( serviceObjects ); 1845 } 1846 1847 /** 1848 * This method creates a new service object for a given service using a given collection of service factories. 1849 * 1850 * @param <T> The type of the service. 1851 * @param service The service to create a new object of. 1852 * @param type The class of the type of the service. 1853 * @param factories The service factories to use for creating the new service object. 1854 * 1855 * @return An new service object for {@code service}. 1856 * 1857 * @throws NullPointerException if {@code service}, {@code type} or {@code factories} is {@code null}. 1858 * @throws ModelException if creating the service object fails. 1859 * @since 1.9 1860 */ 1861 private <T> T createServiceObject( final Service service, final Class<T> type, 1862 final Collection<ServiceFactory> factories ) throws ModelException 1863 { 1864 if ( service == null ) 1865 { 1866 throw new NullPointerException( "service" ); 1867 } 1868 if ( type == null ) 1869 { 1870 throw new NullPointerException( "type" ); 1871 } 1872 if ( factories == null ) 1873 { 1874 throw new NullPointerException( "factories" ); 1875 } 1876 1877 T serviceObject = null; 1878 1879 for ( final ServiceFactory factory : factories ) 1880 { 1881 final T current = factory.createServiceObject( this, service, type ); 1882 1883 if ( current != null ) 1884 { 1885 if ( this.isLoggable( Level.FINER ) ) 1886 { 1887 this.log( Level.FINER, getMessage( "creatingService", service.getOrdinal(), service.getIdentifier(), 1888 service.getClazz(), factory.toString() ), null ); 1889 1890 } 1891 1892 serviceObject = current; 1893 break; 1894 } 1895 } 1896 1897 if ( serviceObject == null ) 1898 { 1899 throw new ModelException( getMessage( "serviceNotCreated", service.getOrdinal(), service.getIdentifier(), 1900 service.getClazz() ), null ); 1901 1902 } 1903 1904 return serviceObject; 1905 } 1906 1907 private <T> Collection<T> loadModletServices( final Class<T> serviceClass ) throws ModelException 1908 { 1909 InputStream in = null; 1910 BufferedReader reader = null; 1911 1912 try 1913 { 1914 final String serviceNamePrefix = serviceClass.getName() + "."; 1915 final Map<String, T> sortedPlatformServices = new TreeMap<String, T>( new Comparator<String>() 1916 { 1917 1918 public int compare( final String key1, final String key2 ) 1919 { 1920 return key1.compareTo( key2 ); 1921 } 1922 1923 } ); 1924 1925 final File platformServices = new File( this.getPlatformProviderLocation() ); 1926 1927 if ( platformServices.exists() ) 1928 { 1929 if ( this.isLoggable( Level.FINEST ) ) 1930 { 1931 this.log( Level.FINEST, getMessage( "processing", platformServices.getAbsolutePath() ), null ); 1932 } 1933 1934 final java.util.Properties p = new java.util.Properties(); 1935 1936 in = new FileInputStream( platformServices ); 1937 1938 p.load( in ); 1939 1940 in.close(); 1941 in = null; 1942 1943 for ( final Map.Entry<Object, Object> e : p.entrySet() ) 1944 { 1945 if ( e.getKey().toString().startsWith( serviceNamePrefix ) ) 1946 { 1947 final String configuration = e.getValue().toString(); 1948 1949 if ( this.isLoggable( Level.FINEST ) ) 1950 { 1951 this.log( Level.FINEST, getMessage( "serviceInfo", platformServices.getAbsolutePath(), 1952 serviceClass.getName(), configuration ), null ); 1953 1954 } 1955 1956 sortedPlatformServices.put( e.getKey().toString(), 1957 this.createModletServiceObject( serviceClass, configuration ) ); 1958 1959 } 1960 } 1961 } 1962 1963 final Enumeration<URL> classpathServices = 1964 this.findResources( this.getProviderLocation() + '/' + serviceClass.getName() ); 1965 1966 int count = 0; 1967 final long t0 = System.nanoTime(); 1968 final List<T> sortedClasspathServices = new LinkedList<T>(); 1969 1970 while ( classpathServices.hasMoreElements() ) 1971 { 1972 count++; 1973 final URL url = classpathServices.nextElement(); 1974 1975 if ( this.isLoggable( Level.FINEST ) ) 1976 { 1977 this.log( Level.FINEST, getMessage( "processing", url.toExternalForm() ), null ); 1978 } 1979 1980 reader = new BufferedReader( new InputStreamReader( url.openStream(), "UTF-8" ) ); 1981 1982 for ( String line = reader.readLine(); line != null; line = reader.readLine() ) 1983 { 1984 if ( line.contains( "#" ) ) 1985 { 1986 continue; 1987 } 1988 1989 if ( this.isLoggable( Level.FINEST ) ) 1990 { 1991 this.log( Level.FINEST, getMessage( "serviceInfo", url.toExternalForm(), 1992 serviceClass.getName(), line ), null ); 1993 1994 } 1995 1996 final T serviceObject = this.createModletServiceObject( serviceClass, line ); 1997 sortedClasspathServices.add( serviceObject ); 1998 } 1999 2000 Collections.sort( sortedClasspathServices, 2001 new Comparator<Object>() 2002 { 2003 2004 public int compare( final Object o1, final Object o2 ) 2005 { 2006 return ordinalOf( o1 ) - ordinalOf( o2 ); 2007 } 2008 2009 } ); 2010 2011 reader.close(); 2012 reader = null; 2013 } 2014 2015 if ( this.isLoggable( Level.FINE ) ) 2016 { 2017 this.log( Level.FINE, getMessage( "contextReport", count, 2018 this.getProviderLocation() + '/' + serviceClass.getName(), 2019 System.nanoTime() - t0 ), null ); 2020 2021 } 2022 2023 final List<T> services = 2024 new ArrayList<T>( sortedPlatformServices.size() + sortedClasspathServices.size() ); 2025 2026 services.addAll( sortedPlatformServices.values() ); 2027 services.addAll( sortedClasspathServices ); 2028 2029 return services; 2030 } 2031 catch ( final IOException e ) 2032 { 2033 throw new ModelException( getMessage( e ), e ); 2034 } 2035 finally 2036 { 2037 try 2038 { 2039 if ( in != null ) 2040 { 2041 in.close(); 2042 } 2043 } 2044 catch ( final IOException e ) 2045 { 2046 this.log( Level.SEVERE, getMessage( e ), e ); 2047 } 2048 finally 2049 { 2050 try 2051 { 2052 if ( reader != null ) 2053 { 2054 reader.close(); 2055 } 2056 } 2057 catch ( final IOException e ) 2058 { 2059 this.log( Level.SEVERE, getMessage( e ), e ); 2060 } 2061 } 2062 } 2063 } 2064 2065 private <T> T createModletServiceObject( final Class<T> serviceClass, final String configuration ) 2066 throws ModelException 2067 { 2068 String className = configuration; 2069 final int i0 = configuration.indexOf( '[' ); 2070 final int i1 = configuration.lastIndexOf( ']' ); 2071 final Service service = new Service(); 2072 service.setIdentifier( serviceClass.getName() ); 2073 2074 if ( i0 != -1 && i1 != -1 ) 2075 { 2076 className = configuration.substring( 0, i0 ); 2077 final StringTokenizer propertyTokens = 2078 new StringTokenizer( configuration.substring( i0 + 1, i1 ), "," ); 2079 2080 while ( propertyTokens.hasMoreTokens() ) 2081 { 2082 final String property = propertyTokens.nextToken(); 2083 final int d0 = property.indexOf( '=' ); 2084 2085 String propertyName = property; 2086 String propertyValue = null; 2087 2088 if ( d0 != -1 ) 2089 { 2090 propertyName = property.substring( 0, d0 ); 2091 propertyValue = property.substring( d0 + 1, property.length() ); 2092 } 2093 2094 final Property p = new Property(); 2095 service.getProperty().add( p ); 2096 2097 p.setName( propertyName ); 2098 p.setValue( propertyValue ); 2099 } 2100 } 2101 2102 service.setClazz( className ); 2103 2104 // Need a way to exchange the service factory creating modlet service objects? 2105 return new DefaultServiceFactory().createServiceObject( this, service, serviceClass ); 2106 } 2107 2108 /** 2109 * Searches the context for {@code META-INF/MANIFEST.MF} resources and returns a set of URIs of entries whose names 2110 * end with a known schema extension. 2111 * 2112 * @return Set of URIs of any matching entries. 2113 * 2114 * @throws IOException if reading fails. 2115 * @throws URISyntaxException if parsing fails. 2116 * @throws ModelException if searching the context fails. 2117 */ 2118 private Set<URI> getSchemaResources() throws IOException, URISyntaxException, ModelException 2119 { 2120 final Set<URI> resources = new HashSet<URI>(); 2121 final long t0 = System.nanoTime(); 2122 int count = 0; 2123 2124 for ( final Enumeration<URL> e = this.findResources( "META-INF/MANIFEST.MF" ); e.hasMoreElements(); ) 2125 { 2126 InputStream manifestStream = null; 2127 2128 try 2129 { 2130 count++; 2131 final URL manifestUrl = e.nextElement(); 2132 final String externalForm = manifestUrl.toExternalForm(); 2133 final String baseUrl = externalForm.substring( 0, externalForm.indexOf( "META-INF" ) ); 2134 manifestStream = manifestUrl.openStream(); 2135 final Manifest mf = new Manifest( manifestStream ); 2136 2137 if ( this.isLoggable( Level.FINEST ) ) 2138 { 2139 this.log( Level.FINEST, getMessage( "processing", externalForm ), null ); 2140 } 2141 2142 for ( final Map.Entry<String, Attributes> entry : mf.getEntries().entrySet() ) 2143 { 2144 for ( int i = SCHEMA_EXTENSIONS.length - 1; i >= 0; i-- ) 2145 { 2146 if ( entry.getKey().toLowerCase().endsWith( '.' + SCHEMA_EXTENSIONS[i].toLowerCase() ) ) 2147 { 2148 final URL schemaUrl = new URL( baseUrl + entry.getKey() ); 2149 resources.add( schemaUrl.toURI() ); 2150 2151 if ( this.isLoggable( Level.FINEST ) ) 2152 { 2153 this.log( Level.FINEST, getMessage( "foundSchemaCandidate", 2154 schemaUrl.toExternalForm() ), null ); 2155 2156 } 2157 } 2158 } 2159 } 2160 2161 manifestStream.close(); 2162 manifestStream = null; 2163 } 2164 finally 2165 { 2166 try 2167 { 2168 if ( manifestStream != null ) 2169 { 2170 manifestStream.close(); 2171 } 2172 } 2173 catch ( final IOException ex ) 2174 { 2175 this.log( Level.SEVERE, getMessage( ex ), ex ); 2176 } 2177 } 2178 } 2179 2180 if ( this.isLoggable( Level.FINE ) ) 2181 { 2182 this.log( Level.FINE, getMessage( "contextReport", count, "META-INF/MANIFEST.MF", System.nanoTime() - t0 ), 2183 null ); 2184 2185 } 2186 2187 return resources; 2188 } 2189 2190 private static int ordinalOf( final Object serviceObject ) 2191 { 2192 int ordinal = 0; 2193 2194 if ( serviceObject instanceof ModletProvider ) 2195 { 2196 ordinal = ( (ModletProvider) serviceObject ).getOrdinal(); 2197 } 2198 if ( serviceObject instanceof ModletProcessor ) 2199 { 2200 ordinal = ( (ModletProcessor) serviceObject ).getOrdinal(); 2201 } 2202 if ( serviceObject instanceof ModletValidator ) 2203 { 2204 ordinal = ( (ModletValidator) serviceObject ).getOrdinal(); 2205 } 2206 if ( serviceObject instanceof ServiceFactory ) 2207 { 2208 ordinal = ( (ServiceFactory) serviceObject ).getOrdinal(); 2209 } 2210 2211 return ordinal; 2212 } 2213 2214 private static String getMessage( final String key, final Object... arguments ) 2215 { 2216 return MessageFormat.format( ResourceBundle.getBundle( 2217 DefaultModelContext.class.getName().replace( '.', '/' ) ).getString( key ), arguments ); 2218 2219 } 2220 2221 private static String getMessage( final Throwable t ) 2222 { 2223 return t != null 2224 ? t.getMessage() != null && t.getMessage().trim().length() > 0 2225 ? t.getMessage() 2226 : getMessage( t.getCause() ) 2227 : null; 2228 2229 } 2230 2231} 2232 2233/** 2234 * {@code ErrorHandler} collecting {@code ModelValidationReport} details. 2235 * 2236 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 2237 * @version $JOMC: DefaultModelContext.java 5305 2016-08-30 21:46:23Z schulte $ 2238 */ 2239class ModelErrorHandler extends DefaultHandler 2240{ 2241 2242 /** 2243 * The context of the instance. 2244 */ 2245 private final ModelContext context; 2246 2247 /** 2248 * The report of the instance. 2249 */ 2250 private final ModelValidationReport report; 2251 2252 /** 2253 * Creates a new {@code ModelErrorHandler} instance taking a context. 2254 * 2255 * @param context The context of the instance. 2256 */ 2257 ModelErrorHandler( final ModelContext context ) 2258 { 2259 this( context, new ModelValidationReport() ); 2260 } 2261 2262 /** 2263 * Creates a new {@code ModelErrorHandler} instance taking a report to use for collecting validation events. 2264 * 2265 * @param context The context of the instance. 2266 * @param report A report to use for collecting validation events. 2267 */ 2268 ModelErrorHandler( final ModelContext context, final ModelValidationReport report ) 2269 { 2270 super(); 2271 this.context = context; 2272 this.report = report; 2273 } 2274 2275 /** 2276 * Gets the report of the instance. 2277 * 2278 * @return The report of the instance. 2279 */ 2280 public ModelValidationReport getReport() 2281 { 2282 return this.report; 2283 } 2284 2285 @Override 2286 public void warning( final SAXParseException exception ) throws SAXException 2287 { 2288 String message = getMessage( exception ); 2289 if ( message == null && exception.getException() != null ) 2290 { 2291 message = getMessage( exception.getException() ); 2292 } 2293 2294 if ( this.context != null && this.context.isLoggable( Level.FINE ) ) 2295 { 2296 this.context.log( Level.FINE, message, exception ); 2297 } 2298 2299 this.getReport().getDetails().add( new ModelValidationReport.Detail( 2300 "W3C XML 1.0 Recommendation - Warning condition", Level.WARNING, message, null ) ); 2301 2302 } 2303 2304 @Override 2305 public void error( final SAXParseException exception ) throws SAXException 2306 { 2307 String message = getMessage( exception ); 2308 if ( message == null && exception.getException() != null ) 2309 { 2310 message = getMessage( exception.getException() ); 2311 } 2312 2313 if ( this.context != null && this.context.isLoggable( Level.FINE ) ) 2314 { 2315 this.context.log( Level.FINE, message, exception ); 2316 } 2317 2318 this.getReport().getDetails().add( new ModelValidationReport.Detail( 2319 "W3C XML 1.0 Recommendation - Section 1.2 - Error", Level.SEVERE, message, null ) ); 2320 2321 } 2322 2323 @Override 2324 public void fatalError( final SAXParseException exception ) throws SAXException 2325 { 2326 String message = getMessage( exception ); 2327 if ( message == null && exception.getException() != null ) 2328 { 2329 message = getMessage( exception.getException() ); 2330 } 2331 2332 if ( this.context != null && this.context.isLoggable( Level.FINE ) ) 2333 { 2334 this.context.log( Level.FINE, message, exception ); 2335 } 2336 2337 this.getReport().getDetails().add( new ModelValidationReport.Detail( 2338 "W3C XML 1.0 Recommendation - Section 1.2 - Fatal Error", Level.SEVERE, message, null ) ); 2339 2340 } 2341 2342 private static String getMessage( final Throwable t ) 2343 { 2344 return t != null 2345 ? t.getMessage() != null && t.getMessage().trim().length() > 0 2346 ? t.getMessage() 2347 : getMessage( t.getCause() ) 2348 : null; 2349 2350 } 2351 2352} 2353 2354/** 2355 * List of {@code Marshaller.Listener}s. 2356 * 2357 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 2358 * @version $JOMC: DefaultModelContext.java 5305 2016-08-30 21:46:23Z schulte $ 2359 * @since 1.2 2360 */ 2361class MarshallerListenerList extends Marshaller.Listener 2362{ 2363 2364 /** 2365 * The {@code Marshaller.Listener}s of the instance. 2366 */ 2367 private final List<Marshaller.Listener> listeners = new CopyOnWriteArrayList<Marshaller.Listener>(); 2368 2369 /** 2370 * Creates a new {@code MarshallerListenerList} instance. 2371 */ 2372 MarshallerListenerList() 2373 { 2374 super(); 2375 } 2376 2377 /** 2378 * Gets the listeners of the instance. 2379 * <p> 2380 * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 2381 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 2382 * listeners property. 2383 * </p> 2384 * 2385 * @return The list of listeners of the instance. 2386 */ 2387 List<Marshaller.Listener> getListeners() 2388 { 2389 return this.listeners; 2390 } 2391 2392 @Override 2393 public void beforeMarshal( final Object source ) 2394 { 2395 for ( final Marshaller.Listener listener : this.getListeners() ) 2396 { 2397 listener.beforeMarshal( source ); 2398 } 2399 } 2400 2401 @Override 2402 public void afterMarshal( final Object source ) 2403 { 2404 for ( final Marshaller.Listener listener : this.getListeners() ) 2405 { 2406 listener.afterMarshal( source ); 2407 } 2408 } 2409 2410} 2411 2412/** 2413 * List of {@code Unmarshaller.Listener}s. 2414 * 2415 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 2416 * @version $JOMC: DefaultModelContext.java 5305 2016-08-30 21:46:23Z schulte $ 2417 * @since 1.2 2418 */ 2419class UnmarshallerListenerList extends Unmarshaller.Listener 2420{ 2421 2422 /** 2423 * The {@code Unmarshaller.Listener}s of the instance. 2424 */ 2425 private final List<Unmarshaller.Listener> listeners = new CopyOnWriteArrayList<Unmarshaller.Listener>(); 2426 2427 /** 2428 * Creates a new {@code UnmarshallerListenerList} instance. 2429 */ 2430 UnmarshallerListenerList() 2431 { 2432 super(); 2433 } 2434 2435 /** 2436 * Gets the listeners of the instance. 2437 * <p> 2438 * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 2439 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 2440 * listeners property. 2441 * </p> 2442 * 2443 * @return The list of listeners of the instance. 2444 */ 2445 List<Unmarshaller.Listener> getListeners() 2446 { 2447 return this.listeners; 2448 } 2449 2450 @Override 2451 public void beforeUnmarshal( final Object target, final Object parent ) 2452 { 2453 for ( final Unmarshaller.Listener listener : this.getListeners() ) 2454 { 2455 listener.beforeUnmarshal( target, parent ); 2456 } 2457 } 2458 2459 @Override 2460 public void afterUnmarshal( final Object target, final Object parent ) 2461 { 2462 for ( final Unmarshaller.Listener listener : this.getListeners() ) 2463 { 2464 listener.afterUnmarshal( target, parent ); 2465 } 2466 } 2467 2468}