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