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