View Javadoc
1   /*
2    *   Copyright (C) 2005 Christian Schulte <cs@schulte.it>
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 5293 2016-08-29 17:41:51Z schulte $
29   *
30   */
31  package org.jomc.modlet;
32  
33  import java.lang.reflect.UndeclaredThrowableException;
34  import java.net.MalformedURLException;
35  import java.net.URI;
36  import java.net.URISyntaxException;
37  import java.net.URL;
38  import java.text.MessageFormat;
39  import java.util.ArrayList;
40  import java.util.Enumeration;
41  import java.util.HashSet;
42  import java.util.List;
43  import java.util.ResourceBundle;
44  import java.util.Set;
45  import java.util.concurrent.Callable;
46  import java.util.concurrent.CancellationException;
47  import java.util.concurrent.ExecutionException;
48  import java.util.concurrent.Future;
49  import java.util.logging.Level;
50  import javax.xml.bind.JAXBContext;
51  import javax.xml.bind.JAXBElement;
52  import javax.xml.bind.JAXBException;
53  import javax.xml.bind.UnmarshalException;
54  import javax.xml.bind.Unmarshaller;
55  
56  /**
57   * Default {@code ModletProvider} implementation.
58   *
59   * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
60   * @version $JOMC: DefaultModletProvider.java 5293 2016-08-29 17:41:51Z schulte $
61   * @see ModelContext#findModlets(org.jomc.modlet.Modlets)
62   */
63  public class DefaultModletProvider implements ModletProvider
64  {
65  
66      /**
67       * Constant for the name of the model context attribute backing property {@code enabled}.
68       *
69       * @see #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
70       * @see ModelContext#getAttribute(java.lang.String)
71       * @since 1.2
72       */
73      public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.modlet.DefaultModletProvider.enabledAttribute";
74  
75      /**
76       * Constant for the name of the system property controlling property {@code defaultEnabled}.
77       *
78       * @see #isDefaultEnabled()
79       * @since 1.2
80       */
81      private static final String DEFAULT_ENABLED_PROPERTY_NAME =
82          "org.jomc.modlet.DefaultModletProvider.defaultEnabled";
83  
84      /**
85       * Default value of the flag indicating the provider is enabled by default.
86       *
87       * @see #isDefaultEnabled()
88       * @since 1.2
89       */
90      private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
91  
92      /**
93       * Flag indicating the provider is enabled by default.
94       */
95      private static volatile Boolean defaultEnabled;
96  
97      /**
98       * Flag indicating the provider is enabled.
99       */
100     private volatile Boolean enabled;
101 
102     /**
103      * Constant for the name of the model context attribute backing property {@code modletLocation}.
104      *
105      * @see #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
106      * @see ModelContext#getAttribute(java.lang.String)
107      * @since 1.2
108      */
109     public static final String MODLET_LOCATION_ATTRIBUTE_NAME =
110         "org.jomc.modlet.DefaultModletProvider.modletLocationAttribute";
111 
112     /**
113      * Constant for the name of the system property controlling property {@code defaultModletLocation}.
114      *
115      * @see #getDefaultModletLocation()
116      * @since 1.2
117      */
118     private static final String DEFAULT_MODLET_LOCATION_PROPERTY_NAME =
119         "org.jomc.modlet.DefaultModletProvider.defaultModletLocation";
120 
121     /**
122      * Class path location searched for {@code Modlets} by default.
123      *
124      * @see #getDefaultModletLocation()
125      */
126     private static final String DEFAULT_MODLET_LOCATION = "META-INF/jomc-modlet.xml";
127 
128     /**
129      * Default {@code Modlet} location.
130      */
131     private static volatile String defaultModletLocation;
132 
133     /**
134      * Modlet location of the instance.
135      */
136     private volatile String modletLocation;
137 
138     /**
139      * Constant for the name of the model context attribute backing property {@code validating}.
140      *
141      * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
142      * @see ModelContext#getAttribute(java.lang.String)
143      * @since 1.2
144      */
145     public static final String VALIDATING_ATTRIBUTE_NAME =
146         "org.jomc.modlet.DefaultModletProvider.validatingAttribute";
147 
148     /**
149      * Constant for the name of the system property controlling property {@code defaultValidating}.
150      *
151      * @see #isDefaultValidating()
152      * @since 1.2
153      */
154     private static final String DEFAULT_VALIDATING_PROPERTY_NAME =
155         "org.jomc.modlet.DefaultModletProvider.defaultValidating";
156 
157     /**
158      * Default value of the flag indicating the provider is validating resources by default.
159      *
160      * @see #isDefaultValidating()
161      * @since 1.2
162      */
163     private static final Boolean DEFAULT_VALIDATING = Boolean.TRUE;
164 
165     /**
166      * Flag indicating the provider is validating resources by default.
167      *
168      * @since 1.2
169      */
170     private static volatile Boolean defaultValidating;
171 
172     /**
173      * Flag indicating the provider is validating resources.
174      *
175      * @since 1.2
176      */
177     private volatile Boolean validating;
178 
179     /**
180      * Constant for the name of the system property controlling property {@code defaultOrdinal}.
181      *
182      * @see #getDefaultOrdinal()
183      * @since 1.6
184      */
185     private static final String DEFAULT_ORDINAL_PROPERTY_NAME =
186         "org.jomc.modlet.DefaultModletProvider.defaultOrdinal";
187 
188     /**
189      * Default value of the ordinal number of the provider.
190      *
191      * @see #getDefaultOrdinal()
192      * @since 1.6
193      */
194     private static final Integer DEFAULT_ORDINAL = 0;
195 
196     /**
197      * Default ordinal number of the provider.
198      *
199      * @since 1.6
200      */
201     private static volatile Integer defaultOrdinal;
202 
203     /**
204      * Ordinal number of the provider.
205      *
206      * @since 1.6
207      */
208     private volatile Integer ordinal;
209 
210     /**
211      * Creates a new {@code DefaultModletProvider} instance.
212      */
213     public DefaultModletProvider()
214     {
215         super();
216     }
217 
218     /**
219      * Gets a flag indicating the provider is enabled by default.
220      * <p>
221      * The default enabled flag is controlled by system property
222      * {@code org.jomc.modlet.DefaultModletProvider.defaultEnabled} holding a value indicating the provider is
223      * enabled by default. If that property is not set, the {@code true} default is returned.
224      * </p>
225      *
226      * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by
227      * default.
228      *
229      * @see #isEnabled()
230      * @see #setDefaultEnabled(java.lang.Boolean)
231      */
232     public static boolean isDefaultEnabled()
233     {
234         if ( defaultEnabled == null )
235         {
236             defaultEnabled = Boolean.valueOf( System.getProperty(
237                 DEFAULT_ENABLED_PROPERTY_NAME, Boolean.toString( DEFAULT_ENABLED ) ) );
238 
239         }
240 
241         return defaultEnabled;
242     }
243 
244     /**
245      * Sets the flag indicating the provider is enabled by default.
246      *
247      * @param value The new value of the flag indicating the provider is enabled by default or {@code null}.
248      *
249      * @see #isDefaultEnabled()
250      */
251     public static void setDefaultEnabled( final Boolean value )
252     {
253         defaultEnabled = value;
254     }
255 
256     /**
257      * Gets a flag indicating the provider is enabled.
258      *
259      * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled.
260      *
261      * @see #isDefaultEnabled()
262      * @see #setEnabled(java.lang.Boolean)
263      */
264     public final boolean isEnabled()
265     {
266         if ( this.enabled == null )
267         {
268             this.enabled = isDefaultEnabled();
269         }
270 
271         return this.enabled;
272     }
273 
274     /**
275      * Sets the flag indicating the provider is enabled.
276      *
277      * @param value The new value of the flag indicating the provider is enabled or {@code null}.
278      *
279      * @see #isEnabled()
280      */
281     public final void setEnabled( final Boolean value )
282     {
283         this.enabled = value;
284     }
285 
286     /**
287      * Gets the default location searched for {@code Modlet} resources.
288      * <p>
289      * The default {@code Modlet} location is controlled by system property
290      * {@code org.jomc.modlet.DefaultModletProvider.defaultModletLocation} holding the location to search for
291      * {@code Modlet} resources by default. If that property is not set, the {@code META-INF/jomc-modlet.xml} default is
292      * returned.
293      * </p>
294      *
295      * @return The location searched for {@code Modlet} resources by default.
296      *
297      * @see #setDefaultModletLocation(java.lang.String)
298      */
299     public static String getDefaultModletLocation()
300     {
301         if ( defaultModletLocation == null )
302         {
303             defaultModletLocation = System.getProperty(
304                 DEFAULT_MODLET_LOCATION_PROPERTY_NAME, DEFAULT_MODLET_LOCATION );
305 
306         }
307 
308         return defaultModletLocation;
309     }
310 
311     /**
312      * Sets the default location searched for {@code Modlet} resources.
313      *
314      * @param value The new default location to search for {@code Modlet} resources or {@code null}.
315      *
316      * @see #getDefaultModletLocation()
317      */
318     public static void setDefaultModletLocation( final String value )
319     {
320         defaultModletLocation = value;
321     }
322 
323     /**
324      * Gets the location searched for {@code Modlet} resources.
325      *
326      * @return The location searched for {@code Modlet} resources.
327      *
328      * @see #getDefaultModletLocation()
329      * @see #setModletLocation(java.lang.String)
330      */
331     public final String getModletLocation()
332     {
333         if ( this.modletLocation == null )
334         {
335             this.modletLocation = getDefaultModletLocation();
336         }
337 
338         return this.modletLocation;
339     }
340 
341     /**
342      * Sets the location searched for {@code Modlet} resources.
343      *
344      * @param value The new location to search for {@code Modlet} resources or {@code null}.
345      *
346      * @see #getModletLocation()
347      */
348     public final void setModletLocation( final String value )
349     {
350         this.modletLocation = value;
351     }
352 
353     /**
354      * Gets a flag indicating the provider is validating resources by default.
355      * <p>
356      * The default validating flag is controlled by system property
357      * {@code org.jomc.modlet.DefaultModletProvider.defaultValidating} holding a value indicating the provider is
358      * validating resources by default. If that property is not set, the {@code true} default is returned.
359      * </p>
360      *
361      * @return {@code true}, if the provider is validating resources by default; {@code false}, if the provider is not
362      * validating resources by default.
363      *
364      * @see #isValidating()
365      * @see #setDefaultValidating(java.lang.Boolean)
366      *
367      * @since 1.2
368      */
369     public static boolean isDefaultValidating()
370     {
371         if ( defaultValidating == null )
372         {
373             defaultValidating = Boolean.valueOf( System.getProperty(
374                 DEFAULT_VALIDATING_PROPERTY_NAME, Boolean.toString( DEFAULT_VALIDATING ) ) );
375 
376         }
377 
378         return defaultValidating;
379     }
380 
381     /**
382      * Sets the flag indicating the provider is validating resources by default.
383      *
384      * @param value The new value of the flag indicating the provider is validating resources by default or
385      * {@code null}.
386      *
387      * @see #isDefaultValidating()
388      *
389      * @since 1.2
390      */
391     public static void setDefaultValidating( final Boolean value )
392     {
393         defaultValidating = value;
394     }
395 
396     /**
397      * Gets a flag indicating the provider is validating resources.
398      *
399      * @return {@code true}, if the provider is validating resources; {@code false}, if the provider is not validating
400      * resources.
401      *
402      * @see #isDefaultValidating()
403      * @see #setValidating(java.lang.Boolean)
404      *
405      * @since 1.2
406      */
407     public final boolean isValidating()
408     {
409         if ( this.validating == null )
410         {
411             this.validating = isDefaultValidating();
412         }
413 
414         return this.validating;
415     }
416 
417     /**
418      * Sets the flag indicating the provider is validating resources.
419      *
420      * @param value The new value of the flag indicating the provider is validating resources or {@code null}.
421      *
422      * @see #isValidating()
423      *
424      * @since 1.2
425      */
426     public final void setValidating( final Boolean value )
427     {
428         this.validating = value;
429     }
430 
431     /**
432      * Gets the default ordinal number of the provider.
433      * <p>
434      * The default ordinal number is controlled by system property
435      * {@code org.jomc.modlet.DefaultModletProvider.defaultOrdinal} holding the default ordinal number of the provider.
436      * If that property is not set, the {@code 0} default is returned.
437      * </p>
438      *
439      * @return The default ordinal number of the provider.
440      *
441      * @see #setDefaultOrdinal(java.lang.Integer)
442      *
443      * @since 1.6
444      */
445     public static int getDefaultOrdinal()
446     {
447         if ( defaultOrdinal == null )
448         {
449             defaultOrdinal = Integer.getInteger( DEFAULT_ORDINAL_PROPERTY_NAME, DEFAULT_ORDINAL );
450         }
451 
452         return defaultOrdinal;
453     }
454 
455     /**
456      * Sets the default ordinal number of the provider.
457      *
458      * @param value The new default ordinal number of the provider or {@code null}.
459      *
460      * @see #getDefaultOrdinal()
461      *
462      * @since 1.6
463      */
464     public static void setDefaultOrdinal( final Integer value )
465     {
466         defaultOrdinal = value;
467     }
468 
469     /**
470      * Gets the ordinal number of the provider.
471      *
472      * @return The ordinal number of the provider.
473      *
474      * @see #getDefaultOrdinal()
475      * @see #setOrdinal(java.lang.Integer)
476      *
477      * @since 1.6
478      */
479     public final int getOrdinal()
480     {
481         if ( this.ordinal == null )
482         {
483             this.ordinal = getDefaultOrdinal();
484         }
485 
486         return this.ordinal;
487     }
488 
489     /**
490      * Sets the ordinal number of the provider.
491      *
492      * @param value The new ordinal number of the provider or {@code null}.
493      *
494      * @see #getOrdinal()
495      *
496      * @since 1.6
497      */
498     public final void setOrdinal( final Integer value )
499     {
500         this.ordinal = value;
501     }
502 
503     /**
504      * Searches a given context for {@code Modlets}.
505      *
506      * @param context The context to search for {@code Modlets}.
507      * @param location The location to search at.
508      *
509      * @return The {@code Modlets} found at {@code location} in {@code context} or {@code null}, if no {@code Modlets}
510      * are found.
511      *
512      * @throws NullPointerException if {@code context} or {@code location} is {@code null}.
513      * @throws ModelException if searching the context fails.
514      *
515      * @see #isValidating()
516      * @see #VALIDATING_ATTRIBUTE_NAME
517      */
518     public Modlets findModlets( final ModelContext context, final String location ) throws ModelException
519     {
520         if ( context == null )
521         {
522             throw new NullPointerException( "context" );
523         }
524         if ( location == null )
525         {
526             throw new NullPointerException( "location" );
527         }
528 
529         URL url = null;
530         try
531         {
532             boolean contextValidating = this.isValidating();
533             if ( DEFAULT_VALIDATING == contextValidating
534                      && context.getAttribute( VALIDATING_ATTRIBUTE_NAME ) instanceof Boolean )
535             {
536                 contextValidating = (Boolean) context.getAttribute( VALIDATING_ATTRIBUTE_NAME );
537             }
538 
539             final Modlets modlets = new Modlets();
540             final long t0 = System.nanoTime();
541             final Enumeration<URL> modletResourceEnumeration = context.findResources( location );
542             final Set<URI> modletResources = new HashSet<URI>();
543             while ( modletResourceEnumeration.hasMoreElements() )
544             {
545                 modletResources.add( modletResourceEnumeration.nextElement().toURI() );
546             }
547 
548             if ( !modletResources.isEmpty() )
549             {
550                 if ( context.getExecutorService() != null && modletResources.size() > 1 )
551                 {
552                     final JAXBContext ctx = context.createContext( ModletObject.MODEL_PUBLIC_ID );
553                     final javax.xml.validation.Schema schema = context.createSchema( ModletObject.MODEL_PUBLIC_ID );
554                     final ThreadLocal<Unmarshaller> threadLocalUnmarshaller = new ThreadLocal<Unmarshaller>();
555                     final List<Callable<Object>> tasks = new ArrayList<Callable<Object>>( modletResources.size() );
556 
557                     class UnmarshalTask implements Callable<Object>
558                     {
559 
560                         final URI resource;
561 
562                         final boolean validating;
563 
564                         UnmarshalTask( final URI resource, final boolean validating )
565                         {
566                             super();
567                             this.resource = resource;
568                             this.validating = validating;
569                         }
570 
571                         public Object call() throws ModelException, JAXBException, MalformedURLException
572                         {
573                             Unmarshaller unmarshaller = threadLocalUnmarshaller.get();
574                             if ( unmarshaller == null )
575                             {
576                                 unmarshaller = ctx.createUnmarshaller();
577 
578                                 if ( this.validating )
579                                 {
580                                     unmarshaller.setSchema( schema );
581                                 }
582 
583                                 threadLocalUnmarshaller.set( unmarshaller );
584                             }
585 
586                             return unmarshaller.unmarshal( this.resource.toURL() );
587                         }
588 
589                     }
590 
591                     for ( final URI modletResoure : modletResources )
592                     {
593                         tasks.add( new UnmarshalTask( modletResoure, contextValidating ) );
594                     }
595 
596                     for ( final Future<Object> task : context.getExecutorService().invokeAll( tasks ) )
597                     {
598                         Object content = task.get();
599                         if ( content instanceof JAXBElement<?> )
600                         {
601                             content = ( (JAXBElement<?>) content ).getValue();
602                         }
603 
604                         if ( content instanceof Modlet )
605                         {
606                             modlets.getModlet().add( (Modlet) content );
607                         }
608                         else if ( content instanceof Modlets )
609                         {
610                             modlets.getModlet().addAll( ( (Modlets) content ).getModlet() );
611                         }
612                     }
613                 }
614                 else
615                 {
616                     final JAXBContext ctx = context.createContext( ModletObject.MODEL_PUBLIC_ID );
617                     final Unmarshaller u = ctx.createUnmarshaller();
618 
619                     if ( contextValidating )
620                     {
621                         u.setSchema( context.createSchema( ModletObject.MODEL_PUBLIC_ID ) );
622                     }
623 
624                     for ( final URI modletResource : modletResources )
625                     {
626                         url = modletResource.toURL();
627                         Object content = u.unmarshal( url );
628                         if ( content instanceof JAXBElement<?> )
629                         {
630                             content = ( (JAXBElement<?>) content ).getValue();
631                         }
632 
633                         if ( content instanceof Modlet )
634                         {
635                             modlets.getModlet().add( (Modlet) content );
636                         }
637                         else if ( content instanceof Modlets )
638                         {
639                             modlets.getModlet().addAll( ( (Modlets) content ).getModlet() );
640                         }
641                     }
642                 }
643             }
644 
645             if ( context.isLoggable( Level.FINE ) )
646             {
647                 context.log( Level.FINE, getMessage( "contextReport",
648                                                      modlets.getModlet().size(),
649                                                      location, System.nanoTime() - t0 ), null );
650 
651             }
652 
653             return modlets.getModlet().isEmpty() ? null : modlets;
654         }
655         catch ( final CancellationException e )
656         {
657             throw new ModelException( getMessage( e ), e );
658         }
659         catch ( final InterruptedException e )
660         {
661             throw new ModelException( getMessage( e ), e );
662         }
663         catch ( final URISyntaxException e )
664         {
665             throw new ModelException( getMessage( e ), e );
666         }
667         catch ( final MalformedURLException e )
668         {
669             throw new ModelException( getMessage( e ), e );
670         }
671         catch ( final UnmarshalException e )
672         {
673             String message = getMessage( e );
674             if ( message == null && e.getLinkedException() != null )
675             {
676                 message = getMessage( e.getLinkedException() );
677             }
678 
679             if ( url != null )
680             {
681                 message = getMessage( "unmarshalException", url.toExternalForm(),
682                                       message != null ? " " + message : "" );
683 
684             }
685 
686             throw new ModelException( message, e );
687         }
688         catch ( final JAXBException e )
689         {
690             String message = getMessage( e );
691             if ( message == null && e.getLinkedException() != null )
692             {
693                 message = getMessage( e.getLinkedException() );
694             }
695 
696             throw new ModelException( message, e );
697         }
698         catch ( final ExecutionException e )
699         {
700             if ( e.getCause() instanceof ModelException )
701             {
702                 throw (ModelException) e.getCause();
703             }
704             else if ( e.getCause() instanceof JAXBException )
705             {
706                 String message = getMessage( e.getCause() );
707                 if ( message == null && ( (JAXBException) e.getCause() ).getLinkedException() != null )
708                 {
709                     message = getMessage( ( (JAXBException) e.getCause() ).getLinkedException() );
710                 }
711 
712                 throw new ModelException( message, e.getCause() );
713             }
714             else if ( e.getCause() instanceof RuntimeException )
715             {
716                 // The fork-join framework breaks the exception handling contract of Callable by re-throwing any
717                 // exception caught using a runtime exception.
718                 if ( e.getCause().getCause() instanceof ModelException )
719                 {
720                     throw (ModelException) e.getCause().getCause();
721                 }
722                 else if ( e.getCause().getCause() instanceof JAXBException )
723                 {
724                     String message = getMessage( e.getCause().getCause() );
725                     if ( message == null && ( (JAXBException) e.getCause().getCause() ).getLinkedException() != null )
726                     {
727                         message = getMessage( ( (JAXBException) e.getCause().getCause() ).getLinkedException() );
728                     }
729 
730                     throw new ModelException( message, e.getCause().getCause() );
731                 }
732                 else if ( e.getCause().getCause() instanceof MalformedURLException )
733                 {
734                     throw new ModelException( getMessage( e.getCause().getCause() ), e.getCause().getCause() );
735                 }
736                 else if ( e.getCause().getCause() instanceof RuntimeException )
737                 {
738                     throw (RuntimeException) e.getCause().getCause();
739                 }
740                 else if ( e.getCause().getCause() instanceof Error )
741                 {
742                     throw (Error) e.getCause().getCause();
743                 }
744                 else if ( e.getCause().getCause() instanceof Exception )
745                 {
746                     // Checked exception not declared to be thrown by the Callable's 'call' method.
747                     throw new UndeclaredThrowableException( e.getCause().getCause() );
748                 }
749                 else
750                 {
751                     throw (RuntimeException) e.getCause();
752                 }
753             }
754             else if ( e.getCause() instanceof Error )
755             {
756                 throw (Error) e.getCause();
757             }
758             else
759             {
760                 // Checked exception not declared to be thrown by the Callable's 'call' method.
761                 throw new UndeclaredThrowableException( e.getCause() );
762             }
763         }
764     }
765 
766     /**
767      * {@inheritDoc}
768      *
769      * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider
770      * is disabled.
771      *
772      * @see #isEnabled()
773      * @see #getModletLocation()
774      * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
775      * @see #ENABLED_ATTRIBUTE_NAME
776      * @see #MODLET_LOCATION_ATTRIBUTE_NAME
777      * @deprecated As of JOMC 1.6, this method has been replaced by {@link #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)}.
778      * This method will be removed in JOMC 2.0.
779      */
780     @Deprecated
781     public Modlets findModlets( final ModelContext context ) throws ModelException
782     {
783         if ( context == null )
784         {
785             throw new NullPointerException( "context" );
786         }
787 
788         return this.findModlets( context, new Modlets() );
789     }
790 
791     /**
792      * {@inheritDoc}
793      *
794      * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider
795      * is disabled.
796      *
797      * @see #isEnabled()
798      * @see #getModletLocation()
799      * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
800      * @see #ENABLED_ATTRIBUTE_NAME
801      * @see #MODLET_LOCATION_ATTRIBUTE_NAME
802      * @since 1.6
803      */
804     public Modlets findModlets( final ModelContext context, final Modlets modlets ) throws ModelException
805     {
806         if ( context == null )
807         {
808             throw new NullPointerException( "context" );
809         }
810         if ( modlets == null )
811         {
812             throw new NullPointerException( "context" );
813         }
814 
815         Modlets provided = null;
816 
817         boolean contextEnabled = this.isEnabled();
818         if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
819         {
820             contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
821         }
822 
823         String contextModletLocation = this.getModletLocation();
824         if ( DEFAULT_MODLET_LOCATION.equals( contextModletLocation )
825                  && context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME ) instanceof String )
826         {
827             contextModletLocation = (String) context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME );
828         }
829 
830         if ( contextEnabled )
831         {
832             final Modlets found = this.findModlets( context, contextModletLocation );
833 
834             if ( found != null )
835             {
836                 provided = modlets.clone();
837                 provided.getModlet().addAll( found.getModlet() );
838             }
839         }
840         else if ( context.isLoggable( Level.FINER ) )
841         {
842             context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName() ), null );
843         }
844 
845         return provided;
846     }
847 
848     private static String getMessage( final String key, final Object... arguments )
849     {
850         return MessageFormat.format( ResourceBundle.getBundle(
851             DefaultModletProvider.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
852 
853     }
854 
855     private static String getMessage( final Throwable t )
856     {
857         return t != null
858                    ? t.getMessage() != null && t.getMessage().trim().length() > 0
859                          ? t.getMessage()
860                          : getMessage( t.getCause() )
861                    : null;
862 
863     }
864 
865 }