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: DefaultModletProvider.java 4613 2012-09-22 10:07:08Z 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 4613 2012-09-22 10:07:08Z schulte $ 049 * @see ModelContext#findModlets() 050 */ 051public class DefaultModletProvider implements ModletProvider 052{ 053 054 /** 055 * Constant for the name of the model context attribute backing property {@code enabled}. 056 * @see #findModlets(org.jomc.modlet.ModelContext) 057 * @see ModelContext#getAttribute(java.lang.String) 058 * @since 1.2 059 */ 060 public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.modlet.DefaultModletProvider.enabledAttribute"; 061 062 /** 063 * Constant for the name of the system property controlling property {@code defaultEnabled}. 064 * @see #isDefaultEnabled() 065 * @since 1.2 066 */ 067 private static final String DEFAULT_ENABLED_PROPERTY_NAME = 068 "org.jomc.modlet.DefaultModletProvider.defaultEnabled"; 069 070 /** 071 * Default value of the flag indicating the provider is enabled by default. 072 * @see #isDefaultEnabled() 073 * @since 1.2 074 */ 075 private static final Boolean DEFAULT_ENABLED = Boolean.TRUE; 076 077 /** Flag indicating the provider is enabled by default. */ 078 private static volatile Boolean defaultEnabled; 079 080 /** Flag indicating the provider is enabled. */ 081 private Boolean enabled; 082 083 /** 084 * Constant for the name of the model context attribute backing property {@code modletLocation}. 085 * @see #findModlets(org.jomc.modlet.ModelContext) 086 * @see ModelContext#getAttribute(java.lang.String) 087 * @since 1.2 088 */ 089 public static final String MODLET_LOCATION_ATTRIBUTE_NAME = 090 "org.jomc.modlet.DefaultModletProvider.modletLocationAttribute"; 091 092 /** 093 * Constant for the name of the system property controlling property {@code defaultModletLocation}. 094 * @see #getDefaultModletLocation() 095 * @since 1.2 096 */ 097 private static final String DEFAULT_MODLET_LOCATION_PROPERTY_NAME = 098 "org.jomc.modlet.DefaultModletProvider.defaultModletLocation"; 099 100 /** 101 * Class path location searched for {@code Modlets} by default. 102 * @see #getDefaultModletLocation() 103 */ 104 private static final String DEFAULT_MODLET_LOCATION = "META-INF/jomc-modlet.xml"; 105 106 /** Default {@code Modlet} location. */ 107 private static volatile String defaultModletLocation; 108 109 /** Modlet location of the instance. */ 110 private String modletLocation; 111 112 /** 113 * Constant for the name of the model context attribute backing property {@code validating}. 114 * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String) 115 * @see ModelContext#getAttribute(java.lang.String) 116 * @since 1.2 117 */ 118 public static final String VALIDATING_ATTRIBUTE_NAME = 119 "org.jomc.modlet.DefaultModletProvider.validatingAttribute"; 120 121 /** 122 * Constant for the name of the system property controlling property {@code defaultValidating}. 123 * @see #isDefaultValidating() 124 * @since 1.2 125 */ 126 private static final String DEFAULT_VALIDATING_PROPERTY_NAME = 127 "org.jomc.modlet.DefaultModletProvider.defaultValidating"; 128 129 /** 130 * Default value of the flag indicating the provider is validating resources by default. 131 * @see #isDefaultValidating() 132 * @since 1.2 133 */ 134 private static final Boolean DEFAULT_VALIDATING = Boolean.TRUE; 135 136 /** 137 * Flag indicating the provider is validating resources by default. 138 * @since 1.2 139 */ 140 private static volatile Boolean defaultValidating; 141 142 /** 143 * Flag indicating the provider is validating resources. 144 * @since 1.2 145 */ 146 private Boolean validating; 147 148 /** Creates a new {@code DefaultModletProvider} instance. */ 149 public DefaultModletProvider() 150 { 151 super(); 152 } 153 154 /** 155 * Gets a flag indicating the provider is enabled by default. 156 * <p>The default enabled flag is controlled by system property 157 * {@code org.jomc.modlet.DefaultModletProvider.defaultEnabled} holding a value indicating the provider is 158 * enabled by default. If that property is not set, the {@code true} default is returned.</p> 159 * 160 * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by 161 * default. 162 * 163 * @see #isEnabled() 164 * @see #setDefaultEnabled(java.lang.Boolean) 165 */ 166 public static boolean isDefaultEnabled() 167 { 168 if ( defaultEnabled == null ) 169 { 170 defaultEnabled = Boolean.valueOf( System.getProperty( 171 DEFAULT_ENABLED_PROPERTY_NAME, Boolean.toString( DEFAULT_ENABLED ) ) ); 172 173 } 174 175 return defaultEnabled; 176 } 177 178 /** 179 * Sets the flag indicating the provider is enabled by default. 180 * 181 * @param value The new value of the flag indicating the provider is enabled by default or {@code null}. 182 * 183 * @see #isDefaultEnabled() 184 */ 185 public static void setDefaultEnabled( final Boolean value ) 186 { 187 defaultEnabled = value; 188 } 189 190 /** 191 * Gets a flag indicating the provider is enabled. 192 * 193 * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled. 194 * 195 * @see #isDefaultEnabled() 196 * @see #setEnabled(java.lang.Boolean) 197 */ 198 public final boolean isEnabled() 199 { 200 if ( this.enabled == null ) 201 { 202 this.enabled = isDefaultEnabled(); 203 } 204 205 return this.enabled; 206 } 207 208 /** 209 * Sets the flag indicating the provider is enabled. 210 * 211 * @param value The new value of the flag indicating the provider is enabled or {@code null}. 212 * 213 * @see #isEnabled() 214 */ 215 public final void setEnabled( final Boolean value ) 216 { 217 this.enabled = value; 218 } 219 220 /** 221 * Gets the default location searched for {@code Modlet} resources. 222 * <p>The default {@code Modlet} location is controlled by system property 223 * {@code org.jomc.modlet.DefaultModletProvider.defaultModletLocation} holding the location to search for 224 * {@code Modlet} resources by default. If that property is not set, the {@code META-INF/jomc-modlet.xml} default is 225 * returned.</p> 226 * 227 * @return The location searched for {@code Modlet} resources by default. 228 * 229 * @see #setDefaultModletLocation(java.lang.String) 230 */ 231 public static String getDefaultModletLocation() 232 { 233 if ( defaultModletLocation == null ) 234 { 235 defaultModletLocation = System.getProperty( 236 DEFAULT_MODLET_LOCATION_PROPERTY_NAME, DEFAULT_MODLET_LOCATION ); 237 238 } 239 240 return defaultModletLocation; 241 } 242 243 /** 244 * Sets the default location searched for {@code Modlet} resources. 245 * 246 * @param value The new default location to search for {@code Modlet} resources or {@code null}. 247 * 248 * @see #getDefaultModletLocation() 249 */ 250 public static void setDefaultModletLocation( final String value ) 251 { 252 defaultModletLocation = value; 253 } 254 255 /** 256 * Gets the location searched for {@code Modlet} resources. 257 * 258 * @return The location searched for {@code Modlet} resources. 259 * 260 * @see #getDefaultModletLocation() 261 * @see #setModletLocation(java.lang.String) 262 */ 263 public final String getModletLocation() 264 { 265 if ( this.modletLocation == null ) 266 { 267 this.modletLocation = getDefaultModletLocation(); 268 } 269 270 return this.modletLocation; 271 } 272 273 /** 274 * Sets the location searched for {@code Modlet} resources. 275 * 276 * @param value The new location to search for {@code Modlet} resources or {@code null}. 277 * 278 * @see #getModletLocation() 279 */ 280 public final void setModletLocation( final String value ) 281 { 282 this.modletLocation = value; 283 } 284 285 /** 286 * Gets a flag indicating the provider is validating resources by default. 287 * <p>The default validating flag is controlled by system property 288 * {@code org.jomc.modlet.DefaultModletProvider.defaultValidating} holding a value indicating the provider is 289 * validating resources by default. If that property is not set, the {@code true} default is returned.</p> 290 * 291 * @return {@code true}, if the provider is validating resources by default; {@code false}, if the provider is not 292 * validating resources by default. 293 * 294 * @see #isValidating() 295 * @see #setDefaultValidating(java.lang.Boolean) 296 * 297 * @since 1.2 298 */ 299 public static boolean isDefaultValidating() 300 { 301 if ( defaultValidating == null ) 302 { 303 defaultValidating = Boolean.valueOf( System.getProperty( 304 DEFAULT_VALIDATING_PROPERTY_NAME, Boolean.toString( DEFAULT_VALIDATING ) ) ); 305 306 } 307 308 return defaultValidating; 309 } 310 311 /** 312 * Sets the flag indicating the provider is validating resources by default. 313 * 314 * @param value The new value of the flag indicating the provider is validating resources by default or 315 * {@code null}. 316 * 317 * @see #isDefaultValidating() 318 * 319 * @since 1.2 320 */ 321 public static void setDefaultValidating( final Boolean value ) 322 { 323 defaultValidating = value; 324 } 325 326 /** 327 * Gets a flag indicating the provider is validating resources. 328 * 329 * @return {@code true}, if the provider is validating resources; {@code false}, if the provider is not validating 330 * resources. 331 * 332 * @see #isDefaultValidating() 333 * @see #setValidating(java.lang.Boolean) 334 * 335 * @since 1.2 336 */ 337 public final boolean isValidating() 338 { 339 if ( this.validating == null ) 340 { 341 this.validating = isDefaultValidating(); 342 } 343 344 return this.validating; 345 } 346 347 /** 348 * Sets the flag indicating the provider is validating resources. 349 * 350 * @param value The new value of the flag indicating the provider is validating resources or {@code null}. 351 * 352 * @see #isValidating() 353 * 354 * @since 1.2 355 */ 356 public final void setValidating( final Boolean value ) 357 { 358 this.validating = value; 359 } 360 361 /** 362 * Searches a given context for {@code Modlets}. 363 * 364 * @param context The context to search for {@code Modlets}. 365 * @param location The location to search at. 366 * 367 * @return The {@code Modlets} found at {@code location} in {@code context} or {@code null}, if no {@code Modlets} 368 * are found. 369 * 370 * @throws NullPointerException if {@code context} or {@code location} is {@code null}. 371 * @throws ModelException if searching the context fails. 372 * 373 * @see #isValidating() 374 * @see #VALIDATING_ATTRIBUTE_NAME 375 */ 376 public Modlets findModlets( final ModelContext context, final String location ) throws ModelException 377 { 378 if ( context == null ) 379 { 380 throw new NullPointerException( "context" ); 381 } 382 if ( location == null ) 383 { 384 throw new NullPointerException( "location" ); 385 } 386 387 URL url = null; 388 389 try 390 { 391 boolean contextValidating = this.isValidating(); 392 if ( DEFAULT_VALIDATING == contextValidating 393 && context.getAttribute( VALIDATING_ATTRIBUTE_NAME ) instanceof Boolean ) 394 { 395 contextValidating = (Boolean) context.getAttribute( VALIDATING_ATTRIBUTE_NAME ); 396 } 397 398 Modlets modlets = null; 399 final long t0 = System.currentTimeMillis(); 400 final JAXBContext ctx = context.createContext( ModletObject.MODEL_PUBLIC_ID ); 401 final Unmarshaller u = ctx.createUnmarshaller(); 402 final Enumeration<URL> e = context.findResources( location ); 403 404 if ( contextValidating ) 405 { 406 u.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) ); 407 } 408 409 while ( e.hasMoreElements() ) 410 { 411 url = e.nextElement(); 412 Object content = u.unmarshal( url ); 413 if ( content instanceof JAXBElement<?> ) 414 { 415 content = ( (JAXBElement<?>) content ).getValue(); 416 } 417 418 if ( content instanceof Modlet ) 419 { 420 if ( modlets == null ) 421 { 422 modlets = new Modlets(); 423 } 424 425 modlets.getModlet().add( (Modlet) content ); 426 } 427 else if ( content instanceof Modlets ) 428 { 429 if ( modlets == null ) 430 { 431 modlets = new Modlets(); 432 } 433 434 modlets.getModlet().addAll( ( (Modlets) content ).getModlet() ); 435 } 436 } 437 438 if ( context.isLoggable( Level.FINE ) ) 439 { 440 context.log( Level.FINE, getMessage( "contextReport", 441 modlets != null ? modlets.getModlet().size() : 0, 442 location, System.currentTimeMillis() - t0 ), null ); 443 444 } 445 446 return modlets == null || modlets.getModlet().isEmpty() ? null : modlets; 447 } 448 catch ( final UnmarshalException e ) 449 { 450 String message = getMessage( e ); 451 if ( message == null && e.getLinkedException() != null ) 452 { 453 message = getMessage( e.getLinkedException() ); 454 } 455 456 if ( url != null ) 457 { 458 message = getMessage( "unmarshalException", url.toExternalForm(), 459 message != null ? " " + message : "" ); 460 461 } 462 463 throw new ModelException( message, e ); 464 } 465 catch ( final JAXBException e ) 466 { 467 String message = getMessage( e ); 468 if ( message == null && e.getLinkedException() != null ) 469 { 470 message = getMessage( e.getLinkedException() ); 471 } 472 473 throw new ModelException( message, e ); 474 } 475 } 476 477 /** 478 * {@inheritDoc} 479 * 480 * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider 481 * is disabled. 482 * 483 * @see #isEnabled() 484 * @see #getModletLocation() 485 * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String) 486 * @see #ENABLED_ATTRIBUTE_NAME 487 * @see #MODLET_LOCATION_ATTRIBUTE_NAME 488 */ 489 public Modlets findModlets( final ModelContext context ) throws ModelException 490 { 491 if ( context == null ) 492 { 493 throw new NullPointerException( "context" ); 494 } 495 496 Modlets found = null; 497 498 boolean contextEnabled = this.isEnabled(); 499 if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean ) 500 { 501 contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME ); 502 } 503 504 String contextModletLocation = this.getModletLocation(); 505 if ( DEFAULT_MODLET_LOCATION.equals( contextModletLocation ) 506 && context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME ) instanceof String ) 507 { 508 contextModletLocation = (String) context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME ); 509 } 510 511 if ( contextEnabled ) 512 { 513 found = this.findModlets( context, contextModletLocation ); 514 } 515 else if ( context.isLoggable( Level.FINER ) ) 516 { 517 context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName() ), null ); 518 } 519 520 return found; 521 } 522 523 private static String getMessage( final String key, final Object... arguments ) 524 { 525 return MessageFormat.format( ResourceBundle.getBundle( 526 DefaultModletProvider.class.getName().replace( '.', '/' ) ).getString( key ), arguments ); 527 528 } 529 530 private static String getMessage( final Throwable t ) 531 { 532 return t != null ? t.getMessage() != null ? t.getMessage() : getMessage( t.getCause() ) : null; 533 } 534 535}