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: DefaultModelProvider.java 4941 2014-03-25 03:48:34Z schulte $
29 *
30 */
31 package org.jomc.model.modlet;
32
33 import java.net.URL;
34 import java.text.MessageFormat;
35 import java.util.Enumeration;
36 import java.util.Locale;
37 import java.util.ResourceBundle;
38 import java.util.logging.Level;
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 import org.jomc.model.Module;
44 import org.jomc.model.Modules;
45 import org.jomc.model.Text;
46 import org.jomc.model.Texts;
47 import org.jomc.modlet.Model;
48 import org.jomc.modlet.ModelContext;
49 import org.jomc.modlet.ModelException;
50 import org.jomc.modlet.ModelProvider;
51
52 /**
53 * Default object management and configuration {@code ModelProvider} implementation.
54 *
55 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
56 * @version $JOMC: DefaultModelProvider.java 4941 2014-03-25 03:48:34Z schulte $
57 * @see ModelContext#findModel(java.lang.String)
58 */
59 public class DefaultModelProvider implements ModelProvider
60 {
61
62 /**
63 * Constant for the name of the model context attribute backing property {@code enabled}.
64 * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
65 * @see ModelContext#getAttribute(java.lang.String)
66 * @since 1.2
67 */
68 public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.model.modlet.DefaultModelProvider.enabledAttribute";
69
70 /**
71 * Constant for the name of the system property controlling property {@code defaultEnabled}.
72 * @see #isDefaultEnabled()
73 */
74 private static final String DEFAULT_ENABLED_PROPERTY_NAME =
75 "org.jomc.model.modlet.DefaultModelProvider.defaultEnabled";
76
77 /**
78 * Constant for the name of the deprecated system property controlling property {@code defaultEnabled}.
79 * @see #isDefaultEnabled()
80 */
81 private static final String DEPRECATED_DEFAULT_ENABLED_PROPERTY_NAME =
82 "org.jomc.model.DefaultModelProvider.defaultEnabled";
83
84 /**
85 * Default value of the flag indicating the provider is enabled by default.
86 * @see #isDefaultEnabled()
87 * @since 1.2
88 */
89 private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
90
91 /** Flag indicating the provider is enabled by default. */
92 private static volatile Boolean defaultEnabled;
93
94 /** Flag indicating the provider is enabled. */
95 private Boolean enabled;
96
97 /**
98 * Constant for the name of the model context attribute backing property {@code moduleLocation}.
99 * @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 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 }