001/*
002 *   Copyright (C) 2014 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: DefaultModletProcessor.java 5293 2016-08-29 17:41:51Z schulte $
029 *
030 */
031package org.jomc.modlet;
032
033import java.io.ByteArrayInputStream;
034import java.io.ByteArrayOutputStream;
035import java.io.IOException;
036import java.lang.reflect.UndeclaredThrowableException;
037import java.net.URI;
038import java.net.URISyntaxException;
039import java.net.URL;
040import java.text.MessageFormat;
041import java.util.ArrayList;
042import java.util.Enumeration;
043import java.util.HashSet;
044import java.util.LinkedList;
045import java.util.List;
046import java.util.Locale;
047import java.util.Map;
048import java.util.Properties;
049import java.util.ResourceBundle;
050import java.util.Set;
051import java.util.concurrent.Callable;
052import java.util.concurrent.CancellationException;
053import java.util.concurrent.ExecutionException;
054import java.util.concurrent.Future;
055import java.util.logging.Level;
056import javax.xml.bind.JAXBContext;
057import javax.xml.bind.JAXBElement;
058import javax.xml.bind.JAXBException;
059import javax.xml.bind.util.JAXBResult;
060import javax.xml.bind.util.JAXBSource;
061import javax.xml.transform.ErrorListener;
062import javax.xml.transform.Transformer;
063import javax.xml.transform.TransformerConfigurationException;
064import javax.xml.transform.TransformerException;
065import javax.xml.transform.TransformerFactory;
066import javax.xml.transform.stream.StreamSource;
067
068/**
069 * Default {@code ModletProcessor} implementation.
070 *
071 * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
072 * @version $JOMC: DefaultModletProcessor.java 5293 2016-08-29 17:41:51Z schulte $
073 * @see ModelContext#processModlets(org.jomc.modlet.Modlets)
074 * @since 1.6
075 */
076public class DefaultModletProcessor implements ModletProcessor
077{
078
079    /**
080     * Constant for the name of the model context attribute backing property {@code enabled}.
081     *
082     * @see #processModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
083     * @see ModelContext#getAttribute(java.lang.String)
084     */
085    public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.modlet.DefaultModletProcessor.enabledAttribute";
086
087    /**
088     * Constant for the name of the system property controlling property {@code defaultEnabled}.
089     *
090     * @see #isDefaultEnabled()
091     */
092    private static final String DEFAULT_ENABLED_PROPERTY_NAME =
093        "org.jomc.modlet.DefaultModletProcessor.defaultEnabled";
094
095    /**
096     * Default value of the flag indicating the processor is enabled by default.
097     *
098     * @see #isDefaultEnabled()
099     */
100    private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;
101
102    /**
103     * Flag indicating the processor is enabled by default.
104     */
105    private static volatile Boolean defaultEnabled;
106
107    /**
108     * Flag indicating the processor is enabled.
109     */
110    private volatile Boolean enabled;
111
112    /**
113     * Constant for the name of the system property controlling property {@code defaultOrdinal}.
114     *
115     * @see #getDefaultOrdinal()
116     */
117    private static final String DEFAULT_ORDINAL_PROPERTY_NAME =
118        "org.jomc.modlet.DefaultModletProcessor.defaultOrdinal";
119
120    /**
121     * Default value of the ordinal number of the processor.
122     *
123     * @see #getDefaultOrdinal()
124     */
125    private static final Integer DEFAULT_ORDINAL = 0;
126
127    /**
128     * Default ordinal number of the processor.
129     */
130    private static volatile Integer defaultOrdinal;
131
132    /**
133     * Ordinal number of the processor.
134     */
135    private volatile Integer ordinal;
136
137    /**
138     * Constant for the name of the model context attribute backing property {@code transformerLocation}.
139     *
140     * @see #processModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
141     * @see ModelContext#getAttribute(java.lang.String)
142     * @since 1.2
143     */
144    public static final String TRANSFORMER_LOCATION_ATTRIBUTE_NAME =
145        "org.jomc.modlet.DefaultModletProcessor.transformerLocationAttribute";
146
147    /**
148     * Constant for the name of the system property controlling property {@code defaultTransformerLocation}.
149     *
150     * @see #getDefaultTransformerLocation()
151     */
152    private static final String DEFAULT_TRANSFORMER_LOCATION_PROPERTY_NAME =
153        "org.jomc.modlet.DefaultModletProcessor.defaultTransformerLocation";
154
155    /**
156     * Class path location searched for transformers by default.
157     *
158     * @see #getDefaultTransformerLocation()
159     */
160    private static final String DEFAULT_TRANSFORMER_LOCATION = "META-INF/jomc-modlet.xsl";
161
162    /**
163     * Default transformer location.
164     */
165    private static volatile String defaultTransformerLocation;
166
167    /**
168     * Transformer location of the instance.
169     */
170    private volatile String transformerLocation;
171
172    /**
173     * Creates a new {@code DefaultModletProcessor} instance.
174     */
175    public DefaultModletProcessor()
176    {
177        super();
178    }
179
180    /**
181     * Gets a flag indicating the processor is enabled by default.
182     * <p>
183     * The default enabled flag is controlled by system property
184     * {@code org.jomc.modlet.DefaultModletProcessor.defaultEnabled} holding a value indicating the processor is
185     * enabled by default. If that property is not set, the {@code true} default is returned.
186     * </p>
187     *
188     * @return {@code true}, if the processor is enabled by default; {@code false}, if the processor is disabled by
189     * default.
190     *
191     * @see #isEnabled()
192     * @see #setDefaultEnabled(java.lang.Boolean)
193     */
194    public static boolean isDefaultEnabled()
195    {
196        if ( defaultEnabled == null )
197        {
198            defaultEnabled = Boolean.valueOf( System.getProperty(
199                DEFAULT_ENABLED_PROPERTY_NAME, Boolean.toString( DEFAULT_ENABLED ) ) );
200
201        }
202
203        return defaultEnabled;
204    }
205
206    /**
207     * Sets the flag indicating the processor is enabled by default.
208     *
209     * @param value The new value of the flag indicating the processor is enabled by default or {@code null}.
210     *
211     * @see #isDefaultEnabled()
212     */
213    public static void setDefaultEnabled( final Boolean value )
214    {
215        defaultEnabled = value;
216    }
217
218    /**
219     * Gets a flag indicating the processor is enabled.
220     *
221     * @return {@code true}, if the processor is enabled; {@code false}, if the processor is disabled.
222     *
223     * @see #isDefaultEnabled()
224     * @see #setEnabled(java.lang.Boolean)
225     */
226    public final boolean isEnabled()
227    {
228        if ( this.enabled == null )
229        {
230            this.enabled = isDefaultEnabled();
231        }
232
233        return this.enabled;
234    }
235
236    /**
237     * Sets the flag indicating the processor is enabled.
238     *
239     * @param value The new value of the flag indicating the processor is enabled or {@code null}.
240     *
241     * @see #isEnabled()
242     */
243    public final void setEnabled( final Boolean value )
244    {
245        this.enabled = value;
246    }
247
248    /**
249     * Gets the default ordinal number of the processor.
250     * <p>
251     * The default ordinal number is controlled by system property
252     * {@code org.jomc.modlet.DefaultModletProvider.defaultOrdinal} holding the default ordinal number of the processor.
253     * If that property is not set, the {@code 0} default is returned.
254     * </p>
255     *
256     * @return The default ordinal number of the processor.
257     *
258     * @see #setDefaultOrdinal(java.lang.Integer)
259     */
260    public static int getDefaultOrdinal()
261    {
262        if ( defaultOrdinal == null )
263        {
264            defaultOrdinal = Integer.getInteger( DEFAULT_ORDINAL_PROPERTY_NAME, DEFAULT_ORDINAL );
265        }
266
267        return defaultOrdinal;
268    }
269
270    /**
271     * Sets the default ordinal number of the processor.
272     *
273     * @param value The new default ordinal number of the processor or {@code null}.
274     *
275     * @see #getDefaultOrdinal()
276     */
277    public static void setDefaultOrdinal( final Integer value )
278    {
279        defaultOrdinal = value;
280    }
281
282    /**
283     * Gets the ordinal number of the processor.
284     *
285     * @return The ordinal number of the processor.
286     *
287     * @see #getDefaultOrdinal()
288     * @see #setOrdinal(java.lang.Integer)
289     */
290    public final int getOrdinal()
291    {
292        if ( this.ordinal == null )
293        {
294            this.ordinal = getDefaultOrdinal();
295        }
296
297        return this.ordinal;
298    }
299
300    /**
301     * Sets the ordinal number of the processor.
302     *
303     * @param value The new ordinal number of the processor or {@code null}.
304     *
305     * @see #getOrdinal()
306     */
307    public final void setOrdinal( final Integer value )
308    {
309        this.ordinal = value;
310    }
311
312    /**
313     * Gets the default location searched for transformer resources.
314     * <p>
315     * The default transformer location is controlled by system property
316     * {@code org.jomc.modlet.DefaultModletProcessor.defaultTransformerLocation} holding the location to search
317     * for transformer resources by default. If that property is not set, the {@code META-INF/jomc-modlet.xsl} default
318     * is returned.
319     * </p>
320     *
321     * @return The location searched for transformer resources by default.
322     *
323     * @see #setDefaultTransformerLocation(java.lang.String)
324     */
325    public static String getDefaultTransformerLocation()
326    {
327        if ( defaultTransformerLocation == null )
328        {
329            defaultTransformerLocation =
330                System.getProperty( DEFAULT_TRANSFORMER_LOCATION_PROPERTY_NAME, DEFAULT_TRANSFORMER_LOCATION );
331
332        }
333
334        return defaultTransformerLocation;
335    }
336
337    /**
338     * Sets the default location searched for transformer resources.
339     *
340     * @param value The new default location to search for transformer resources or {@code null}.
341     *
342     * @see #getDefaultTransformerLocation()
343     */
344    public static void setDefaultTransformerLocation( final String value )
345    {
346        defaultTransformerLocation = value;
347    }
348
349    /**
350     * Gets the location searched for transformer resources.
351     *
352     * @return The location searched for transformer resources.
353     *
354     * @see #getDefaultTransformerLocation()
355     * @see #setTransformerLocation(java.lang.String)
356     */
357    public final String getTransformerLocation()
358    {
359        if ( this.transformerLocation == null )
360        {
361            this.transformerLocation = getDefaultTransformerLocation();
362        }
363
364        return this.transformerLocation;
365    }
366
367    /**
368     * Sets the location searched for transformer resources.
369     *
370     * @param value The new location to search for transformer resources or {@code null}.
371     *
372     * @see #getTransformerLocation()
373     */
374    public final void setTransformerLocation( final String value )
375    {
376        this.transformerLocation = value;
377    }
378
379    /**
380     * Searches a given context for transformers.
381     *
382     * @param context The context to search for transformers.
383     * @param location The location to search at.
384     *
385     * @return The transformers found at {@code location} in {@code context} or {@code null}, if no transformers are
386     * found.
387     *
388     * @throws NullPointerException if {@code context} or {@code location} is {@code null}.
389     * @throws ModelException if getting the transformers fails.
390     */
391    public List<Transformer> findTransformers( final ModelContext context, final String location ) throws ModelException
392    {
393        if ( context == null )
394        {
395            throw new NullPointerException( "context" );
396        }
397        if ( location == null )
398        {
399            throw new NullPointerException( "location" );
400        }
401
402        try
403        {
404            final long t0 = System.nanoTime();
405            final List<Transformer> transformers = new LinkedList<Transformer>();
406            final Enumeration<URL> transformerResourceEnumeration = context.findResources( location );
407            final Set<URI> transformerResources = new HashSet<URI>();
408            while ( transformerResourceEnumeration.hasMoreElements() )
409            {
410                transformerResources.add( transformerResourceEnumeration.nextElement().toURI() );
411            }
412
413            if ( !transformerResources.isEmpty() )
414            {
415                final ErrorListener errorListener = new ErrorListener()
416                {
417
418                    public void warning( final TransformerException exception ) throws TransformerException
419                    {
420                        if ( context.isLoggable( Level.WARNING ) )
421                        {
422                            context.log( Level.WARNING, getMessage( exception ), exception );
423                        }
424                    }
425
426                    public void error( final TransformerException exception ) throws TransformerException
427                    {
428                        if ( context.isLoggable( Level.SEVERE ) )
429                        {
430                            context.log( Level.SEVERE, getMessage( exception ), exception );
431                        }
432
433                        throw exception;
434                    }
435
436                    public void fatalError( final TransformerException exception ) throws TransformerException
437                    {
438                        if ( context.isLoggable( Level.SEVERE ) )
439                        {
440                            context.log( Level.SEVERE, getMessage( exception ), exception );
441                        }
442
443                        throw exception;
444                    }
445
446                };
447
448                final Properties transformerParameters = this.getTransformerParameters( context );
449
450                if ( context.getExecutorService() != null && transformerResources.size() > 1 )
451                {
452                    final ThreadLocal<TransformerFactory> threadLocalTransformerFactory =
453                        new ThreadLocal<TransformerFactory>();
454
455                    final List<Callable<Transformer>> tasks =
456                        new ArrayList<Callable<Transformer>>( transformerResources.size() );
457
458                    for ( final URI transformerResource : transformerResources )
459                    {
460                        tasks.add( new Callable<Transformer>()
461                        {
462
463                            public Transformer call() throws TransformerConfigurationException, URISyntaxException
464                            {
465                                TransformerFactory transformerFactory = threadLocalTransformerFactory.get();
466                                if ( transformerFactory == null )
467                                {
468                                    transformerFactory = TransformerFactory.newInstance();
469                                    threadLocalTransformerFactory.set( transformerFactory );
470                                }
471
472                                return createTransformer( context, transformerFactory, transformerParameters,
473                                                          transformerResource );
474
475                            }
476
477                        } );
478                    }
479
480                    for ( final Future<Transformer> task : context.getExecutorService().invokeAll( tasks ) )
481                    {
482                        transformers.add( task.get() );
483                    }
484                }
485                else
486                {
487                    final TransformerFactory transformerFactory = TransformerFactory.newInstance();
488                    transformerFactory.setErrorListener( errorListener );
489
490                    for ( final URI transformerResource : transformerResources )
491                    {
492                        transformers.add( this.createTransformer( context, transformerFactory, transformerParameters,
493                                                                  transformerResource ) );
494
495                    }
496                }
497            }
498
499            if ( context.isLoggable( Level.FINE ) )
500            {
501                context.log( Level.FINE, getMessage( "contextReport", transformerResources.size(), location,
502                                                     System.nanoTime() - t0 ), null );
503
504            }
505
506            return transformers.isEmpty() ? null : transformers;
507        }
508        catch ( final CancellationException e )
509        {
510            throw new ModelException( getMessage( e ), e );
511        }
512        catch ( final InterruptedException e )
513        {
514            throw new ModelException( getMessage( e ), e );
515        }
516        catch ( final IOException e )
517        {
518            throw new ModelException( getMessage( e ), e );
519        }
520        catch ( final URISyntaxException e )
521        {
522            throw new ModelException( getMessage( e ), e );
523        }
524        catch ( final TransformerConfigurationException e )
525        {
526            String message = getMessage( e );
527            if ( message == null && e.getException() != null )
528            {
529                message = getMessage( e.getException() );
530            }
531
532            throw new ModelException( message, e );
533        }
534        catch ( final ExecutionException e )
535        {
536            if ( e.getCause() instanceof URISyntaxException )
537            {
538                throw new ModelException( getMessage( e.getCause() ), e.getCause() );
539            }
540            else if ( e.getCause() instanceof TransformerConfigurationException )
541            {
542                String message = getMessage( e.getCause() );
543                if ( message == null && ( (TransformerConfigurationException) e.getCause() ).getException() != null )
544                {
545                    message = getMessage( ( (TransformerConfigurationException) e.getCause() ).getException() );
546                }
547
548                throw new ModelException( message, e.getCause() );
549            }
550            else if ( e.getCause() instanceof RuntimeException )
551            {
552                // The fork-join framework breaks the exception handling contract of Callable by re-throwing any
553                // exception caught using a runtime exception.
554                if ( e.getCause().getCause() instanceof TransformerConfigurationException )
555                {
556                    String message = getMessage( e.getCause().getCause() );
557                    if ( message == null
558                             && ( (TransformerConfigurationException) e.getCause().getCause() ).getException() != null )
559                    {
560                        message = getMessage( ( (TransformerConfigurationException) e.getCause().getCause() ).
561                            getException() );
562
563                    }
564
565                    throw new ModelException( message, e.getCause().getCause() );
566                }
567                else if ( e.getCause().getCause() instanceof URISyntaxException )
568                {
569                    throw new ModelException( getMessage( e.getCause().getCause() ), e.getCause().getCause() );
570                }
571                else if ( e.getCause().getCause() instanceof RuntimeException )
572                {
573                    throw (RuntimeException) e.getCause().getCause();
574                }
575                else if ( e.getCause().getCause() instanceof Error )
576                {
577                    throw (Error) e.getCause().getCause();
578                }
579                else if ( e.getCause().getCause() instanceof Exception )
580                {
581                    // Checked exception not declared to be thrown by the Callable's 'call' method.
582                    throw new UndeclaredThrowableException( e.getCause().getCause() );
583                }
584                else
585                {
586                    throw (RuntimeException) e.getCause();
587                }
588            }
589            else if ( e.getCause() instanceof Error )
590            {
591                throw (Error) e.getCause();
592            }
593            else
594            {
595                // Checked exception not declared to be thrown by the Callable's 'call' method.
596                throw new UndeclaredThrowableException( e.getCause() );
597            }
598        }
599    }
600
601    /**
602     * {@inheritDoc}
603     *
604     * @see #isEnabled()
605     * @see #getTransformerLocation()
606     * @see #findTransformers(org.jomc.modlet.ModelContext, java.lang.String)
607     * @see #ENABLED_ATTRIBUTE_NAME
608     * @see #TRANSFORMER_LOCATION_ATTRIBUTE_NAME
609     */
610    public Modlets processModlets( final ModelContext context, final Modlets modlets ) throws ModelException
611    {
612        if ( context == null )
613        {
614            throw new NullPointerException( "context" );
615        }
616        if ( modlets == null )
617        {
618            throw new NullPointerException( "modlets" );
619        }
620
621        try
622        {
623            Modlets processed = null;
624
625            boolean contextEnabled = this.isEnabled();
626            if ( DEFAULT_ENABLED == contextEnabled
627                     && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
628            {
629                contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
630            }
631
632            String contextTransformerLocation = this.getTransformerLocation();
633            if ( DEFAULT_TRANSFORMER_LOCATION.equals( contextTransformerLocation )
634                     && context.getAttribute( TRANSFORMER_LOCATION_ATTRIBUTE_NAME ) instanceof String )
635            {
636                contextTransformerLocation = (String) context.getAttribute( TRANSFORMER_LOCATION_ATTRIBUTE_NAME );
637            }
638
639            if ( contextEnabled )
640            {
641                final org.jomc.modlet.ObjectFactory objectFactory = new org.jomc.modlet.ObjectFactory();
642                final JAXBContext jaxbContext = context.createContext( ModletObject.MODEL_PUBLIC_ID );
643                final List<Transformer> transformers = this.findTransformers( context, contextTransformerLocation );
644
645                if ( transformers != null )
646                {
647                    processed = modlets.clone();
648
649                    for ( int i = 0, s0 = transformers.size(); i < s0; i++ )
650                    {
651                        final JAXBElement<Modlets> e = objectFactory.createModlets( processed );
652                        final JAXBSource source = new JAXBSource( jaxbContext, e );
653                        final JAXBResult result = new JAXBResult( jaxbContext );
654                        transformers.get( i ).transform( source, result );
655
656                        if ( result.getResult() instanceof JAXBElement<?>
657                                 && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Modlets )
658                        {
659                            processed = (Modlets) ( (JAXBElement<?>) result.getResult() ).getValue();
660                        }
661                        else
662                        {
663                            throw new ModelException( getMessage( "illegalTransformationResult" ) );
664                        }
665                    }
666                }
667            }
668            else if ( context.isLoggable( Level.FINER ) )
669            {
670                context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName() ), null );
671            }
672
673            return processed;
674        }
675        catch ( final TransformerException e )
676        {
677            String message = getMessage( e );
678            if ( message == null && e.getException() != null )
679            {
680                message = getMessage( e.getException() );
681            }
682
683            throw new ModelException( message, e );
684        }
685        catch ( final JAXBException e )
686        {
687            String message = getMessage( e );
688            if ( message == null && e.getLinkedException() != null )
689            {
690                message = getMessage( e.getLinkedException() );
691            }
692
693            throw new ModelException( message, e );
694        }
695    }
696
697    private Transformer createTransformer( final ModelContext context, final TransformerFactory transformerFactory,
698                                           final Map<Object, Object> parameters, final URI transformerResource )
699        throws TransformerConfigurationException, URISyntaxException
700    {
701        if ( context.isLoggable( Level.FINEST ) )
702        {
703            context.log( Level.FINEST, getMessage( "processing", transformerResource.toString() ), null );
704        }
705
706        final Transformer transformer =
707            transformerFactory.newTransformer( new StreamSource( transformerResource.toASCIIString() ) );
708
709        transformer.setErrorListener( transformerFactory.getErrorListener() );
710
711        for ( final Map.Entry<Object, Object> e : parameters.entrySet() )
712        {
713            transformer.setParameter( e.getKey().toString(), e.getValue() );
714        }
715
716        return transformer;
717    }
718
719    private Properties getTransformerParameters( final ModelContext context ) throws IOException
720    {
721        final Properties properties = new Properties();
722
723        if ( context.getExecutorService() != null )
724        {
725            ByteArrayInputStream in = null;
726            ByteArrayOutputStream out = null;
727            try
728            {
729                out = new ByteArrayOutputStream();
730                System.getProperties().store( out, this.getClass().getName() );
731                out.close();
732                final byte[] bytes = out.toByteArray();
733                out = null;
734
735                in = new ByteArrayInputStream( bytes );
736                properties.load( in );
737                in.close();
738                in = null;
739            }
740            finally
741            {
742                try
743                {
744                    if ( out != null )
745                    {
746                        out.close();
747                    }
748                }
749                catch ( final IOException e )
750                {
751                    // Suppressed.
752                }
753                finally
754                {
755                    try
756                    {
757                        if ( in != null )
758                        {
759                            in.close();
760                        }
761                    }
762                    catch ( final IOException e )
763                    {
764                        // Suppressed.
765                    }
766                }
767            }
768        }
769        else
770        {
771            properties.putAll( System.getProperties() );
772        }
773
774        return properties;
775    }
776
777    private static String getMessage( final String key, final Object... args )
778    {
779        return MessageFormat.format( ResourceBundle.getBundle(
780            DefaultModletProcessor.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );
781
782    }
783
784    private static String getMessage( final Throwable t )
785    {
786        return t != null
787                   ? t.getMessage() != null && t.getMessage().trim().length() > 0
788                         ? t.getMessage()
789                         : getMessage( t.getCause() )
790                   : null;
791
792    }
793
794}