1 /*
2 * Copyright (C) Christian Schulte, 2005-206
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * o Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 *
12 * o Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in
14 * the documentation and/or other materials provided with the
15 * distribution.
16 *
17 * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18 * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
19 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
20 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 *
28 * $JOMC: DefaultModletProvider.java 4613 2012-09-22 10:07:08Z schulte $
29 *
30 */
31 package org.jomc.modlet;
32
33 import java.net.URL;
34 import java.text.MessageFormat;
35 import java.util.Enumeration;
36 import java.util.ResourceBundle;
37 import java.util.logging.Level;
38 import javax.xml.bind.JAXBContext;
39 import javax.xml.bind.JAXBElement;
40 import javax.xml.bind.JAXBException;
41 import javax.xml.bind.UnmarshalException;
42 import javax.xml.bind.Unmarshaller;
43
44 /**
45 * Default {@code ModletProvider} implementation.
46 *
47 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
48 * @version $JOMC: DefaultModletProvider.java 4613 2012-09-22 10:07:08Z schulte $
49 * @see ModelContext#findModlets()
50 */
51 public class DefaultModletProvider implements ModletProvider
52 {
53
54 /**
55 * Constant for the name of the model context attribute backing property {@code enabled}.
56 * @see #findModlets(org.jomc.modlet.ModelContext)
57 * @see ModelContext#getAttribute(java.lang.String)
58 * @since 1.2
59 */
60 public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.modlet.DefaultModletProvider.enabledAttribute";
61
62 /**
63 * Constant for the name of the system property controlling property {@code defaultEnabled}.
64 * @see #isDefaultEnabled()
65 * @since 1.2
66 */
67 private static final String DEFAULT_ENABLED_PROPERTY_NAME =
68 "org.jomc.modlet.DefaultModletProvider.defaultEnabled";
69
70 /**
71 * Default value of the flag indicating the provider is enabled by default.
72 * @see #isDefaultEnabled()
73 * @since 1.2
74 */
75 private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
76
77 /** Flag indicating the provider is enabled by default. */
78 private static volatile Boolean defaultEnabled;
79
80 /** Flag indicating the provider is enabled. */
81 private Boolean enabled;
82
83 /**
84 * Constant for the name of the model context attribute backing property {@code modletLocation}.
85 * @see #findModlets(org.jomc.modlet.ModelContext)
86 * @see ModelContext#getAttribute(java.lang.String)
87 * @since 1.2
88 */
89 public static final String MODLET_LOCATION_ATTRIBUTE_NAME =
90 "org.jomc.modlet.DefaultModletProvider.modletLocationAttribute";
91
92 /**
93 * Constant for the name of the system property controlling property {@code defaultModletLocation}.
94 * @see #getDefaultModletLocation()
95 * @since 1.2
96 */
97 private static final String DEFAULT_MODLET_LOCATION_PROPERTY_NAME =
98 "org.jomc.modlet.DefaultModletProvider.defaultModletLocation";
99
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 }