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