View Javadoc
1   /*
2    *   Copyright (C) Christian Schulte <cs@schulte.it>, 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 5043 2015-05-27 07:03:39Z 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 5043 2015-05-27 07:03:39Z 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       *
65       * @see #findModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
66       * @see ModelContext#getAttribute(java.lang.String)
67       * @since 1.2
68       */
69      public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.model.modlet.DefaultModelProvider.enabledAttribute";
70  
71      /**
72       * Constant for the name of the system property controlling property {@code defaultEnabled}.
73       *
74       * @see #isDefaultEnabled()
75       */
76      private static final String DEFAULT_ENABLED_PROPERTY_NAME =
77          "org.jomc.model.modlet.DefaultModelProvider.defaultEnabled";
78  
79      /**
80       * Constant for the name of the deprecated system property controlling property {@code defaultEnabled}.
81       * @see #isDefaultEnabled()
82       */
83      private static final String DEPRECATED_DEFAULT_ENABLED_PROPERTY_NAME =
84          "org.jomc.model.DefaultModelProvider.defaultEnabled";
85  
86      /**
87       * Default value of the flag indicating the provider is enabled by default.
88       *
89       * @see #isDefaultEnabled()
90       * @since 1.2
91       */
92      private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
93  
94      /**
95       * Flag indicating the provider is enabled by default.
96       */
97      private static volatile Boolean defaultEnabled;
98  
99      /**
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 }