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: DefaultModletProvider.java 5043 2015-05-27 07:03:39Z 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 5043 2015-05-27 07:03:39Z schulte $
49   * @see ModelContext#findModlets(org.jomc.modlet.Modlets)
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       *
57       * @see #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
58       * @see ModelContext#getAttribute(java.lang.String)
59       * @since 1.2
60       */
61      public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.modlet.DefaultModletProvider.enabledAttribute";
62  
63      /**
64       * Constant for the name of the system property controlling property {@code defaultEnabled}.
65       *
66       * @see #isDefaultEnabled()
67       * @since 1.2
68       */
69      private static final String DEFAULT_ENABLED_PROPERTY_NAME =
70          "org.jomc.modlet.DefaultModletProvider.defaultEnabled";
71  
72      /**
73       * Default value of the flag indicating the provider is enabled by default.
74       *
75       * @see #isDefaultEnabled()
76       * @since 1.2
77       */
78      private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
79  
80      /**
81       * Flag indicating the provider is enabled by default.
82       */
83      private static volatile Boolean defaultEnabled;
84  
85      /**
86       * Flag indicating the provider is enabled.
87       */
88      private Boolean enabled;
89  
90      /**
91       * Constant for the name of the model context attribute backing property {@code modletLocation}.
92       *
93       * @see #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
94       * @see ModelContext#getAttribute(java.lang.String)
95       * @since 1.2
96       */
97      public static final String MODLET_LOCATION_ATTRIBUTE_NAME =
98          "org.jomc.modlet.DefaultModletProvider.modletLocationAttribute";
99  
100     /**
101      * Constant for the name of the system property controlling property {@code defaultModletLocation}.
102      *
103      * @see #getDefaultModletLocation()
104      * @since 1.2
105      */
106     private static final String DEFAULT_MODLET_LOCATION_PROPERTY_NAME =
107         "org.jomc.modlet.DefaultModletProvider.defaultModletLocation";
108 
109     /**
110      * Class path location searched for {@code Modlets} by default.
111      *
112      * @see #getDefaultModletLocation()
113      */
114     private static final String DEFAULT_MODLET_LOCATION = "META-INF/jomc-modlet.xml";
115 
116     /**
117      * Default {@code Modlet} location.
118      */
119     private static volatile String defaultModletLocation;
120 
121     /**
122      * Modlet location of the instance.
123      */
124     private String modletLocation;
125 
126     /**
127      * Constant for the name of the model context attribute backing property {@code validating}.
128      *
129      * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
130      * @see ModelContext#getAttribute(java.lang.String)
131      * @since 1.2
132      */
133     public static final String VALIDATING_ATTRIBUTE_NAME =
134         "org.jomc.modlet.DefaultModletProvider.validatingAttribute";
135 
136     /**
137      * Constant for the name of the system property controlling property {@code defaultValidating}.
138      *
139      * @see #isDefaultValidating()
140      * @since 1.2
141      */
142     private static final String DEFAULT_VALIDATING_PROPERTY_NAME =
143         "org.jomc.modlet.DefaultModletProvider.defaultValidating";
144 
145     /**
146      * Default value of the flag indicating the provider is validating resources by default.
147      *
148      * @see #isDefaultValidating()
149      * @since 1.2
150      */
151     private static final Boolean DEFAULT_VALIDATING = Boolean.TRUE;
152 
153     /**
154      * Flag indicating the provider is validating resources by default.
155      *
156      * @since 1.2
157      */
158     private static volatile Boolean defaultValidating;
159 
160     /**
161      * Flag indicating the provider is validating resources.
162      *
163      * @since 1.2
164      */
165     private Boolean validating;
166 
167     /**
168      * Constant for the name of the system property controlling property {@code defaultOrdinal}.
169      *
170      * @see #getDefaultOrdinal()
171      * @since 1.6
172      */
173     private static final String DEFAULT_ORDINAL_PROPERTY_NAME =
174         "org.jomc.modlet.DefaultModletProvider.defaultOrdinal";
175 
176     /**
177      * Default value of the ordinal number of the provider.
178      *
179      * @see #getDefaultOrdinal()
180      * @since 1.6
181      */
182     private static final Integer DEFAULT_ORDINAL = 0;
183 
184     /**
185      * Default ordinal number of the provider.
186      *
187      * @since 1.6
188      */
189     private static volatile Integer defaultOrdinal;
190 
191     /**
192      * Ordinal number of the provider.
193      *
194      * @since 1.6
195      */
196     private Integer ordinal;
197 
198     /**
199      * Creates a new {@code DefaultModletProvider} instance.
200      */
201     public DefaultModletProvider()
202     {
203         super();
204     }
205 
206     /**
207      * Gets a flag indicating the provider is enabled by default.
208      * <p>
209      * The default enabled flag is controlled by system property
210      * {@code org.jomc.modlet.DefaultModletProvider.defaultEnabled} holding a value indicating the provider is
211      * enabled by default. If that property is not set, the {@code true} default is returned.
212      * </p>
213      *
214      * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by
215      * default.
216      *
217      * @see #isEnabled()
218      * @see #setDefaultEnabled(java.lang.Boolean)
219      */
220     public static boolean isDefaultEnabled()
221     {
222         if ( defaultEnabled == null )
223         {
224             defaultEnabled = Boolean.valueOf( System.getProperty(
225                 DEFAULT_ENABLED_PROPERTY_NAME, Boolean.toString( DEFAULT_ENABLED ) ) );
226 
227         }
228 
229         return defaultEnabled;
230     }
231 
232     /**
233      * Sets the flag indicating the provider is enabled by default.
234      *
235      * @param value The new value of the flag indicating the provider is enabled by default or {@code null}.
236      *
237      * @see #isDefaultEnabled()
238      */
239     public static void setDefaultEnabled( final Boolean value )
240     {
241         defaultEnabled = value;
242     }
243 
244     /**
245      * Gets a flag indicating the provider is enabled.
246      *
247      * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled.
248      *
249      * @see #isDefaultEnabled()
250      * @see #setEnabled(java.lang.Boolean)
251      */
252     public final boolean isEnabled()
253     {
254         if ( this.enabled == null )
255         {
256             this.enabled = isDefaultEnabled();
257         }
258 
259         return this.enabled;
260     }
261 
262     /**
263      * Sets the flag indicating the provider is enabled.
264      *
265      * @param value The new value of the flag indicating the provider is enabled or {@code null}.
266      *
267      * @see #isEnabled()
268      */
269     public final void setEnabled( final Boolean value )
270     {
271         this.enabled = value;
272     }
273 
274     /**
275      * Gets the default location searched for {@code Modlet} resources.
276      * <p>
277      * The default {@code Modlet} location is controlled by system property
278      * {@code org.jomc.modlet.DefaultModletProvider.defaultModletLocation} holding the location to search for
279      * {@code Modlet} resources by default. If that property is not set, the {@code META-INF/jomc-modlet.xml} default is
280      * returned.
281      * </p>
282      *
283      * @return The location searched for {@code Modlet} resources by default.
284      *
285      * @see #setDefaultModletLocation(java.lang.String)
286      */
287     public static String getDefaultModletLocation()
288     {
289         if ( defaultModletLocation == null )
290         {
291             defaultModletLocation = System.getProperty(
292                 DEFAULT_MODLET_LOCATION_PROPERTY_NAME, DEFAULT_MODLET_LOCATION );
293 
294         }
295 
296         return defaultModletLocation;
297     }
298 
299     /**
300      * Sets the default location searched for {@code Modlet} resources.
301      *
302      * @param value The new default location to search for {@code Modlet} resources or {@code null}.
303      *
304      * @see #getDefaultModletLocation()
305      */
306     public static void setDefaultModletLocation( final String value )
307     {
308         defaultModletLocation = value;
309     }
310 
311     /**
312      * Gets the location searched for {@code Modlet} resources.
313      *
314      * @return The location searched for {@code Modlet} resources.
315      *
316      * @see #getDefaultModletLocation()
317      * @see #setModletLocation(java.lang.String)
318      */
319     public final String getModletLocation()
320     {
321         if ( this.modletLocation == null )
322         {
323             this.modletLocation = getDefaultModletLocation();
324         }
325 
326         return this.modletLocation;
327     }
328 
329     /**
330      * Sets the location searched for {@code Modlet} resources.
331      *
332      * @param value The new location to search for {@code Modlet} resources or {@code null}.
333      *
334      * @see #getModletLocation()
335      */
336     public final void setModletLocation( final String value )
337     {
338         this.modletLocation = value;
339     }
340 
341     /**
342      * Gets a flag indicating the provider is validating resources by default.
343      * <p>
344      * The default validating flag is controlled by system property
345      * {@code org.jomc.modlet.DefaultModletProvider.defaultValidating} holding a value indicating the provider is
346      * validating resources by default. If that property is not set, the {@code true} default is returned.
347      * </p>
348      *
349      * @return {@code true}, if the provider is validating resources by default; {@code false}, if the provider is not
350      * validating resources by default.
351      *
352      * @see #isValidating()
353      * @see #setDefaultValidating(java.lang.Boolean)
354      *
355      * @since 1.2
356      */
357     public static boolean isDefaultValidating()
358     {
359         if ( defaultValidating == null )
360         {
361             defaultValidating = Boolean.valueOf( System.getProperty(
362                 DEFAULT_VALIDATING_PROPERTY_NAME, Boolean.toString( DEFAULT_VALIDATING ) ) );
363 
364         }
365 
366         return defaultValidating;
367     }
368 
369     /**
370      * Sets the flag indicating the provider is validating resources by default.
371      *
372      * @param value The new value of the flag indicating the provider is validating resources by default or
373      * {@code null}.
374      *
375      * @see #isDefaultValidating()
376      *
377      * @since 1.2
378      */
379     public static void setDefaultValidating( final Boolean value )
380     {
381         defaultValidating = value;
382     }
383 
384     /**
385      * Gets a flag indicating the provider is validating resources.
386      *
387      * @return {@code true}, if the provider is validating resources; {@code false}, if the provider is not validating
388      * resources.
389      *
390      * @see #isDefaultValidating()
391      * @see #setValidating(java.lang.Boolean)
392      *
393      * @since 1.2
394      */
395     public final boolean isValidating()
396     {
397         if ( this.validating == null )
398         {
399             this.validating = isDefaultValidating();
400         }
401 
402         return this.validating;
403     }
404 
405     /**
406      * Sets the flag indicating the provider is validating resources.
407      *
408      * @param value The new value of the flag indicating the provider is validating resources or {@code null}.
409      *
410      * @see #isValidating()
411      *
412      * @since 1.2
413      */
414     public final void setValidating( final Boolean value )
415     {
416         this.validating = value;
417     }
418 
419     /**
420      * Gets the default ordinal number of the provider.
421      * <p>
422      * The default ordinal number is controlled by system property
423      * {@code org.jomc.modlet.DefaultModletProvider.defaultOrdinal} holding the default ordinal number of the provider.
424      * If that property is not set, the {@code 0} default is returned.
425      * </p>
426      *
427      * @return The default ordinal number of the provider.
428      *
429      * @see #setDefaultOrdinal(java.lang.Integer)
430      *
431      * @since 1.6
432      */
433     public static int getDefaultOrdinal()
434     {
435         if ( defaultOrdinal == null )
436         {
437             defaultOrdinal = Integer.getInteger( DEFAULT_ORDINAL_PROPERTY_NAME, DEFAULT_ORDINAL );
438         }
439 
440         return defaultOrdinal;
441     }
442 
443     /**
444      * Sets the default ordinal number of the provider.
445      *
446      * @param value The new default ordinal number of the provider or {@code null}.
447      *
448      * @see #getDefaultOrdinal()
449      *
450      * @since 1.6
451      */
452     public static void setDefaultOrdinal( final Integer value )
453     {
454         defaultOrdinal = value;
455     }
456 
457     /**
458      * Gets the ordinal number of the provider.
459      *
460      * @return The ordinal number of the provider.
461      *
462      * @see #getDefaultOrdinal()
463      * @see #setOrdinal(java.lang.Integer)
464      *
465      * @since 1.6
466      */
467     public final int getOrdinal()
468     {
469         if ( this.ordinal == null )
470         {
471             this.ordinal = getDefaultOrdinal();
472         }
473 
474         return this.ordinal;
475     }
476 
477     /**
478      * Sets the ordinal number of the provider.
479      *
480      * @param value The new ordinal number of the provider or {@code null}.
481      *
482      * @see #getOrdinal()
483      *
484      * @since 1.6
485      */
486     public final void setOrdinal( final Integer value )
487     {
488         this.ordinal = value;
489     }
490 
491     /**
492      * Searches a given context for {@code Modlets}.
493      *
494      * @param context The context to search for {@code Modlets}.
495      * @param location The location to search at.
496      *
497      * @return The {@code Modlets} found at {@code location} in {@code context} or {@code null}, if no {@code Modlets}
498      * are found.
499      *
500      * @throws NullPointerException if {@code context} or {@code location} is {@code null}.
501      * @throws ModelException if searching the context fails.
502      *
503      * @see #isValidating()
504      * @see #VALIDATING_ATTRIBUTE_NAME
505      */
506     public Modlets findModlets( final ModelContext context, final String location ) throws ModelException
507     {
508         if ( context == null )
509         {
510             throw new NullPointerException( "context" );
511         }
512         if ( location == null )
513         {
514             throw new NullPointerException( "location" );
515         }
516 
517         URL url = null;
518 
519         try
520         {
521             boolean contextValidating = this.isValidating();
522             if ( DEFAULT_VALIDATING == contextValidating
523                      && context.getAttribute( VALIDATING_ATTRIBUTE_NAME ) instanceof Boolean )
524             {
525                 contextValidating = (Boolean) context.getAttribute( VALIDATING_ATTRIBUTE_NAME );
526             }
527 
528             Modlets modlets = null;
529             final long t0 = System.currentTimeMillis();
530             final JAXBContext ctx = context.createContext( ModletObject.MODEL_PUBLIC_ID );
531             final Unmarshaller u = ctx.createUnmarshaller();
532             final Enumeration<URL> e = context.findResources( location );
533 
534             if ( contextValidating )
535             {
536                 u.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
537             }
538 
539             while ( e.hasMoreElements() )
540             {
541                 url = e.nextElement();
542                 Object content = u.unmarshal( url );
543                 if ( content instanceof JAXBElement<?> )
544                 {
545                     content = ( (JAXBElement<?>) content ).getValue();
546                 }
547 
548                 if ( content instanceof Modlet )
549                 {
550                     if ( modlets == null )
551                     {
552                         modlets = new Modlets();
553                     }
554 
555                     modlets.getModlet().add( (Modlet) content );
556                 }
557                 else if ( content instanceof Modlets )
558                 {
559                     if ( modlets == null )
560                     {
561                         modlets = new Modlets();
562                     }
563 
564                     modlets.getModlet().addAll( ( (Modlets) content ).getModlet() );
565                 }
566             }
567 
568             if ( context.isLoggable( Level.FINE ) )
569             {
570                 context.log( Level.FINE, getMessage( "contextReport",
571                                                      modlets != null ? modlets.getModlet().size() : 0,
572                                                      location, System.currentTimeMillis() - t0 ), null );
573 
574             }
575 
576             return modlets == null || modlets.getModlet().isEmpty() ? null : modlets;
577         }
578         catch ( final UnmarshalException e )
579         {
580             String message = getMessage( e );
581             if ( message == null && e.getLinkedException() != null )
582             {
583                 message = getMessage( e.getLinkedException() );
584             }
585 
586             if ( url != null )
587             {
588                 message = getMessage( "unmarshalException", url.toExternalForm(),
589                                       message != null ? " " + message : "" );
590 
591             }
592 
593             throw new ModelException( message, e );
594         }
595         catch ( final JAXBException e )
596         {
597             String message = getMessage( e );
598             if ( message == null && e.getLinkedException() != null )
599             {
600                 message = getMessage( e.getLinkedException() );
601             }
602 
603             throw new ModelException( message, e );
604         }
605     }
606 
607     /**
608      * {@inheritDoc}
609      *
610      * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider
611      * is disabled.
612      *
613      * @see #isEnabled()
614      * @see #getModletLocation()
615      * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
616      * @see #ENABLED_ATTRIBUTE_NAME
617      * @see #MODLET_LOCATION_ATTRIBUTE_NAME
618      * @deprecated As of JOMC 1.6, this method has been replaced by {@link #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)}.
619      * This method will be removed in JOMC 2.0.
620      */
621     @Deprecated
622     public Modlets findModlets( final ModelContext context ) throws ModelException
623     {
624         if ( context == null )
625         {
626             throw new NullPointerException( "context" );
627         }
628 
629         return this.findModlets( context, new Modlets() );
630     }
631 
632     /**
633      * {@inheritDoc}
634      *
635      * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider
636      * is disabled.
637      *
638      * @see #isEnabled()
639      * @see #getModletLocation()
640      * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
641      * @see #ENABLED_ATTRIBUTE_NAME
642      * @see #MODLET_LOCATION_ATTRIBUTE_NAME
643      * @since 1.6
644      */
645     public Modlets findModlets( final ModelContext context, final Modlets modlets ) throws ModelException
646     {
647         if ( context == null )
648         {
649             throw new NullPointerException( "context" );
650         }
651         if ( modlets == null )
652         {
653             throw new NullPointerException( "context" );
654         }
655 
656         Modlets provided = null;
657 
658         boolean contextEnabled = this.isEnabled();
659         if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
660         {
661             contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
662         }
663 
664         String contextModletLocation = this.getModletLocation();
665         if ( DEFAULT_MODLET_LOCATION.equals( contextModletLocation )
666                  && context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME ) instanceof String )
667         {
668             contextModletLocation = (String) context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME );
669         }
670 
671         if ( contextEnabled )
672         {
673             final Modlets found = this.findModlets( context, contextModletLocation );
674 
675             if ( found != null )
676             {
677                 provided = modlets.clone();
678                 provided.getModlet().addAll( found.getModlet() );
679             }
680         }
681         else if ( context.isLoggable( Level.FINER ) )
682         {
683             context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName() ), null );
684         }
685 
686         return provided;
687     }
688 
689     private static String getMessage( final String key, final Object... arguments )
690     {
691         return MessageFormat.format( ResourceBundle.getBundle(
692             DefaultModletProvider.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
693 
694     }
695 
696     private static String getMessage( final Throwable t )
697     {
698         return t != null
699                    ? t.getMessage() != null && t.getMessage().trim().length() > 0
700                          ? t.getMessage()
701                          : getMessage( t.getCause() )
702                    : null;
703 
704     }
705 
706 }