001/* 002 * Copyright (C) Christian Schulte <cs@schulte.it>, 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: DefaultModletProvider.java 5043 2015-05-27 07:03:39Z schulte $ 029 * 030 */ 031package org.jomc.modlet; 032 033import java.net.URL; 034import java.text.MessageFormat; 035import java.util.Enumeration; 036import java.util.ResourceBundle; 037import java.util.logging.Level; 038import javax.xml.bind.JAXBContext; 039import javax.xml.bind.JAXBElement; 040import javax.xml.bind.JAXBException; 041import javax.xml.bind.UnmarshalException; 042import javax.xml.bind.Unmarshaller; 043 044/** 045 * Default {@code ModletProvider} implementation. 046 * 047 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a> 048 * @version $JOMC: DefaultModletProvider.java 5043 2015-05-27 07:03:39Z schulte $ 049 * @see ModelContext#findModlets(org.jomc.modlet.Modlets) 050 */ 051public class DefaultModletProvider implements ModletProvider 052{ 053 054 /** 055 * Constant for the name of the model context attribute backing property {@code enabled}. 056 * 057 * @see #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets) 058 * @see ModelContext#getAttribute(java.lang.String) 059 * @since 1.2 060 */ 061 public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.modlet.DefaultModletProvider.enabledAttribute"; 062 063 /** 064 * Constant for the name of the system property controlling property {@code defaultEnabled}. 065 * 066 * @see #isDefaultEnabled() 067 * @since 1.2 068 */ 069 private static final String DEFAULT_ENABLED_PROPERTY_NAME = 070 "org.jomc.modlet.DefaultModletProvider.defaultEnabled"; 071 072 /** 073 * Default value of the flag indicating the provider is enabled by default. 074 * 075 * @see #isDefaultEnabled() 076 * @since 1.2 077 */ 078 private static final Boolean DEFAULT_ENABLED = Boolean.TRUE; 079 080 /** 081 * Flag indicating the provider is enabled by default. 082 */ 083 private static volatile Boolean defaultEnabled; 084 085 /** 086 * Flag indicating the provider is enabled. 087 */ 088 private Boolean enabled; 089 090 /** 091 * Constant for the name of the model context attribute backing property {@code modletLocation}. 092 * 093 * @see #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets) 094 * @see ModelContext#getAttribute(java.lang.String) 095 * @since 1.2 096 */ 097 public static final String MODLET_LOCATION_ATTRIBUTE_NAME = 098 "org.jomc.modlet.DefaultModletProvider.modletLocationAttribute"; 099 100 /** 101 * Constant for the name of the system property controlling property {@code defaultModletLocation}. 102 * 103 * @see #getDefaultModletLocation() 104 * @since 1.2 105 */ 106 private static final String DEFAULT_MODLET_LOCATION_PROPERTY_NAME = 107 "org.jomc.modlet.DefaultModletProvider.defaultModletLocation"; 108 109 /** 110 * Class path location searched for {@code Modlets} by default. 111 * 112 * @see #getDefaultModletLocation() 113 */ 114 private static final String DEFAULT_MODLET_LOCATION = "META-INF/jomc-modlet.xml"; 115 116 /** 117 * Default {@code Modlet} location. 118 */ 119 private static volatile String defaultModletLocation; 120 121 /** 122 * Modlet location of the instance. 123 */ 124 private String modletLocation; 125 126 /** 127 * Constant for the name of the model context attribute backing property {@code validating}. 128 * 129 * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String) 130 * @see ModelContext#getAttribute(java.lang.String) 131 * @since 1.2 132 */ 133 public static final String VALIDATING_ATTRIBUTE_NAME = 134 "org.jomc.modlet.DefaultModletProvider.validatingAttribute"; 135 136 /** 137 * Constant for the name of the system property controlling property {@code defaultValidating}. 138 * 139 * @see #isDefaultValidating() 140 * @since 1.2 141 */ 142 private static final String DEFAULT_VALIDATING_PROPERTY_NAME = 143 "org.jomc.modlet.DefaultModletProvider.defaultValidating"; 144 145 /** 146 * Default value of the flag indicating the provider is validating resources by default. 147 * 148 * @see #isDefaultValidating() 149 * @since 1.2 150 */ 151 private static final Boolean DEFAULT_VALIDATING = Boolean.TRUE; 152 153 /** 154 * Flag indicating the provider is validating resources by default. 155 * 156 * @since 1.2 157 */ 158 private static volatile Boolean defaultValidating; 159 160 /** 161 * Flag indicating the provider is validating resources. 162 * 163 * @since 1.2 164 */ 165 private Boolean validating; 166 167 /** 168 * Constant for the name of the system property controlling property {@code defaultOrdinal}. 169 * 170 * @see #getDefaultOrdinal() 171 * @since 1.6 172 */ 173 private static final String DEFAULT_ORDINAL_PROPERTY_NAME = 174 "org.jomc.modlet.DefaultModletProvider.defaultOrdinal"; 175 176 /** 177 * Default value of the ordinal number of the provider. 178 * 179 * @see #getDefaultOrdinal() 180 * @since 1.6 181 */ 182 private static final Integer DEFAULT_ORDINAL = 0; 183 184 /** 185 * Default ordinal number of the provider. 186 * 187 * @since 1.6 188 */ 189 private static volatile Integer defaultOrdinal; 190 191 /** 192 * Ordinal number of the provider. 193 * 194 * @since 1.6 195 */ 196 private Integer ordinal; 197 198 /** 199 * Creates a new {@code DefaultModletProvider} instance. 200 */ 201 public DefaultModletProvider() 202 { 203 super(); 204 } 205 206 /** 207 * Gets a flag indicating the provider is enabled by default. 208 * <p> 209 * The default enabled flag is controlled by system property 210 * {@code org.jomc.modlet.DefaultModletProvider.defaultEnabled} holding a value indicating the provider is 211 * enabled by default. If that property is not set, the {@code true} default is returned. 212 * </p> 213 * 214 * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by 215 * default. 216 * 217 * @see #isEnabled() 218 * @see #setDefaultEnabled(java.lang.Boolean) 219 */ 220 public static boolean isDefaultEnabled() 221 { 222 if ( defaultEnabled == null ) 223 { 224 defaultEnabled = Boolean.valueOf( System.getProperty( 225 DEFAULT_ENABLED_PROPERTY_NAME, Boolean.toString( DEFAULT_ENABLED ) ) ); 226 227 } 228 229 return defaultEnabled; 230 } 231 232 /** 233 * Sets the flag indicating the provider is enabled by default. 234 * 235 * @param value The new value of the flag indicating the provider is enabled by default or {@code null}. 236 * 237 * @see #isDefaultEnabled() 238 */ 239 public static void setDefaultEnabled( final Boolean value ) 240 { 241 defaultEnabled = value; 242 } 243 244 /** 245 * Gets a flag indicating the provider is enabled. 246 * 247 * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled. 248 * 249 * @see #isDefaultEnabled() 250 * @see #setEnabled(java.lang.Boolean) 251 */ 252 public final boolean isEnabled() 253 { 254 if ( this.enabled == null ) 255 { 256 this.enabled = isDefaultEnabled(); 257 } 258 259 return this.enabled; 260 } 261 262 /** 263 * Sets the flag indicating the provider is enabled. 264 * 265 * @param value The new value of the flag indicating the provider is enabled or {@code null}. 266 * 267 * @see #isEnabled() 268 */ 269 public final void setEnabled( final Boolean value ) 270 { 271 this.enabled = value; 272 } 273 274 /** 275 * Gets the default location searched for {@code Modlet} resources. 276 * <p> 277 * The default {@code Modlet} location is controlled by system property 278 * {@code org.jomc.modlet.DefaultModletProvider.defaultModletLocation} holding the location to search for 279 * {@code Modlet} resources by default. If that property is not set, the {@code META-INF/jomc-modlet.xml} default is 280 * returned. 281 * </p> 282 * 283 * @return The location searched for {@code Modlet} resources by default. 284 * 285 * @see #setDefaultModletLocation(java.lang.String) 286 */ 287 public static String getDefaultModletLocation() 288 { 289 if ( defaultModletLocation == null ) 290 { 291 defaultModletLocation = System.getProperty( 292 DEFAULT_MODLET_LOCATION_PROPERTY_NAME, DEFAULT_MODLET_LOCATION ); 293 294 } 295 296 return defaultModletLocation; 297 } 298 299 /** 300 * Sets the default location searched for {@code Modlet} resources. 301 * 302 * @param value The new default location to search for {@code Modlet} resources or {@code null}. 303 * 304 * @see #getDefaultModletLocation() 305 */ 306 public static void setDefaultModletLocation( final String value ) 307 { 308 defaultModletLocation = value; 309 } 310 311 /** 312 * Gets the location searched for {@code Modlet} resources. 313 * 314 * @return The location searched for {@code Modlet} resources. 315 * 316 * @see #getDefaultModletLocation() 317 * @see #setModletLocation(java.lang.String) 318 */ 319 public final String getModletLocation() 320 { 321 if ( this.modletLocation == null ) 322 { 323 this.modletLocation = getDefaultModletLocation(); 324 } 325 326 return this.modletLocation; 327 } 328 329 /** 330 * Sets the location searched for {@code Modlet} resources. 331 * 332 * @param value The new location to search for {@code Modlet} resources or {@code null}. 333 * 334 * @see #getModletLocation() 335 */ 336 public final void setModletLocation( final String value ) 337 { 338 this.modletLocation = value; 339 } 340 341 /** 342 * Gets a flag indicating the provider is validating resources by default. 343 * <p> 344 * The default validating flag is controlled by system property 345 * {@code org.jomc.modlet.DefaultModletProvider.defaultValidating} holding a value indicating the provider is 346 * validating resources by default. If that property is not set, the {@code true} default is returned. 347 * </p> 348 * 349 * @return {@code true}, if the provider is validating resources by default; {@code false}, if the provider is not 350 * validating resources by default. 351 * 352 * @see #isValidating() 353 * @see #setDefaultValidating(java.lang.Boolean) 354 * 355 * @since 1.2 356 */ 357 public static boolean isDefaultValidating() 358 { 359 if ( defaultValidating == null ) 360 { 361 defaultValidating = Boolean.valueOf( System.getProperty( 362 DEFAULT_VALIDATING_PROPERTY_NAME, Boolean.toString( DEFAULT_VALIDATING ) ) ); 363 364 } 365 366 return defaultValidating; 367 } 368 369 /** 370 * Sets the flag indicating the provider is validating resources by default. 371 * 372 * @param value The new value of the flag indicating the provider is validating resources by default or 373 * {@code null}. 374 * 375 * @see #isDefaultValidating() 376 * 377 * @since 1.2 378 */ 379 public static void setDefaultValidating( final Boolean value ) 380 { 381 defaultValidating = value; 382 } 383 384 /** 385 * Gets a flag indicating the provider is validating resources. 386 * 387 * @return {@code true}, if the provider is validating resources; {@code false}, if the provider is not validating 388 * resources. 389 * 390 * @see #isDefaultValidating() 391 * @see #setValidating(java.lang.Boolean) 392 * 393 * @since 1.2 394 */ 395 public final boolean isValidating() 396 { 397 if ( this.validating == null ) 398 { 399 this.validating = isDefaultValidating(); 400 } 401 402 return this.validating; 403 } 404 405 /** 406 * Sets the flag indicating the provider is validating resources. 407 * 408 * @param value The new value of the flag indicating the provider is validating resources or {@code null}. 409 * 410 * @see #isValidating() 411 * 412 * @since 1.2 413 */ 414 public final void setValidating( final Boolean value ) 415 { 416 this.validating = value; 417 } 418 419 /** 420 * Gets the default ordinal number of the provider. 421 * <p> 422 * The default ordinal number is controlled by system property 423 * {@code org.jomc.modlet.DefaultModletProvider.defaultOrdinal} holding the default ordinal number of the provider. 424 * If that property is not set, the {@code 0} default is returned. 425 * </p> 426 * 427 * @return The default ordinal number of the provider. 428 * 429 * @see #setDefaultOrdinal(java.lang.Integer) 430 * 431 * @since 1.6 432 */ 433 public static int getDefaultOrdinal() 434 { 435 if ( defaultOrdinal == null ) 436 { 437 defaultOrdinal = Integer.getInteger( DEFAULT_ORDINAL_PROPERTY_NAME, DEFAULT_ORDINAL ); 438 } 439 440 return defaultOrdinal; 441 } 442 443 /** 444 * Sets the default ordinal number of the provider. 445 * 446 * @param value The new default ordinal number of the provider or {@code null}. 447 * 448 * @see #getDefaultOrdinal() 449 * 450 * @since 1.6 451 */ 452 public static void setDefaultOrdinal( final Integer value ) 453 { 454 defaultOrdinal = value; 455 } 456 457 /** 458 * Gets the ordinal number of the provider. 459 * 460 * @return The ordinal number of the provider. 461 * 462 * @see #getDefaultOrdinal() 463 * @see #setOrdinal(java.lang.Integer) 464 * 465 * @since 1.6 466 */ 467 public final int getOrdinal() 468 { 469 if ( this.ordinal == null ) 470 { 471 this.ordinal = getDefaultOrdinal(); 472 } 473 474 return this.ordinal; 475 } 476 477 /** 478 * Sets the ordinal number of the provider. 479 * 480 * @param value The new ordinal number of the provider or {@code null}. 481 * 482 * @see #getOrdinal() 483 * 484 * @since 1.6 485 */ 486 public final void setOrdinal( final Integer value ) 487 { 488 this.ordinal = value; 489 } 490 491 /** 492 * Searches a given context for {@code Modlets}. 493 * 494 * @param context The context to search for {@code Modlets}. 495 * @param location The location to search at. 496 * 497 * @return The {@code Modlets} found at {@code location} in {@code context} or {@code null}, if no {@code Modlets} 498 * are found. 499 * 500 * @throws NullPointerException if {@code context} or {@code location} is {@code null}. 501 * @throws ModelException if searching the context fails. 502 * 503 * @see #isValidating() 504 * @see #VALIDATING_ATTRIBUTE_NAME 505 */ 506 public Modlets findModlets( final ModelContext context, final String location ) throws ModelException 507 { 508 if ( context == null ) 509 { 510 throw new NullPointerException( "context" ); 511 } 512 if ( location == null ) 513 { 514 throw new NullPointerException( "location" ); 515 } 516 517 URL url = null; 518 519 try 520 { 521 boolean contextValidating = this.isValidating(); 522 if ( DEFAULT_VALIDATING == contextValidating 523 && context.getAttribute( VALIDATING_ATTRIBUTE_NAME ) instanceof Boolean ) 524 { 525 contextValidating = (Boolean) context.getAttribute( VALIDATING_ATTRIBUTE_NAME ); 526 } 527 528 Modlets modlets = null; 529 final long t0 = System.currentTimeMillis(); 530 final JAXBContext ctx = context.createContext( ModletObject.MODEL_PUBLIC_ID ); 531 final Unmarshaller u = ctx.createUnmarshaller(); 532 final Enumeration<URL> e = context.findResources( location ); 533 534 if ( contextValidating ) 535 { 536 u.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) ); 537 } 538 539 while ( e.hasMoreElements() ) 540 { 541 url = e.nextElement(); 542 Object content = u.unmarshal( url ); 543 if ( content instanceof JAXBElement<?> ) 544 { 545 content = ( (JAXBElement<?>) content ).getValue(); 546 } 547 548 if ( content instanceof Modlet ) 549 { 550 if ( modlets == null ) 551 { 552 modlets = new Modlets(); 553 } 554 555 modlets.getModlet().add( (Modlet) content ); 556 } 557 else if ( content instanceof Modlets ) 558 { 559 if ( modlets == null ) 560 { 561 modlets = new Modlets(); 562 } 563 564 modlets.getModlet().addAll( ( (Modlets) content ).getModlet() ); 565 } 566 } 567 568 if ( context.isLoggable( Level.FINE ) ) 569 { 570 context.log( Level.FINE, getMessage( "contextReport", 571 modlets != null ? modlets.getModlet().size() : 0, 572 location, System.currentTimeMillis() - t0 ), null ); 573 574 } 575 576 return modlets == null || modlets.getModlet().isEmpty() ? null : modlets; 577 } 578 catch ( final UnmarshalException e ) 579 { 580 String message = getMessage( e ); 581 if ( message == null && e.getLinkedException() != null ) 582 { 583 message = getMessage( e.getLinkedException() ); 584 } 585 586 if ( url != null ) 587 { 588 message = getMessage( "unmarshalException", url.toExternalForm(), 589 message != null ? " " + message : "" ); 590 591 } 592 593 throw new ModelException( message, e ); 594 } 595 catch ( final JAXBException e ) 596 { 597 String message = getMessage( e ); 598 if ( message == null && e.getLinkedException() != null ) 599 { 600 message = getMessage( e.getLinkedException() ); 601 } 602 603 throw new ModelException( message, e ); 604 } 605 } 606 607 /** 608 * {@inheritDoc} 609 * 610 * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider 611 * is disabled. 612 * 613 * @see #isEnabled() 614 * @see #getModletLocation() 615 * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String) 616 * @see #ENABLED_ATTRIBUTE_NAME 617 * @see #MODLET_LOCATION_ATTRIBUTE_NAME 618 * @deprecated As of JOMC 1.6, this method has been replaced by {@link #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)}. 619 * This method will be removed in JOMC 2.0. 620 */ 621 @Deprecated 622 public Modlets findModlets( final ModelContext context ) throws ModelException 623 { 624 if ( context == null ) 625 { 626 throw new NullPointerException( "context" ); 627 } 628 629 return this.findModlets( context, new Modlets() ); 630 } 631 632 /** 633 * {@inheritDoc} 634 * 635 * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider 636 * is disabled. 637 * 638 * @see #isEnabled() 639 * @see #getModletLocation() 640 * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String) 641 * @see #ENABLED_ATTRIBUTE_NAME 642 * @see #MODLET_LOCATION_ATTRIBUTE_NAME 643 * @since 1.6 644 */ 645 public Modlets findModlets( final ModelContext context, final Modlets modlets ) throws ModelException 646 { 647 if ( context == null ) 648 { 649 throw new NullPointerException( "context" ); 650 } 651 if ( modlets == null ) 652 { 653 throw new NullPointerException( "context" ); 654 } 655 656 Modlets provided = null; 657 658 boolean contextEnabled = this.isEnabled(); 659 if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean ) 660 { 661 contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME ); 662 } 663 664 String contextModletLocation = this.getModletLocation(); 665 if ( DEFAULT_MODLET_LOCATION.equals( contextModletLocation ) 666 && context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME ) instanceof String ) 667 { 668 contextModletLocation = (String) context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME ); 669 } 670 671 if ( contextEnabled ) 672 { 673 final Modlets found = this.findModlets( context, contextModletLocation ); 674 675 if ( found != null ) 676 { 677 provided = modlets.clone(); 678 provided.getModlet().addAll( found.getModlet() ); 679 } 680 } 681 else if ( context.isLoggable( Level.FINER ) ) 682 { 683 context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName() ), null ); 684 } 685 686 return provided; 687 } 688 689 private static String getMessage( final String key, final Object... arguments ) 690 { 691 return MessageFormat.format( ResourceBundle.getBundle( 692 DefaultModletProvider.class.getName().replace( '.', '/' ) ).getString( key ), arguments ); 693 694 } 695 696 private static String getMessage( final Throwable t ) 697 { 698 return t != null 699 ? t.getMessage() != null && t.getMessage().trim().length() > 0 700 ? t.getMessage() 701 : getMessage( t.getCause() ) 702 : null; 703 704 } 705 706}