001/*
002 *   Copyright (C) 2005 Christian Schulte <cs@schulte.it>
003 *   All rights reserved.
004 *
005 *   Redistribution and use in source and binary forms, with or without
006 *   modification, are permitted provided that the following conditions
007 *   are met:
008 *
009 *     o Redistributions of source code must retain the above copyright
010 *       notice, this list of conditions and the following disclaimer.
011 *
012 *     o Redistributions in binary form must reproduce the above copyright
013 *       notice, this list of conditions and the following disclaimer in
014 *       the documentation and/or other materials provided with the
015 *       distribution.
016 *
017 *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
018 *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
019 *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
020 *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
021 *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
022 *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
023 *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
024 *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
025 *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
026 *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
027 *
028 *   $JOMC: DefaultModletProvider.java 5351 2016-09-05 04:22:17Z schulte $
029 *
030 */
031package org.jomc.modlet;
032
033import java.lang.reflect.UndeclaredThrowableException;
034import java.net.URL;
035import java.text.MessageFormat;
036import java.util.Enumeration;
037import java.util.LinkedList;
038import java.util.List;
039import java.util.ResourceBundle;
040import java.util.concurrent.Callable;
041import java.util.concurrent.CancellationException;
042import java.util.concurrent.ExecutionException;
043import java.util.concurrent.Future;
044import java.util.logging.Level;
045import javax.xml.bind.JAXBContext;
046import javax.xml.bind.JAXBElement;
047import javax.xml.bind.JAXBException;
048import javax.xml.bind.UnmarshalException;
049import javax.xml.bind.Unmarshaller;
050
051/**
052 * Default {@code ModletProvider} implementation.
053 *
054 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
055 * @version $JOMC: DefaultModletProvider.java 5351 2016-09-05 04:22:17Z schulte $
056 * @see ModelContext#findModlets(org.jomc.modlet.Modlets)
057 */
058public class DefaultModletProvider implements ModletProvider
059{
060
061    /**
062     * Constant for the name of the model context attribute backing property {@code enabled}.
063     *
064     * @see #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
065     * @see ModelContext#getAttribute(java.lang.String)
066     * @since 1.2
067     */
068    public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.modlet.DefaultModletProvider.enabledAttribute";
069
070    /**
071     * Constant for the name of the system property controlling property {@code defaultEnabled}.
072     *
073     * @see #isDefaultEnabled()
074     * @since 1.2
075     */
076    private static final String DEFAULT_ENABLED_PROPERTY_NAME =
077        "org.jomc.modlet.DefaultModletProvider.defaultEnabled";
078
079    /**
080     * Default value of the flag indicating the provider is enabled by default.
081     *
082     * @see #isDefaultEnabled()
083     * @since 1.2
084     */
085    private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
086
087    /**
088     * Flag indicating the provider is enabled by default.
089     */
090    private static volatile Boolean defaultEnabled;
091
092    /**
093     * Flag indicating the provider is enabled.
094     */
095    private volatile Boolean enabled;
096
097    /**
098     * Constant for the name of the model context attribute backing property {@code modletLocation}.
099     *
100     * @see #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
101     * @see ModelContext#getAttribute(java.lang.String)
102     * @since 1.2
103     */
104    public static final String MODLET_LOCATION_ATTRIBUTE_NAME =
105        "org.jomc.modlet.DefaultModletProvider.modletLocationAttribute";
106
107    /**
108     * Constant for the name of the system property controlling property {@code defaultModletLocation}.
109     *
110     * @see #getDefaultModletLocation()
111     * @since 1.2
112     */
113    private static final String DEFAULT_MODLET_LOCATION_PROPERTY_NAME =
114        "org.jomc.modlet.DefaultModletProvider.defaultModletLocation";
115
116    /**
117     * Class path location searched for {@code Modlets} by default.
118     *
119     * @see #getDefaultModletLocation()
120     */
121    private static final String DEFAULT_MODLET_LOCATION = "META-INF/jomc-modlet.xml";
122
123    /**
124     * Default {@code Modlet} location.
125     */
126    private static volatile String defaultModletLocation;
127
128    /**
129     * Modlet location of the instance.
130     */
131    private volatile String modletLocation;
132
133    /**
134     * Constant for the name of the model context attribute backing property {@code validating}.
135     *
136     * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
137     * @see ModelContext#getAttribute(java.lang.String)
138     * @since 1.2
139     */
140    public static final String VALIDATING_ATTRIBUTE_NAME =
141        "org.jomc.modlet.DefaultModletProvider.validatingAttribute";
142
143    /**
144     * Constant for the name of the system property controlling property {@code defaultValidating}.
145     *
146     * @see #isDefaultValidating()
147     * @since 1.2
148     */
149    private static final String DEFAULT_VALIDATING_PROPERTY_NAME =
150        "org.jomc.modlet.DefaultModletProvider.defaultValidating";
151
152    /**
153     * Default value of the flag indicating the provider is validating resources by default.
154     *
155     * @see #isDefaultValidating()
156     * @since 1.2
157     */
158    private static final Boolean DEFAULT_VALIDATING = Boolean.TRUE;
159
160    /**
161     * Flag indicating the provider is validating resources by default.
162     *
163     * @since 1.2
164     */
165    private static volatile Boolean defaultValidating;
166
167    /**
168     * Flag indicating the provider is validating resources.
169     *
170     * @since 1.2
171     */
172    private volatile Boolean validating;
173
174    /**
175     * Constant for the name of the system property controlling property {@code defaultOrdinal}.
176     *
177     * @see #getDefaultOrdinal()
178     * @since 1.6
179     */
180    private static final String DEFAULT_ORDINAL_PROPERTY_NAME =
181        "org.jomc.modlet.DefaultModletProvider.defaultOrdinal";
182
183    /**
184     * Default value of the ordinal number of the provider.
185     *
186     * @see #getDefaultOrdinal()
187     * @since 1.6
188     */
189    private static final Integer DEFAULT_ORDINAL = 0;
190
191    /**
192     * Default ordinal number of the provider.
193     *
194     * @since 1.6
195     */
196    private static volatile Integer defaultOrdinal;
197
198    /**
199     * Ordinal number of the provider.
200     *
201     * @since 1.6
202     */
203    private volatile Integer ordinal;
204
205    /**
206     * Creates a new {@code DefaultModletProvider} instance.
207     */
208    public DefaultModletProvider()
209    {
210        super();
211    }
212
213    /**
214     * Gets a flag indicating the provider is enabled by default.
215     * <p>
216     * The default enabled flag is controlled by system property
217     * {@code org.jomc.modlet.DefaultModletProvider.defaultEnabled} holding a value indicating the provider is
218     * enabled by default. If that property is not set, the {@code true} default is returned.
219     * </p>
220     *
221     * @return {@code true}, if the provider is enabled by default; {@code false}, if the provider is disabled by
222     * default.
223     *
224     * @see #isEnabled()
225     * @see #setDefaultEnabled(java.lang.Boolean)
226     */
227    public static boolean isDefaultEnabled()
228    {
229        if ( defaultEnabled == null )
230        {
231            defaultEnabled = Boolean.valueOf( System.getProperty(
232                DEFAULT_ENABLED_PROPERTY_NAME, Boolean.toString( DEFAULT_ENABLED ) ) );
233
234        }
235
236        return defaultEnabled;
237    }
238
239    /**
240     * Sets the flag indicating the provider is enabled by default.
241     *
242     * @param value The new value of the flag indicating the provider is enabled by default or {@code null}.
243     *
244     * @see #isDefaultEnabled()
245     */
246    public static void setDefaultEnabled( final Boolean value )
247    {
248        defaultEnabled = value;
249    }
250
251    /**
252     * Gets a flag indicating the provider is enabled.
253     *
254     * @return {@code true}, if the provider is enabled; {@code false}, if the provider is disabled.
255     *
256     * @see #isDefaultEnabled()
257     * @see #setEnabled(java.lang.Boolean)
258     */
259    public final boolean isEnabled()
260    {
261        if ( this.enabled == null )
262        {
263            this.enabled = isDefaultEnabled();
264        }
265
266        return this.enabled;
267    }
268
269    /**
270     * Sets the flag indicating the provider is enabled.
271     *
272     * @param value The new value of the flag indicating the provider is enabled or {@code null}.
273     *
274     * @see #isEnabled()
275     */
276    public final void setEnabled( final Boolean value )
277    {
278        this.enabled = value;
279    }
280
281    /**
282     * Gets the default location searched for {@code Modlet} resources.
283     * <p>
284     * The default {@code Modlet} location is controlled by system property
285     * {@code org.jomc.modlet.DefaultModletProvider.defaultModletLocation} holding the location to search for
286     * {@code Modlet} resources by default. If that property is not set, the {@code META-INF/jomc-modlet.xml} default is
287     * returned.
288     * </p>
289     *
290     * @return The location searched for {@code Modlet} resources by default.
291     *
292     * @see #setDefaultModletLocation(java.lang.String)
293     */
294    public static String getDefaultModletLocation()
295    {
296        if ( defaultModletLocation == null )
297        {
298            defaultModletLocation = System.getProperty(
299                DEFAULT_MODLET_LOCATION_PROPERTY_NAME, DEFAULT_MODLET_LOCATION );
300
301        }
302
303        return defaultModletLocation;
304    }
305
306    /**
307     * Sets the default location searched for {@code Modlet} resources.
308     *
309     * @param value The new default location to search for {@code Modlet} resources or {@code null}.
310     *
311     * @see #getDefaultModletLocation()
312     */
313    public static void setDefaultModletLocation( final String value )
314    {
315        defaultModletLocation = value;
316    }
317
318    /**
319     * Gets the location searched for {@code Modlet} resources.
320     *
321     * @return The location searched for {@code Modlet} resources.
322     *
323     * @see #getDefaultModletLocation()
324     * @see #setModletLocation(java.lang.String)
325     */
326    public final String getModletLocation()
327    {
328        if ( this.modletLocation == null )
329        {
330            this.modletLocation = getDefaultModletLocation();
331        }
332
333        return this.modletLocation;
334    }
335
336    /**
337     * Sets the location searched for {@code Modlet} resources.
338     *
339     * @param value The new location to search for {@code Modlet} resources or {@code null}.
340     *
341     * @see #getModletLocation()
342     */
343    public final void setModletLocation( final String value )
344    {
345        this.modletLocation = value;
346    }
347
348    /**
349     * Gets a flag indicating the provider is validating resources by default.
350     * <p>
351     * The default validating flag is controlled by system property
352     * {@code org.jomc.modlet.DefaultModletProvider.defaultValidating} holding a value indicating the provider is
353     * validating resources by default. If that property is not set, the {@code true} default is returned.
354     * </p>
355     *
356     * @return {@code true}, if the provider is validating resources by default; {@code false}, if the provider is not
357     * validating resources by default.
358     *
359     * @see #isValidating()
360     * @see #setDefaultValidating(java.lang.Boolean)
361     *
362     * @since 1.2
363     */
364    public static boolean isDefaultValidating()
365    {
366        if ( defaultValidating == null )
367        {
368            defaultValidating = Boolean.valueOf( System.getProperty(
369                DEFAULT_VALIDATING_PROPERTY_NAME, Boolean.toString( DEFAULT_VALIDATING ) ) );
370
371        }
372
373        return defaultValidating;
374    }
375
376    /**
377     * Sets the flag indicating the provider is validating resources by default.
378     *
379     * @param value The new value of the flag indicating the provider is validating resources by default or
380     * {@code null}.
381     *
382     * @see #isDefaultValidating()
383     *
384     * @since 1.2
385     */
386    public static void setDefaultValidating( final Boolean value )
387    {
388        defaultValidating = value;
389    }
390
391    /**
392     * Gets a flag indicating the provider is validating resources.
393     *
394     * @return {@code true}, if the provider is validating resources; {@code false}, if the provider is not validating
395     * resources.
396     *
397     * @see #isDefaultValidating()
398     * @see #setValidating(java.lang.Boolean)
399     *
400     * @since 1.2
401     */
402    public final boolean isValidating()
403    {
404        if ( this.validating == null )
405        {
406            this.validating = isDefaultValidating();
407        }
408
409        return this.validating;
410    }
411
412    /**
413     * Sets the flag indicating the provider is validating resources.
414     *
415     * @param value The new value of the flag indicating the provider is validating resources or {@code null}.
416     *
417     * @see #isValidating()
418     *
419     * @since 1.2
420     */
421    public final void setValidating( final Boolean value )
422    {
423        this.validating = value;
424    }
425
426    /**
427     * Gets the default ordinal number of the provider.
428     * <p>
429     * The default ordinal number is controlled by system property
430     * {@code org.jomc.modlet.DefaultModletProvider.defaultOrdinal} holding the default ordinal number of the provider.
431     * If that property is not set, the {@code 0} default is returned.
432     * </p>
433     *
434     * @return The default ordinal number of the provider.
435     *
436     * @see #setDefaultOrdinal(java.lang.Integer)
437     *
438     * @since 1.6
439     */
440    public static int getDefaultOrdinal()
441    {
442        if ( defaultOrdinal == null )
443        {
444            defaultOrdinal = Integer.getInteger( DEFAULT_ORDINAL_PROPERTY_NAME, DEFAULT_ORDINAL );
445        }
446
447        return defaultOrdinal;
448    }
449
450    /**
451     * Sets the default ordinal number of the provider.
452     *
453     * @param value The new default ordinal number of the provider or {@code null}.
454     *
455     * @see #getDefaultOrdinal()
456     *
457     * @since 1.6
458     */
459    public static void setDefaultOrdinal( final Integer value )
460    {
461        defaultOrdinal = value;
462    }
463
464    /**
465     * Gets the ordinal number of the provider.
466     *
467     * @return The ordinal number of the provider.
468     *
469     * @see #getDefaultOrdinal()
470     * @see #setOrdinal(java.lang.Integer)
471     *
472     * @since 1.6
473     */
474    public final int getOrdinal()
475    {
476        if ( this.ordinal == null )
477        {
478            this.ordinal = getDefaultOrdinal();
479        }
480
481        return this.ordinal;
482    }
483
484    /**
485     * Sets the ordinal number of the provider.
486     *
487     * @param value The new ordinal number of the provider or {@code null}.
488     *
489     * @see #getOrdinal()
490     *
491     * @since 1.6
492     */
493    public final void setOrdinal( final Integer value )
494    {
495        this.ordinal = value;
496    }
497
498    /**
499     * Searches a given context for {@code Modlets}.
500     *
501     * @param context The context to search for {@code Modlets}.
502     * @param location The location to search at.
503     *
504     * @return The {@code Modlets} found at {@code location} in {@code context} or {@code null}, if no {@code Modlets}
505     * are found.
506     *
507     * @throws NullPointerException if {@code context} or {@code location} is {@code null}.
508     * @throws ModelException if searching the context fails.
509     *
510     * @see #isValidating()
511     * @see #VALIDATING_ATTRIBUTE_NAME
512     */
513    public Modlets findModlets( final ModelContext context, final String location ) throws ModelException
514    {
515        if ( context == null )
516        {
517            throw new NullPointerException( "context" );
518        }
519        if ( location == null )
520        {
521            throw new NullPointerException( "location" );
522        }
523
524        URL url = null;
525        try
526        {
527            boolean contextValidating = this.isValidating();
528            if ( DEFAULT_VALIDATING == contextValidating
529                     && context.getAttribute( VALIDATING_ATTRIBUTE_NAME ) instanceof Boolean )
530            {
531                contextValidating = (Boolean) context.getAttribute( VALIDATING_ATTRIBUTE_NAME );
532            }
533
534            final Modlets modlets = new Modlets();
535            final long t0 = System.nanoTime();
536            final Enumeration<URL> modletResourceEnumeration = context.findResources( location );
537            final JAXBContext ctx = context.createContext( ModletObject.MODEL_PUBLIC_ID );
538            final javax.xml.validation.Schema schema = contextValidating
539                                                           ? context.createSchema( ModletObject.MODEL_PUBLIC_ID )
540                                                           : null;
541
542            final ThreadLocal<Unmarshaller> threadLocalUnmarshaller = new ThreadLocal<Unmarshaller>();
543
544            class UnmarshalTask implements Callable<Modlets>
545            {
546
547                final URL resource;
548
549                final javax.xml.validation.Schema schema;
550
551                UnmarshalTask( final URL resource, final javax.xml.validation.Schema schema )
552                {
553                    super();
554                    this.resource = resource;
555                    this.schema = schema;
556                }
557
558                public Modlets call() throws ModelException
559                {
560                    try
561                    {
562                        final Modlets modlets = new Modlets();
563                        Unmarshaller unmarshaller = threadLocalUnmarshaller.get();
564                        if ( unmarshaller == null )
565                        {
566                            unmarshaller = ctx.createUnmarshaller();
567                            unmarshaller.setSchema( this.schema );
568
569                            threadLocalUnmarshaller.set( unmarshaller );
570                        }
571
572                        Object content = unmarshaller.unmarshal( this.resource );
573                        if ( content instanceof JAXBElement<?> )
574                        {
575                            content = ( (JAXBElement<?>) content ).getValue();
576                        }
577
578                        if ( content instanceof Modlet )
579                        {
580                            modlets.getModlet().add( (Modlet) content );
581                        }
582                        else if ( content instanceof Modlets )
583                        {
584                            modlets.getModlet().addAll( ( (Modlets) content ).getModlet() );
585                        }
586
587                        return modlets;
588                    }
589                    catch ( final UnmarshalException e )
590                    {
591                        String message = getMessage( e );
592                        if ( message == null && e.getLinkedException() != null )
593                        {
594                            message = getMessage( e.getLinkedException() );
595                        }
596
597                        message = getMessage( "unmarshalException", this.resource.toExternalForm(),
598                                              message != null ? " " + message : "" );
599
600                        throw new ModelException( message, e );
601                    }
602                    catch ( final JAXBException e )
603                    {
604                        String message = getMessage( e );
605                        if ( message == null && e.getLinkedException() != null )
606                        {
607                            message = getMessage( e.getLinkedException() );
608                        }
609
610                        throw new ModelException( message, e );
611                    }
612                }
613
614            }
615
616            final List<UnmarshalTask> tasks = new LinkedList<UnmarshalTask>();
617
618            while ( modletResourceEnumeration.hasMoreElements() )
619            {
620                tasks.add( new UnmarshalTask( modletResourceEnumeration.nextElement(), schema ) );
621            }
622
623            if ( context.getExecutorService() != null && tasks.size() > 1 )
624            {
625                for ( final Future<Modlets> task : context.getExecutorService().invokeAll( tasks ) )
626                {
627                    modlets.getModlet().addAll( task.get().getModlet() );
628                }
629            }
630            else
631            {
632                for ( final UnmarshalTask task : tasks )
633                {
634                    modlets.getModlet().addAll( task.call().getModlet() );
635                }
636            }
637
638            if ( context.isLoggable( Level.FINE ) )
639            {
640                context.log( Level.FINE, getMessage( "contextReport",
641                                                     modlets.getModlet().size(),
642                                                     location, System.nanoTime() - t0 ), null );
643
644            }
645
646            return modlets.getModlet().isEmpty() ? null : modlets;
647        }
648        catch ( final CancellationException e )
649        {
650            throw new ModelException( getMessage( e ), e );
651        }
652        catch ( final InterruptedException e )
653        {
654            throw new ModelException( getMessage( e ), e );
655        }
656        catch ( final ExecutionException e )
657        {
658            if ( e.getCause() instanceof ModelException )
659            {
660                throw (ModelException) e.getCause();
661            }
662            else if ( e.getCause() instanceof RuntimeException )
663            {
664                // The fork-join framework breaks the exception handling contract of Callable by re-throwing any
665                // exception caught using a runtime exception.
666                if ( e.getCause().getCause() instanceof ModelException )
667                {
668                    throw (ModelException) e.getCause().getCause();
669                }
670                else if ( e.getCause().getCause() instanceof RuntimeException )
671                {
672                    throw (RuntimeException) e.getCause().getCause();
673                }
674                else if ( e.getCause().getCause() instanceof Error )
675                {
676                    throw (Error) e.getCause().getCause();
677                }
678                else if ( e.getCause().getCause() instanceof Exception )
679                {
680                    // Checked exception not declared to be thrown by the Callable's 'call' method.
681                    throw new UndeclaredThrowableException( e.getCause().getCause() );
682                }
683                else
684                {
685                    throw (RuntimeException) e.getCause();
686                }
687            }
688            else if ( e.getCause() instanceof Error )
689            {
690                throw (Error) e.getCause();
691            }
692            else
693            {
694                // Checked exception not declared to be thrown by the Callable's 'call' method.
695                throw new UndeclaredThrowableException( e.getCause() );
696            }
697        }
698    }
699
700    /**
701     * {@inheritDoc}
702     *
703     * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider
704     * is disabled.
705     *
706     * @see #isEnabled()
707     * @see #getModletLocation()
708     * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
709     * @see #ENABLED_ATTRIBUTE_NAME
710     * @see #MODLET_LOCATION_ATTRIBUTE_NAME
711     * @deprecated As of JOMC 1.6, this method has been replaced by {@link #findModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)}.
712     * This method will be removed in JOMC 2.0.
713     */
714    @Deprecated
715    public Modlets findModlets( final ModelContext context ) throws ModelException
716    {
717        if ( context == null )
718        {
719            throw new NullPointerException( "context" );
720        }
721
722        return this.findModlets( context, new Modlets() );
723    }
724
725    /**
726     * {@inheritDoc}
727     *
728     * @return The {@code Modlets} found in the context or {@code null}, if no {@code Modlets} are found or the provider
729     * is disabled.
730     *
731     * @see #isEnabled()
732     * @see #getModletLocation()
733     * @see #findModlets(org.jomc.modlet.ModelContext, java.lang.String)
734     * @see #ENABLED_ATTRIBUTE_NAME
735     * @see #MODLET_LOCATION_ATTRIBUTE_NAME
736     * @since 1.6
737     */
738    public Modlets findModlets( final ModelContext context, final Modlets modlets ) throws ModelException
739    {
740        if ( context == null )
741        {
742            throw new NullPointerException( "context" );
743        }
744        if ( modlets == null )
745        {
746            throw new NullPointerException( "context" );
747        }
748
749        Modlets provided = null;
750
751        boolean contextEnabled = this.isEnabled();
752        if ( DEFAULT_ENABLED == contextEnabled && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
753        {
754            contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
755        }
756
757        String contextModletLocation = this.getModletLocation();
758        if ( DEFAULT_MODLET_LOCATION.equals( contextModletLocation )
759                 && context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME ) instanceof String )
760        {
761            contextModletLocation = (String) context.getAttribute( MODLET_LOCATION_ATTRIBUTE_NAME );
762        }
763
764        if ( contextEnabled )
765        {
766            final Modlets found = this.findModlets( context, contextModletLocation );
767
768            if ( found != null )
769            {
770                provided = modlets.clone();
771                provided.getModlet().addAll( found.getModlet() );
772            }
773        }
774        else if ( context.isLoggable( Level.FINER ) )
775        {
776            context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName() ), null );
777        }
778
779        return provided;
780    }
781
782    private static String getMessage( final String key, final Object... arguments )
783    {
784        return MessageFormat.format( ResourceBundle.getBundle(
785            DefaultModletProvider.class.getName().replace( '.', '/' ) ).getString( key ), arguments );
786
787    }
788
789    private static String getMessage( final Throwable t )
790    {
791        return t != null
792                   ? t.getMessage() != null && t.getMessage().trim().length() > 0
793                         ? t.getMessage()
794                         : getMessage( t.getCause() )
795                   : null;
796
797    }
798
799}