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: ModelContext.java 5305 2016-08-30 21:46:23Z schulte $ 029 * 030 */ 031package org.jomc.modlet; 032 033import java.io.IOException; 034import java.lang.reflect.Constructor; 035import java.lang.reflect.InvocationTargetException; 036import java.net.URI; 037import java.net.URL; 038import java.text.MessageFormat; 039import java.util.Collection; 040import java.util.Collections; 041import java.util.Enumeration; 042import java.util.List; 043import java.util.Locale; 044import java.util.Map; 045import java.util.ResourceBundle; 046import java.util.Set; 047import java.util.concurrent.ConcurrentHashMap; 048import java.util.concurrent.CopyOnWriteArrayList; 049import java.util.concurrent.ExecutorService; 050import java.util.logging.Level; 051import javax.xml.bind.JAXBContext; 052import javax.xml.bind.Marshaller; 053import javax.xml.bind.Unmarshaller; 054import javax.xml.transform.Source; 055import org.w3c.dom.ls.LSResourceResolver; 056import org.xml.sax.EntityResolver; 057 058/** 059 * Model context interface. 060 * <p> 061 * <b>Use Cases:</b><br/><ul> 062 * <li>{@link #createContext(java.lang.String) }</li> 063 * <li>{@link #createEntityResolver(java.lang.String) }</li> 064 * <li>{@link #createMarshaller(java.lang.String) }</li> 065 * <li>{@link #createResourceResolver(java.lang.String) }</li> 066 * <li>{@link #createSchema(java.lang.String) }</li> 067 * <li>{@link #createUnmarshaller(java.lang.String) }</li> 068 * <li>{@link #findModel(java.lang.String) }</li> 069 * <li>{@link #findModel(org.jomc.modlet.Model) }</li> 070 * <li>{@link #processModel(org.jomc.modlet.Model) }</li> 071 * <li>{@link #validateModel(org.jomc.modlet.Model) }</li> 072 * <li>{@link #validateModel(java.lang.String, javax.xml.transform.Source) }</li> 073 * </ul> 074 * 075 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 076 * @version $JOMC: ModelContext.java 5305 2016-08-30 21:46:23Z schulte $ 077 * 078 * @see ModelContextFactory 079 */ 080public abstract class ModelContext 081{ 082 083 /** 084 * Listener interface. 085 */ 086 public abstract static class Listener 087 { 088 089 /** 090 * Creates a new {@code Listener} instance. 091 */ 092 public Listener() 093 { 094 super(); 095 } 096 097 /** 098 * Gets called on logging. 099 * 100 * @param level The level of the event. 101 * @param message The message of the event or {@code null}. 102 * @param t The throwable of the event or {@code null}. 103 * 104 * @throws NullPointerException if {@code level} is {@code null}. 105 */ 106 public void onLog( final Level level, final String message, final Throwable t ) 107 { 108 if ( level == null ) 109 { 110 throw new NullPointerException( "level" ); 111 } 112 } 113 114 } 115 116 /** 117 * Default {@code http://jomc.org/modlet} namespace schema system id. 118 * 119 * @see #getDefaultModletSchemaSystemId() 120 */ 121 private static final String DEFAULT_MODLET_SCHEMA_SYSTEM_ID = 122 "http://xml.jomc.org/modlet/jomc-modlet-1.9.xsd"; 123 124 /** 125 * Log level events are logged at by default. 126 * 127 * @see #getDefaultLogLevel() 128 */ 129 private static final Level DEFAULT_LOG_LEVEL = Level.WARNING; 130 131 /** 132 * Default log level. 133 */ 134 private static volatile Level defaultLogLevel; 135 136 /** 137 * Default {@code http://jomc.org/model/modlet} namespace schema system id. 138 */ 139 private static volatile String defaultModletSchemaSystemId; 140 141 /** 142 * Class name of the {@code ModelContext} implementation. 143 */ 144 @Deprecated 145 private static volatile String modelContextClassName; 146 147 /** 148 * The attributes of the instance. 149 */ 150 private final Map<String, Object> attributes = new ConcurrentHashMap<String, Object>( 32, .75f, 1 ); 151 152 /** 153 * The class loader of the instance. 154 */ 155 private volatile ClassLoader classLoader; 156 157 /** 158 * Flag indicating the {@code classLoader} field is initialized. 159 * 160 * @since 1.2 161 */ 162 private volatile boolean classLoaderSet; 163 164 /** 165 * The listeners of the instance. 166 */ 167 private final List<Listener> listeners = new CopyOnWriteArrayList<Listener>(); 168 169 /** 170 * Log level of the instance. 171 */ 172 private volatile Level logLevel; 173 174 /** 175 * The {@code Modlets} of the instance. 176 */ 177 private volatile Modlets modlets; 178 179 /** 180 * Modlet namespace schema system id of the instance. 181 */ 182 private volatile String modletSchemaSystemId; 183 184 /** 185 * The {@code ExecutorService} of the instance. 186 * 187 * @since 1.10 188 */ 189 private volatile ExecutorService executorService; 190 191 /** 192 * Creates a new {@code ModelContext} instance. 193 * 194 * @since 1.2 195 */ 196 public ModelContext() 197 { 198 super(); 199 this.classLoader = null; 200 this.classLoaderSet = false; 201 } 202 203 /** 204 * Creates a new {@code ModelContext} instance taking a class loader. 205 * 206 * @param classLoader The class loader of the context. 207 * 208 * @see #getClassLoader() 209 */ 210 public ModelContext( final ClassLoader classLoader ) 211 { 212 super(); 213 this.classLoader = classLoader; 214 this.classLoaderSet = true; 215 } 216 217 /** 218 * Gets a set holding the names of all attributes of the context. 219 * 220 * @return An unmodifiable set holding the names of all attributes of the context. 221 * 222 * @see #clearAttribute(java.lang.String) 223 * @see #getAttribute(java.lang.String) 224 * @see #getAttribute(java.lang.String, java.lang.Object) 225 * @see #setAttribute(java.lang.String, java.lang.Object) 226 * @since 1.2 227 */ 228 public Set<String> getAttributeNames() 229 { 230 return Collections.unmodifiableSet( this.attributes.keySet() ); 231 } 232 233 /** 234 * Gets an attribute of the context. 235 * 236 * @param name The name of the attribute to get. 237 * 238 * @return The value of the attribute with name {@code name}; {@code null} if no attribute matching {@code name} is 239 * found. 240 * 241 * @throws NullPointerException if {@code name} is {@code null}. 242 * 243 * @see #getAttribute(java.lang.String, java.lang.Object) 244 * @see #setAttribute(java.lang.String, java.lang.Object) 245 * @see #clearAttribute(java.lang.String) 246 */ 247 public Object getAttribute( final String name ) 248 { 249 if ( name == null ) 250 { 251 throw new NullPointerException( "name" ); 252 } 253 254 return this.attributes.get( name ); 255 } 256 257 /** 258 * Gets an attribute of the context. 259 * 260 * @param name The name of the attribute to get. 261 * @param def The value to return if no attribute matching {@code name} is found. 262 * 263 * @return The value of the attribute with name {@code name}; {@code def} if no such attribute is found. 264 * 265 * @throws NullPointerException if {@code name} is {@code null}. 266 * 267 * @see #getAttribute(java.lang.String) 268 * @see #setAttribute(java.lang.String, java.lang.Object) 269 * @see #clearAttribute(java.lang.String) 270 */ 271 public Object getAttribute( final String name, final Object def ) 272 { 273 if ( name == null ) 274 { 275 throw new NullPointerException( "name" ); 276 } 277 278 Object value = this.getAttribute( name ); 279 280 if ( value == null ) 281 { 282 value = def; 283 } 284 285 return value; 286 } 287 288 /** 289 * Sets an attribute in the context. 290 * 291 * @param name The name of the attribute to set. 292 * @param value The value of the attribute to set. 293 * 294 * @return The previous value of the attribute with name {@code name}; {@code null} if no such value is found. 295 * 296 * @throws NullPointerException if {@code name} or {@code value} is {@code null}. 297 * 298 * @see #getAttribute(java.lang.String) 299 * @see #getAttribute(java.lang.String, java.lang.Object) 300 * @see #clearAttribute(java.lang.String) 301 */ 302 public Object setAttribute( final String name, final Object value ) 303 { 304 if ( name == null ) 305 { 306 throw new NullPointerException( "name" ); 307 } 308 if ( value == null ) 309 { 310 throw new NullPointerException( "value" ); 311 } 312 313 return this.attributes.put( name, value ); 314 } 315 316 /** 317 * Removes an attribute from the context. 318 * 319 * @param name The name of the attribute to remove. 320 * 321 * @throws NullPointerException if {@code name} is {@code null}. 322 * 323 * @see #getAttribute(java.lang.String) 324 * @see #getAttribute(java.lang.String, java.lang.Object) 325 * @see #setAttribute(java.lang.String, java.lang.Object) 326 */ 327 public void clearAttribute( final String name ) 328 { 329 if ( name == null ) 330 { 331 throw new NullPointerException( "name" ); 332 } 333 334 this.attributes.remove( name ); 335 } 336 337 /** 338 * Gets the class loader of the context. 339 * 340 * @return The class loader of the context or {@code null}, indicating the bootstrap class loader. 341 * 342 * @see #findClass(java.lang.String) 343 * @see #findResource(java.lang.String) 344 * @see #findResources(java.lang.String) 345 */ 346 public ClassLoader getClassLoader() 347 { 348 if ( !this.classLoaderSet ) 349 { 350 this.classLoader = this.getClass().getClassLoader(); 351 this.classLoaderSet = true; 352 } 353 354 return this.classLoader; 355 } 356 357 /** 358 * Gets the listeners of the context. 359 * <p> 360 * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 361 * to the returned list will be present inside the object. This is why there is no {@code set} method for the 362 * listeners property. 363 * </p> 364 * 365 * @return The list of listeners of the context. 366 * 367 * @see #log(java.util.logging.Level, java.lang.String, java.lang.Throwable) 368 */ 369 public List<Listener> getListeners() 370 { 371 return this.listeners; 372 } 373 374 /** 375 * Gets the default {@code http://jomc.org/modlet} namespace schema system id. 376 * <p> 377 * The default {@code http://jomc.org/modlet} namespace schema system id is controlled by system property 378 * {@code org.jomc.modlet.ModelContext.defaultModletSchemaSystemId} holding a system id URI. 379 * If that property is not set, the {@code http://xml.jomc.org/modlet/jomc-modlet-1.9.xsd} default is 380 * returned. 381 * </p> 382 * 383 * @return The default system id of the {@code http://jomc.org/modlet} namespace schema. 384 * 385 * @see #setDefaultModletSchemaSystemId(java.lang.String) 386 */ 387 public static String getDefaultModletSchemaSystemId() 388 { 389 if ( defaultModletSchemaSystemId == null ) 390 { 391 defaultModletSchemaSystemId = System.getProperty( 392 "org.jomc.modlet.ModelContext.defaultModletSchemaSystemId", DEFAULT_MODLET_SCHEMA_SYSTEM_ID ); 393 394 } 395 396 return defaultModletSchemaSystemId; 397 } 398 399 /** 400 * Sets the default {@code http://jomc.org/modlet} namespace schema system id. 401 * 402 * @param value The new default {@code http://jomc.org/modlet} namespace schema system id or {@code null}. 403 * 404 * @see #getDefaultModletSchemaSystemId() 405 */ 406 public static void setDefaultModletSchemaSystemId( final String value ) 407 { 408 defaultModletSchemaSystemId = value; 409 } 410 411 /** 412 * Gets the {@code http://jomc.org/modlet} namespace schema system id of the context. 413 * 414 * @return The {@code http://jomc.org/modlet} namespace schema system id of the context. 415 * 416 * @see #getDefaultModletSchemaSystemId() 417 * @see #setModletSchemaSystemId(java.lang.String) 418 */ 419 public final String getModletSchemaSystemId() 420 { 421 if ( this.modletSchemaSystemId == null ) 422 { 423 this.modletSchemaSystemId = getDefaultModletSchemaSystemId(); 424 425 if ( this.isLoggable( Level.CONFIG ) ) 426 { 427 this.log( Level.CONFIG, 428 getMessage( "defaultModletSchemaSystemIdInfo", this.modletSchemaSystemId ), null ); 429 430 } 431 } 432 433 return this.modletSchemaSystemId; 434 } 435 436 /** 437 * Sets the {@code http://jomc.org/modlet} namespace schema system id of the context. 438 * 439 * @param value The new {@code http://jomc.org/modlet} namespace schema system id or {@code null}. 440 * 441 * @see #getModletSchemaSystemId() 442 */ 443 public final void setModletSchemaSystemId( final String value ) 444 { 445 final String oldModletSchemaSystemId = this.getModletSchemaSystemId(); 446 this.modletSchemaSystemId = value; 447 448 if ( this.modlets != null ) 449 { 450 for ( int i = 0, s0 = this.modlets.getModlet().size(); i < s0; i++ ) 451 { 452 final Modlet m = this.modlets.getModlet().get( i ); 453 454 if ( m.getSchemas() != null ) 455 { 456 final Schema s = m.getSchemas().getSchemaBySystemId( oldModletSchemaSystemId ); 457 458 if ( s != null ) 459 { 460 s.setSystemId( value ); 461 } 462 } 463 } 464 } 465 } 466 467 /** 468 * Gets the default log level events are logged at. 469 * <p> 470 * The default log level is controlled by system property 471 * {@code org.jomc.modlet.ModelContext.defaultLogLevel} holding the log level to log events at by default. 472 * If that property is not set, the {@code WARNING} default is returned. 473 * </p> 474 * 475 * @return The log level events are logged at by default. 476 * 477 * @see #getLogLevel() 478 * @see Level#parse(java.lang.String) 479 */ 480 public static Level getDefaultLogLevel() 481 { 482 if ( defaultLogLevel == null ) 483 { 484 defaultLogLevel = Level.parse( System.getProperty( 485 "org.jomc.modlet.ModelContext.defaultLogLevel", DEFAULT_LOG_LEVEL.getName() ) ); 486 487 } 488 489 return defaultLogLevel; 490 } 491 492 /** 493 * Sets the default log level events are logged at. 494 * 495 * @param value The new default level events are logged at or {@code null}. 496 * 497 * @see #getDefaultLogLevel() 498 */ 499 public static void setDefaultLogLevel( final Level value ) 500 { 501 defaultLogLevel = value; 502 } 503 504 /** 505 * Gets the log level of the context. 506 * 507 * @return The log level of the context. 508 * 509 * @see #getDefaultLogLevel() 510 * @see #setLogLevel(java.util.logging.Level) 511 * @see #isLoggable(java.util.logging.Level) 512 */ 513 public final Level getLogLevel() 514 { 515 if ( this.logLevel == null ) 516 { 517 this.logLevel = getDefaultLogLevel(); 518 519 if ( this.isLoggable( Level.CONFIG ) ) 520 { 521 this.log( Level.CONFIG, getMessage( "defaultLogLevelInfo", this.logLevel.getLocalizedName() ), null ); 522 } 523 } 524 525 return this.logLevel; 526 } 527 528 /** 529 * Sets the log level of the context. 530 * 531 * @param value The new log level of the context or {@code null}. 532 * 533 * @see #getLogLevel() 534 * @see #isLoggable(java.util.logging.Level) 535 */ 536 public final void setLogLevel( final Level value ) 537 { 538 this.logLevel = value; 539 } 540 541 /** 542 * Checks if a message at a given level is provided to the listeners of the context. 543 * 544 * @param level The level to test. 545 * 546 * @return {@code true}, if messages at {@code level} are provided to the listeners of the context; {@code false}, 547 * if messages at {@code level} are not provided to the listeners of the context. 548 * 549 * @throws NullPointerException if {@code level} is {@code null}. 550 * 551 * @see #getLogLevel() 552 * @see #setLogLevel(java.util.logging.Level) 553 */ 554 public boolean isLoggable( final Level level ) 555 { 556 if ( level == null ) 557 { 558 throw new NullPointerException( "level" ); 559 } 560 561 return level.intValue() >= this.getLogLevel().intValue(); 562 } 563 564 /** 565 * Notifies all listeners of the context. 566 * 567 * @param level The level of the event. 568 * @param message The message of the event or {@code null}. 569 * @param throwable The throwable of the event {@code null}. 570 * 571 * @throws NullPointerException if {@code level} is {@code null}. 572 * 573 * @see #getListeners() 574 * @see #isLoggable(java.util.logging.Level) 575 */ 576 public void log( final Level level, final String message, final Throwable throwable ) 577 { 578 if ( level == null ) 579 { 580 throw new NullPointerException( "level" ); 581 } 582 583 if ( this.isLoggable( level ) ) 584 { 585 for ( final Listener l : this.getListeners() ) 586 { 587 l.onLog( level, message, throwable ); 588 } 589 } 590 } 591 592 /** 593 * Gets an {@code ExecutorService} used to run tasks in parallel. 594 * 595 * @return An {@code ExecutorService} used to run tasks in parallel or {@code null}, if no such service has been 596 * provided by an application. 597 * 598 * @since 1.10 599 * 600 * @see #setExecutorService(java.util.concurrent.ExecutorService) 601 */ 602 public final ExecutorService getExecutorService() 603 { 604 return this.executorService; 605 } 606 607 /** 608 * Sets the {@code ExecutorService} to be used to run tasks in parallel. 609 * <p> 610 * The {@code ExecutorService} to be used to run tasks in parallel is an optional entity. If no such service is 611 * provided by an application, no parallelization is performed. Configuration or lifecycle management of the given 612 * {@code ExecutorService} is the responsibility of the application. 613 * </p> 614 * 615 * @param value The {@code ExecutorService} to be used to run tasks in parallel or {@code null}, to disable any 616 * parallelization. 617 * 618 * @since 1.10 619 * 620 * @see #getExecutorService() 621 */ 622 public final void setExecutorService( final ExecutorService value ) 623 { 624 this.executorService = value; 625 626 if ( this.executorService != null ) 627 { 628 this.getModletSchemaSystemId(); 629 this.getLogLevel(); 630 631 if ( this instanceof DefaultModelContext ) 632 { 633 ( (DefaultModelContext) this ).getProviderLocation(); 634 ( (DefaultModelContext) this ).getPlatformProviderLocation(); 635 } 636 } 637 } 638 639 /** 640 * Gets the {@code Modlets} of the context. 641 * <p> 642 * If no {@code Modlets} have been set using the {@code setModlets} method, this method calls the 643 * {@code findModlets} method and the {@code processModlets} method to initialize the {@code Modlets} of the 644 * context. 645 * </p> 646 * <p> 647 * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make 648 * to the returned list will be present inside the object. 649 * </p> 650 * 651 * @return The {@code Modlets} of the context. 652 * 653 * @throws ModelException if getting the {@code Modlets} of the context fails. 654 * 655 * @see #setModlets(org.jomc.modlet.Modlets) 656 * @see #findModlets(org.jomc.modlet.Modlets) 657 * @see #processModlets(org.jomc.modlet.Modlets) 658 * @see #validateModlets(org.jomc.modlet.Modlets) 659 */ 660 public synchronized final Modlets getModlets() throws ModelException 661 { 662 if ( this.modlets == null ) 663 { 664 final Modlet modlet = new Modlet(); 665 modlet.setModel( ModletObject.MODEL_PUBLIC_ID ); 666 modlet.setName( getMessage( "projectName" ) ); 667 modlet.setVendor( getMessage( "projectVendor" ) ); 668 modlet.setVersion( getMessage( "projectVersion" ) ); 669 modlet.setSchemas( new Schemas() ); 670 671 final Schema schema = new Schema(); 672 schema.setPublicId( ModletObject.MODEL_PUBLIC_ID ); 673 schema.setSystemId( this.getModletSchemaSystemId() ); 674 schema.setContextId( ModletObject.class.getPackage().getName() ); 675 schema.setClasspathId( ModletObject.class.getPackage().getName().replace( '.', '/' ) 676 + "/jomc-modlet-1.9.xsd" ); 677 678 modlet.getSchemas().getSchema().add( schema ); 679 680 this.modlets = new Modlets(); 681 this.modlets.getModlet().add( modlet ); 682 683 long t0 = System.nanoTime(); 684 final Modlets provided = this.findModlets( this.modlets ); 685 686 if ( this.isLoggable( Level.FINE ) ) 687 { 688 this.log( Level.FINE, getMessage( "findModletsReport", 689 provided != null ? provided.getModlet().size() : 0, 690 System.nanoTime() - t0 ), null ); 691 692 } 693 694 if ( provided != null ) 695 { 696 this.modlets = provided; 697 } 698 699 t0 = System.nanoTime(); 700 final Modlets processed = this.processModlets( this.modlets ); 701 702 if ( this.isLoggable( Level.FINE ) ) 703 { 704 this.log( Level.FINE, getMessage( "processModletsReport", 705 processed != null ? processed.getModlet().size() : 0, 706 System.nanoTime() - t0 ), null ); 707 } 708 709 if ( processed != null ) 710 { 711 this.modlets = processed; 712 } 713 714 t0 = System.nanoTime(); 715 final ModelValidationReport report = this.validateModlets( this.modlets ); 716 717 if ( this.isLoggable( Level.FINE ) ) 718 { 719 this.log( Level.FINE, getMessage( "validateModletsReport", 720 this.modlets.getModlet().size(), 721 System.nanoTime() - t0 ), null ); 722 } 723 724 for ( final ModelValidationReport.Detail detail : report.getDetails() ) 725 { 726 if ( this.isLoggable( detail.getLevel() ) ) 727 { 728 this.log( detail.getLevel(), detail.getMessage(), null ); 729 } 730 } 731 732 if ( !report.isModelValid() ) 733 { 734 this.modlets = null; 735 throw new ModelException( getMessage( "invalidModlets" ) ); 736 } 737 } 738 739 return this.modlets; 740 } 741 742 /** 743 * Sets the {@code Modlets} of the context. 744 * 745 * @param value The new {@code Modlets} of the context or {@code null}. 746 * 747 * @see #getModlets() 748 */ 749 public final void setModlets( final Modlets value ) 750 { 751 this.modlets = value; 752 } 753 754 /** 755 * Searches the context for a class with a given name. 756 * 757 * @param name The name of the class to search. 758 * 759 * @return A class object of the class with name {@code name} or {@code null}, if no such class is found. 760 * 761 * @throws NullPointerException if {@code name} is {@code null}. 762 * @throws ModelException if searching fails. 763 * 764 * @see #getClassLoader() 765 */ 766 public Class<?> findClass( final String name ) throws ModelException 767 { 768 if ( name == null ) 769 { 770 throw new NullPointerException( "name" ); 771 } 772 773 try 774 { 775 return Class.forName( name, false, this.getClassLoader() ); 776 } 777 catch ( final ClassNotFoundException e ) 778 { 779 if ( this.isLoggable( Level.FINE ) ) 780 { 781 this.log( Level.FINE, getMessage( e ), e ); 782 } 783 784 return null; 785 } 786 } 787 788 /** 789 * Searches the context for a resource with a given name. 790 * 791 * @param name The name of the resource to search. 792 * 793 * @return An URL object for reading the resource or {@code null}, if no such resource is found. 794 * 795 * @throws NullPointerException if {@code name} is {@code null}. 796 * @throws ModelException if searching fails. 797 * 798 * @see #getClassLoader() 799 */ 800 public URL findResource( final String name ) throws ModelException 801 { 802 if ( name == null ) 803 { 804 throw new NullPointerException( "name" ); 805 } 806 807 final long t0 = System.nanoTime(); 808 final URL resource = this.getClassLoader() == null 809 ? ClassLoader.getSystemResource( name ) 810 : this.getClassLoader().getResource( name ); 811 812 if ( this.isLoggable( Level.FINE ) ) 813 { 814 this.log( Level.FINE, getMessage( "resourcesReport", name, System.nanoTime() - t0 ), null ); 815 } 816 817 return resource; 818 } 819 820 /** 821 * Searches the context for resources with a given name. 822 * 823 * @param name The name of the resources to search. 824 * 825 * @return An enumeration of URL objects for reading the resources. If no resources are found, the enumeration will 826 * be empty. 827 * 828 * @throws NullPointerException if {@code name} is {@code null}. 829 * @throws ModelException if searching fails. 830 * 831 * @see #getClassLoader() 832 */ 833 public Enumeration<URL> findResources( final String name ) throws ModelException 834 { 835 if ( name == null ) 836 { 837 throw new NullPointerException( "name" ); 838 } 839 840 try 841 { 842 final long t0 = System.nanoTime(); 843 final Enumeration<URL> resources = this.getClassLoader() == null 844 ? ClassLoader.getSystemResources( name ) 845 : this.getClassLoader().getResources( name ); 846 847 if ( this.isLoggable( Level.FINE ) ) 848 { 849 this.log( Level.FINE, getMessage( "resourcesReport", name, System.nanoTime() - t0 ), null ); 850 } 851 852 return resources; 853 } 854 catch ( final IOException e ) 855 { 856 throw new ModelException( getMessage( e ), e ); 857 } 858 } 859 860 /** 861 * Searches the context for {@code Modlets}. 862 * 863 * @return The {@code Modlets} found in the context or {@code null}. 864 * 865 * @throws ModelException if searching {@code Modlets} fails. 866 * 867 * @see ModletProvider META-INF/services/org.jomc.modlet.ModletProvider 868 * @see #getModlets() 869 * @deprecated As of JOMC 1.6, replaced by {@link #findModlets(org.jomc.modlet.Modlets)}. This method will be 870 * removed in JOMC 2.0. 871 */ 872 @Deprecated 873 public abstract Modlets findModlets() throws ModelException; 874 875 /** 876 * Searches the context for {@code Modlets}. 877 * 878 * @param modlets The {@code Modlets} currently being searched. 879 * 880 * @return The {@code Modlets} found in the context or {@code null}. 881 * 882 * @throws NullPointerException if {@code modlets} is {@code null}. 883 * @throws ModelException if searching {@code Modlets} fails. 884 * 885 * @see ModletProvider META-INF/services/org.jomc.modlet.ModletProvider 886 * @see #getModlets() 887 * @since 1.6 888 */ 889 public abstract Modlets findModlets( Modlets modlets ) throws ModelException; 890 891 /** 892 * Processes a list of {@code Modlet}s. 893 * 894 * @param modlets The {@code Modlets} currently being processed. 895 * 896 * @return The processed {@code Modlets} or {@code null}. 897 * 898 * @throws NullPointerException if {@code modlets} is {@code null}. 899 * @throws ModelException if processing {@code Modlets} fails. 900 * 901 * @see ModletProcessor META-INF/services/org.jomc.modlet.ModletProcessor 902 * @see #getModlets() 903 * @since 1.6 904 */ 905 public abstract Modlets processModlets( Modlets modlets ) throws ModelException; 906 907 /** 908 * Validates a list of {@code Modlet}s. 909 * 910 * @param modlets The {@code Modlets} to validate. 911 * 912 * @return Validation report. 913 * 914 * @throws NullPointerException if {@code modlets} is {@code null}. 915 * @throws ModelException if validating {@code modlets} fails. 916 * 917 * @see ModletValidator META-INF/services/org.jomc.modlet.ModletValidator 918 * @see #getModlets() 919 * @since 1.9 920 */ 921 public abstract ModelValidationReport validateModlets( Modlets modlets ) throws ModelException; 922 923 /** 924 * Creates a new {@code Model} instance. 925 * 926 * @param model The identifier of the {@code Model} to create. 927 * 928 * @return A new instance of the {@code Model} identified by {@code model}. 929 * 930 * @throws NullPointerException if {@code model} is {@code null}. 931 * @throws ModelException if creating a new {@code Model} instance fails. 932 * 933 * @see #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class) createServiceObjects( model, ModelProvider.class.getName(), ModelProvider.class ) 934 * @see ModletObject#MODEL_PUBLIC_ID 935 */ 936 public abstract Model findModel( String model ) throws ModelException; 937 938 /** 939 * Populates a given {@code Model} instance. 940 * 941 * @param model The {@code Model} to populate. 942 * 943 * @return The populated model. 944 * 945 * @throws NullPointerException if {@code model} is {@code null}. 946 * @throws ModelException if populating {@code model} fails. 947 * 948 * @see #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class) createServiceObjects( model, ModelProvider.class.getName(), ModelProvider.class ) 949 * 950 * @since 1.2 951 */ 952 public abstract Model findModel( Model model ) throws ModelException; 953 954 /** 955 * Gets the name of the class providing the default {@code ModelContext} implementation. 956 * <p> 957 * The name of the class providing the default {@code ModelContext} implementation returned by method 958 * {@link #createModelContext(java.lang.ClassLoader)} is controlled by system property 959 * {@code org.jomc.modlet.ModelContext.className}. If that property is not set, the name of the 960 * {@link org.jomc.modlet.DefaultModelContext} class is returned. 961 * </p> 962 * 963 * @return The name of the class providing the default {@code ModelContext} implementation. 964 * 965 * @see #setModelContextClassName(java.lang.String) 966 * 967 * @deprecated As of JOMC 1.2, replaced by class {@link ModelContextFactory}. This method will be removed in version 968 * 2.0. 969 */ 970 @Deprecated 971 public static String getModelContextClassName() 972 { 973 if ( modelContextClassName == null ) 974 { 975 modelContextClassName = System.getProperty( "org.jomc.modlet.ModelContext.className", 976 DefaultModelContext.class.getName() ); 977 978 } 979 980 return modelContextClassName; 981 } 982 983 /** 984 * Sets the name of the class providing the default {@code ModelContext} implementation. 985 * 986 * @param value The new name of the class providing the default {@code ModelContext} implementation or {@code null}. 987 * 988 * @see #getModelContextClassName() 989 * 990 * @deprecated As of JOMC 1.2, replaced by class {@link ModelContextFactory}. This method will be removed in version 991 * 2.0. 992 */ 993 @Deprecated 994 public static void setModelContextClassName( final String value ) 995 { 996 modelContextClassName = value; 997 } 998 999 /** 1000 * Creates a new default {@code ModelContext} instance. 1001 * 1002 * @param classLoader The class loader to create a new default {@code ModelContext} instance with or {@code null}, 1003 * to create a new context using the platform's bootstrap class loader. 1004 * 1005 * @return A new {@code ModelContext} instance. 1006 * 1007 * @throws ModelException if creating a new {@code ModelContext} instance fails. 1008 * 1009 * @see #getModelContextClassName() 1010 * 1011 * @deprecated As of JOMC 1.2, replaced by method {@link ModelContextFactory#newModelContext(java.lang.ClassLoader)}. 1012 * This method will be removed in version 2.0. 1013 */ 1014 public static ModelContext createModelContext( final ClassLoader classLoader ) throws ModelException 1015 { 1016 if ( getModelContextClassName().equals( DefaultModelContext.class.getName() ) ) 1017 { 1018 return new DefaultModelContext( classLoader ); 1019 } 1020 1021 try 1022 { 1023 final Class<?> clazz = Class.forName( getModelContextClassName(), false, classLoader ); 1024 1025 if ( !ModelContext.class.isAssignableFrom( clazz ) ) 1026 { 1027 throw new ModelException( getMessage( "illegalContextImplementation", getModelContextClassName(), 1028 ModelContext.class.getName() ) ); 1029 1030 } 1031 1032 final Constructor<? extends ModelContext> ctor = 1033 clazz.asSubclass( ModelContext.class ).getDeclaredConstructor( ClassLoader.class ); 1034 1035 return ctor.newInstance( classLoader ); 1036 } 1037 catch ( final ClassNotFoundException e ) 1038 { 1039 throw new ModelException( getMessage( "contextClassNotFound", getModelContextClassName() ), e ); 1040 } 1041 catch ( final NoSuchMethodException e ) 1042 { 1043 throw new ModelException( getMessage( "contextConstructorNotFound", getModelContextClassName() ), e ); 1044 } 1045 catch ( final InstantiationException e ) 1046 { 1047 final String message = getMessage( e ); 1048 throw new ModelException( getMessage( "contextInstantiationException", getModelContextClassName(), 1049 message != null ? " " + message : "" ), e ); 1050 1051 } 1052 catch ( final IllegalAccessException e ) 1053 { 1054 final String message = getMessage( e ); 1055 throw new ModelException( getMessage( "contextConstructorAccessDenied", getModelContextClassName(), 1056 message != null ? " " + message : "" ), e ); 1057 1058 } 1059 catch ( final InvocationTargetException e ) 1060 { 1061 String message = getMessage( e ); 1062 if ( message == null && e.getTargetException() != null ) 1063 { 1064 message = getMessage( e.getTargetException() ); 1065 } 1066 1067 throw new ModelException( getMessage( "contextConstructorException", getModelContextClassName(), 1068 message != null ? " " + message : "" ), e ); 1069 1070 } 1071 } 1072 1073 /** 1074 * Processes a {@code Model}. 1075 * 1076 * @param model The {@code Model} to process. 1077 * 1078 * @return The processed {@code Model}. 1079 * 1080 * @throws NullPointerException if {@code model} is {@code null}. 1081 * @throws ModelException if processing {@code model} fails. 1082 * 1083 * @see #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class) createServiceObjects( model, ModelProcessor.class.getName(), ModelProcessor.class ) 1084 */ 1085 public abstract Model processModel( Model model ) throws ModelException; 1086 1087 /** 1088 * Validates a given {@code Model}. 1089 * 1090 * @param model The {@code Model} to validate. 1091 * 1092 * @return Validation report. 1093 * 1094 * @throws NullPointerException if {@code model} is {@code null}. 1095 * @throws ModelException if validating {@code model} fails. 1096 * 1097 * @see #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class) createServiceObjects( model, ModelValidator.class.getName(), ModelValidator.class ) 1098 * @see ModelValidationReport#isModelValid() 1099 */ 1100 public abstract ModelValidationReport validateModel( Model model ) throws ModelException; 1101 1102 /** 1103 * Validates a given model. 1104 * 1105 * @param model The identifier of the {@code Model} to use for validating {@code source}. 1106 * @param source A source providing the model to validate. 1107 * 1108 * @return Validation report. 1109 * 1110 * @throws NullPointerException if {@code model} or {@code source} is {@code null}. 1111 * @throws ModelException if validating the model fails. 1112 * 1113 * @see #createSchema(java.lang.String) 1114 * @see ModelValidationReport#isModelValid() 1115 * @see ModletObject#MODEL_PUBLIC_ID 1116 */ 1117 public abstract ModelValidationReport validateModel( String model, Source source ) throws ModelException; 1118 1119 /** 1120 * Creates a new SAX entity resolver instance of a given model. 1121 * 1122 * @param model The identifier of the model to create a new SAX entity resolver of. 1123 * 1124 * @return A new SAX entity resolver instance of the model identified by {@code model}. 1125 * 1126 * @throws NullPointerException if {@code model} is {@code null}. 1127 * @throws ModelException if creating a new SAX entity resolver instance fails. 1128 * 1129 * @see ModletObject#MODEL_PUBLIC_ID 1130 */ 1131 public abstract EntityResolver createEntityResolver( String model ) throws ModelException; 1132 1133 /** 1134 * Creates a new SAX entity resolver instance for a given public identifier URI. 1135 * 1136 * @param publicId The public identifier URI to create a new SAX entity resolver for. 1137 * 1138 * @return A new SAX entity resolver instance for the public identifier URI {@code publicId}. 1139 * 1140 * @throws NullPointerException if {@code publicId} is {@code null}. 1141 * @throws ModelException if creating a new SAX entity resolver instance fails. 1142 * 1143 * @see ModletObject#PUBLIC_ID 1144 * @since 1.2 1145 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0. 1146 */ 1147 @Deprecated 1148 public abstract EntityResolver createEntityResolver( URI publicId ) throws ModelException; 1149 1150 /** 1151 * Creates a new L/S resource resolver instance of a given model. 1152 * 1153 * @param model The identifier of the model to create a new L/S resource resolver of. 1154 * 1155 * @return A new L/S resource resolver instance of the model identified by {@code model}. 1156 * 1157 * @throws NullPointerException if {@code model} is {@code null}. 1158 * @throws ModelException if creating a new L/S resource resolver instance fails. 1159 * 1160 * @see ModletObject#MODEL_PUBLIC_ID 1161 */ 1162 public abstract LSResourceResolver createResourceResolver( String model ) throws ModelException; 1163 1164 /** 1165 * Creates a new L/S resource resolver instance for a given public identifier URI. 1166 * 1167 * @param publicId The public identifier URI to create a new L/S resource resolver for. 1168 * 1169 * @return A new L/S resource resolver instance for the public identifier URI {@code publicId}. 1170 * 1171 * @throws NullPointerException if {@code publicId} is {@code null}. 1172 * @throws ModelException if creating a new L/S resource resolver instance fails. 1173 * 1174 * @see ModletObject#PUBLIC_ID 1175 * @since 1.2 1176 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0. 1177 */ 1178 @Deprecated 1179 public abstract LSResourceResolver createResourceResolver( URI publicId ) throws ModelException; 1180 1181 /** 1182 * Creates a new JAXP schema instance of a given model. 1183 * 1184 * @param model The identifier of the model to create a new JAXP schema instance of. 1185 * 1186 * @return A new JAXP schema instance of the model identified by {@code model}. 1187 * 1188 * @throws NullPointerException if {@code model} is {@code null}. 1189 * @throws ModelException if creating a new JAXP schema instance fails. 1190 * 1191 * @see ModletObject#MODEL_PUBLIC_ID 1192 */ 1193 public abstract javax.xml.validation.Schema createSchema( String model ) throws ModelException; 1194 1195 /** 1196 * Creates a new JAXP schema instance for a given public identifier URI. 1197 * 1198 * @param publicId The public identifier URI to create a new JAXP schema instance for. 1199 * 1200 * @return A new JAXP schema instance for the public identifier URI {@code publicId}. 1201 * 1202 * @throws NullPointerException if {@code publicId} is {@code null}. 1203 * @throws ModelException if creating a new JAXP schema instance fails. 1204 * 1205 * @see ModletObject#PUBLIC_ID 1206 * @since 1.2 1207 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0. 1208 */ 1209 @Deprecated 1210 public abstract javax.xml.validation.Schema createSchema( URI publicId ) throws ModelException; 1211 1212 /** 1213 * Creates a new JAXB context instance of a given model. 1214 * 1215 * @param model The identifier of the model to create a new JAXB context instance of. 1216 * 1217 * @return A new JAXB context instance of the model identified by {@code model}. 1218 * 1219 * @throws NullPointerException if {@code model} is {@code null}. 1220 * @throws ModelException if creating a new JAXB context instance fails. 1221 * 1222 * @see ModletObject#MODEL_PUBLIC_ID 1223 */ 1224 public abstract JAXBContext createContext( String model ) throws ModelException; 1225 1226 /** 1227 * Creates a new JAXB context instance for a given public identifier URI. 1228 * 1229 * @param publicId The public identifier URI to create a new JAXB context instance for. 1230 * 1231 * @return A new JAXB context instance for the public identifier URI {@code publicId}. 1232 * 1233 * @throws NullPointerException if {@code publicId} is {@code null}. 1234 * @throws ModelException if creating a new JAXB context instance fails. 1235 * 1236 * @see ModletObject#PUBLIC_ID 1237 * @since 1.2 1238 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0. 1239 */ 1240 @Deprecated 1241 public abstract JAXBContext createContext( URI publicId ) throws ModelException; 1242 1243 /** 1244 * Creates a new JAXB marshaller instance of a given model. 1245 * 1246 * @param model The identifier of the model to create a new JAXB marshaller instance of. 1247 * 1248 * @return A new JAXB marshaller instance of the model identified by {@code model}. 1249 * 1250 * @throws NullPointerException if {@code model} is {@code null}. 1251 * @throws ModelException if creating a new JAXB marshaller instance fails. 1252 * 1253 * @see ModletObject#MODEL_PUBLIC_ID 1254 */ 1255 public abstract Marshaller createMarshaller( String model ) throws ModelException; 1256 1257 /** 1258 * Creates a new JAXB marshaller instance for a given public identifier URI. 1259 * 1260 * @param publicId The public identifier URI to create a new JAXB marshaller instance for. 1261 * 1262 * @return A new JAXB marshaller instance for the public identifier URI {@code publicId}. 1263 * 1264 * @throws NullPointerException if {@code publicId} is {@code null}. 1265 * @throws ModelException if creating a new JAXB marshaller instance fails. 1266 * 1267 * @see ModletObject#PUBLIC_ID 1268 * @since 1.2 1269 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0. 1270 */ 1271 @Deprecated 1272 public abstract Marshaller createMarshaller( URI publicId ) throws ModelException; 1273 1274 /** 1275 * Creates a new JAXB unmarshaller instance of a given model. 1276 * 1277 * @param model The identifier of the model to create a new JAXB unmarshaller instance of. 1278 * 1279 * @return A new JAXB unmarshaller instance of the model identified by {@code model}. 1280 * 1281 * @throws NullPointerException if {@code model} is {@code null}. 1282 * @throws ModelException if creating a new JAXB unmarshaller instance fails. 1283 * 1284 * @see ModletObject#MODEL_PUBLIC_ID 1285 */ 1286 public abstract Unmarshaller createUnmarshaller( String model ) throws ModelException; 1287 1288 /** 1289 * Creates a new JAXB unmarshaller instance for a given given public identifier URI. 1290 * 1291 * @param publicId The public identifier URI to create a new JAXB unmarshaller instance for. 1292 * 1293 * @return A new JAXB unmarshaller instance for the public identifier URI {@code publicId}. 1294 * 1295 * @throws NullPointerException if {@code publicId} is {@code null}. 1296 * @throws ModelException if creating a new JAXB unmarshaller instance fails. 1297 * 1298 * @see ModletObject#PUBLIC_ID 1299 * @since 1.2 1300 * @deprecated As of JOMC 1.8, removed without replacement. This method will be removed in JOMC 2.0. 1301 */ 1302 @Deprecated 1303 public abstract Unmarshaller createUnmarshaller( URI publicId ) throws ModelException; 1304 1305 /** 1306 * Creates service objects of a model. 1307 * 1308 * @param <T> The type of the service. 1309 * @param model The identifier of the {@code Model} to create service objects of. 1310 * @param service The identifier of the service to create objects of. 1311 * @param type The class of the type of the service. 1312 * 1313 * @return An ordered, unmodifiable collection of new service objects identified by {@code service} of the model 1314 * identified by {@code model}. 1315 * 1316 * @throws NullPointerException if {@code model}, {@code service} or {@code type} is {@code null}. 1317 * @throws ModelException if creating service objects fails. 1318 * 1319 * @see ModelProvider 1320 * @see ModelProcessor 1321 * @see ModelValidator 1322 * 1323 * @since 1.9 1324 */ 1325 public abstract <T> Collection<? extends T> createServiceObjects( final String model, final String service, 1326 final Class<T> type ) 1327 throws ModelException; 1328 1329 /** 1330 * Creates a new service object. 1331 * 1332 * @param <T> The type of the service. 1333 * @param service The service to create a new object of. 1334 * @param type The class of the type of the service. 1335 * 1336 * @return An new service object for {@code service}. 1337 * 1338 * @throws NullPointerException if {@code service} or {@code type} is {@code null}. 1339 * @throws ModelException if creating the service object fails. 1340 * 1341 * @see ModletProvider 1342 * @see ModletProcessor 1343 * @see ModletValidator 1344 * @see ServiceFactory 1345 * 1346 * @since 1.2 1347 * @deprecated As of JOMC 1.9, please use method {@link #createServiceObjects(java.lang.String, java.lang.String, java.lang.Class)}. 1348 * This method will be removed in JOMC 2.0. 1349 */ 1350 @Deprecated 1351 public abstract <T> T createServiceObject( final Service service, final Class<T> type ) throws ModelException; 1352 1353 private static String getMessage( final String key, final Object... args ) 1354 { 1355 return MessageFormat.format( ResourceBundle.getBundle( ModelContext.class.getName(), Locale.getDefault() ). 1356 getString( key ), args ); 1357 1358 } 1359 1360 private static String getMessage( final Throwable t ) 1361 { 1362 return t != null 1363 ? t.getMessage() != null && t.getMessage().trim().length() > 0 1364 ? t.getMessage() 1365 : getMessage( t.getCause() ) 1366 : null; 1367 1368 } 1369 1370}