DefaultModelProcessor.java

  1. /*
  2.  *   Copyright (C) Christian Schulte <cs@schulte.it>, 2005-206
  3.  *   All rights reserved.
  4.  *
  5.  *   Redistribution and use in source and binary forms, with or without
  6.  *   modification, are permitted provided that the following conditions
  7.  *   are met:
  8.  *
  9.  *     o Redistributions of source code must retain the above copyright
  10.  *       notice, this list of conditions and the following disclaimer.
  11.  *
  12.  *     o Redistributions in binary form must reproduce the above copyright
  13.  *       notice, this list of conditions and the following disclaimer in
  14.  *       the documentation and/or other materials provided with the
  15.  *       distribution.
  16.  *
  17.  *   THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
  18.  *   INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  19.  *   AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
  20.  *   THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
  21.  *   INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  22.  *   NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  23.  *   DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  24.  *   THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  25.  *   (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  26.  *   THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  27.  *
  28.  *   $JOMC: DefaultModelProcessor.java 5355 2016-09-05 05:21:13Z schulte $
  29.  *
  30.  */
  31. package org.jomc.model.modlet;

  32. import java.io.ByteArrayInputStream;
  33. import java.io.ByteArrayOutputStream;
  34. import java.io.IOException;
  35. import java.lang.reflect.UndeclaredThrowableException;
  36. import java.net.URISyntaxException;
  37. import java.net.URL;
  38. import java.text.MessageFormat;
  39. import java.util.Enumeration;
  40. import java.util.LinkedList;
  41. import java.util.List;
  42. import java.util.Locale;
  43. import java.util.Map;
  44. import java.util.Properties;
  45. import java.util.ResourceBundle;
  46. import java.util.concurrent.Callable;
  47. import java.util.concurrent.CancellationException;
  48. import java.util.concurrent.ExecutionException;
  49. import java.util.concurrent.Future;
  50. import java.util.logging.Level;
  51. import javax.xml.bind.JAXBContext;
  52. import javax.xml.bind.JAXBElement;
  53. import javax.xml.bind.JAXBException;
  54. import javax.xml.bind.util.JAXBResult;
  55. import javax.xml.bind.util.JAXBSource;
  56. import javax.xml.transform.ErrorListener;
  57. import javax.xml.transform.Transformer;
  58. import javax.xml.transform.TransformerConfigurationException;
  59. import javax.xml.transform.TransformerException;
  60. import javax.xml.transform.TransformerFactory;
  61. import javax.xml.transform.stream.StreamSource;
  62. import org.jomc.modlet.Model;
  63. import org.jomc.modlet.ModelContext;
  64. import org.jomc.modlet.ModelException;
  65. import org.jomc.modlet.ModelProcessor;

  66. /**
  67.  * Default object management and configuration {@code ModelProcessor} implementation.
  68.  *
  69.  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
  70.  * @version $JOMC: DefaultModelProcessor.java 5355 2016-09-05 05:21:13Z schulte $
  71.  * @see ModelContext#processModel(org.jomc.modlet.Model)
  72.  */
  73. public class DefaultModelProcessor implements ModelProcessor
  74. {

  75.     /**
  76.      * Constant for the name of the model context attribute backing property {@code enabled}.
  77.      *
  78.      * @see #processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
  79.      * @see ModelContext#getAttribute(java.lang.String)
  80.      * @since 1.2
  81.      */
  82.     public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.model.modlet.DefaultModelProcessor.enabledAttribute";

  83.     /**
  84.      * Constant for the name of the system property controlling property {@code defaultEnabled}.
  85.      *
  86.      * @see #isDefaultEnabled()
  87.      */
  88.     private static final String DEFAULT_ENABLED_PROPERTY_NAME =
  89.         "org.jomc.model.modlet.DefaultModelProcessor.defaultEnabled";

  90.     /**
  91.      * Constant for the name of the deprecated system property controlling property {@code defaultEnabled}.
  92.      * @see #isDefaultEnabled()
  93.      */
  94.     private static final String DEPRECATED_DEFAULT_ENABLED_PROPERTY_NAME =
  95.         "org.jomc.model.DefaultModelProcessor.defaultEnabled";

  96.     /**
  97.      * Default value of the flag indicating the processor is enabled by default.
  98.      *
  99.      * @see #isDefaultEnabled()
  100.      * @since 1.2
  101.      */
  102.     private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;

  103.     /**
  104.      * Flag indicating the processor is enabled by default.
  105.      */
  106.     private static volatile Boolean defaultEnabled;

  107.     /**
  108.      * Flag indicating the processor is enabled.
  109.      */
  110.     private Boolean enabled;

  111.     /**
  112.      * Constant for the name of the model context attribute backing property {@code transformerLocation}.
  113.      *
  114.      * @see #processModel(org.jomc.modlet.ModelContext, org.jomc.modlet.Model)
  115.      * @see ModelContext#getAttribute(java.lang.String)
  116.      * @since 1.2
  117.      */
  118.     public static final String TRANSFORMER_LOCATION_ATTRIBUTE_NAME =
  119.         "org.jomc.model.modlet.DefaultModelProcessor.transformerLocationAttribute";

  120.     /**
  121.      * Constant for the name of the system property controlling property {@code defaultTransformerLocation}.
  122.      *
  123.      * @see #getDefaultTransformerLocation()
  124.      */
  125.     private static final String DEFAULT_TRANSFORMER_LOCATION_PROPERTY_NAME =
  126.         "org.jomc.model.modlet.DefaultModelProcessor.defaultTransformerLocation";

  127.     /**
  128.      * Constant for the name of the deprecated system property controlling property {@code defaultTransformerLocation}.
  129.      * @see #getDefaultTransformerLocation()
  130.      */
  131.     private static final String DEPRECATED_DEFAULT_TRANSFORMER_LOCATION_PROPERTY_NAME =
  132.         "org.jomc.model.DefaultModelProcessor.defaultTransformerLocation";

  133.     /**
  134.      * Class path location searched for transformers by default.
  135.      *
  136.      * @see #getDefaultTransformerLocation()
  137.      */
  138.     private static final String DEFAULT_TRANSFORMER_LOCATION = "META-INF/jomc.xsl";

  139.     /**
  140.      * Default transformer location.
  141.      */
  142.     private static volatile String defaultTransformerLocation;

  143.     /**
  144.      * Transformer location of the instance.
  145.      */
  146.     private String transformerLocation;

  147.     /**
  148.      * Creates a new {@code DefaultModelProcessor} instance.
  149.      */
  150.     public DefaultModelProcessor()
  151.     {
  152.         super();
  153.     }

  154.     /**
  155.      * Gets a flag indicating the processor is enabled by default.
  156.      * <p>
  157.      * The default enabled flag is controlled by system property
  158.      * {@code org.jomc.model.modlet.DefaultModelProcessor.defaultEnabled} holding a value indicating the processor is
  159.      * enabled by default. If that property is not set, the {@code true} default is returned.
  160.      * </p>
  161.      *
  162.      * @return {@code true}, if the processor is enabled by default; {@code false}, if the processor is disabled by
  163.      * default.
  164.      *
  165.      * @see #setDefaultEnabled(java.lang.Boolean)
  166.      */
  167.     public static boolean isDefaultEnabled()
  168.     {
  169.         if ( defaultEnabled == null )
  170.         {
  171.             defaultEnabled =
  172.                 Boolean.valueOf( System.getProperty( DEFAULT_ENABLED_PROPERTY_NAME,
  173.                                                      System.getProperty( DEPRECATED_DEFAULT_ENABLED_PROPERTY_NAME,
  174.                                                                          Boolean.toString( DEFAULT_ENABLED ) ) ) );

  175.         }

  176.         return defaultEnabled;
  177.     }

  178.     /**
  179.      * Sets the flag indicating the processor is enabled by default.
  180.      *
  181.      * @param value The new value of the flag indicating the processor is enabled by default or {@code null}.
  182.      *
  183.      * @see #isDefaultEnabled()
  184.      */
  185.     public static void setDefaultEnabled( final Boolean value )
  186.     {
  187.         defaultEnabled = value;
  188.     }

  189.     /**
  190.      * Gets a flag indicating the processor is enabled.
  191.      *
  192.      * @return {@code true}, if the processor is enabled; {@code false}, if the processor is disabled.
  193.      *
  194.      * @see #isDefaultEnabled()
  195.      * @see #setEnabled(java.lang.Boolean)
  196.      */
  197.     public final boolean isEnabled()
  198.     {
  199.         if ( this.enabled == null )
  200.         {
  201.             this.enabled = isDefaultEnabled();
  202.         }

  203.         return this.enabled;
  204.     }

  205.     /**
  206.      * Sets the flag indicating the processor is enabled.
  207.      *
  208.      * @param value The new value of the flag indicating the processor is enabled or {@code null}.
  209.      *
  210.      * @see #isEnabled()
  211.      */
  212.     public final void setEnabled( final Boolean value )
  213.     {
  214.         this.enabled = value;
  215.     }

  216.     /**
  217.      * Gets the default location searched for transformer resources.
  218.      * <p>
  219.      * The default transformer location is controlled by system property
  220.      * {@code org.jomc.model.modlet.DefaultModelProcessor.defaultTransformerLocation} holding the location to search for
  221.      * transformer resources by default. If that property is not set, the {@code META-INF/jomc.xsl} default is
  222.      * returned.
  223.      * </p>
  224.      *
  225.      * @return The location searched for transformer resources by default.
  226.      *
  227.      * @see #setDefaultTransformerLocation(java.lang.String)
  228.      */
  229.     public static String getDefaultTransformerLocation()
  230.     {
  231.         if ( defaultTransformerLocation == null )
  232.         {
  233.             defaultTransformerLocation =
  234.                 System.getProperty( DEFAULT_TRANSFORMER_LOCATION_PROPERTY_NAME,
  235.                                     System.getProperty( DEPRECATED_DEFAULT_TRANSFORMER_LOCATION_PROPERTY_NAME,
  236.                                                         DEFAULT_TRANSFORMER_LOCATION ) );

  237.         }

  238.         return defaultTransformerLocation;
  239.     }

  240.     /**
  241.      * Sets the default location searched for transformer resources.
  242.      *
  243.      * @param value The new default location to search for transformer resources or {@code null}.
  244.      *
  245.      * @see #getDefaultTransformerLocation()
  246.      */
  247.     public static void setDefaultTransformerLocation( final String value )
  248.     {
  249.         defaultTransformerLocation = value;
  250.     }

  251.     /**
  252.      * Gets the location searched for transformer resources.
  253.      *
  254.      * @return The location searched for transformer resources.
  255.      *
  256.      * @see #getDefaultTransformerLocation()
  257.      * @see #setTransformerLocation(java.lang.String)
  258.      */
  259.     public final String getTransformerLocation()
  260.     {
  261.         if ( this.transformerLocation == null )
  262.         {
  263.             this.transformerLocation = getDefaultTransformerLocation();
  264.         }

  265.         return this.transformerLocation;
  266.     }

  267.     /**
  268.      * Sets the location searched for transformer resources.
  269.      *
  270.      * @param value The new location to search for transformer resources or {@code null}.
  271.      *
  272.      * @see #getTransformerLocation()
  273.      */
  274.     public final void setTransformerLocation( final String value )
  275.     {
  276.         this.transformerLocation = value;
  277.     }

  278.     /**
  279.      * Searches a given context for transformers.
  280.      *
  281.      * @param context The context to search for transformers.
  282.      * @param location The location to search at.
  283.      *
  284.      * @return The transformers found at {@code location} in {@code context} or {@code null}, if no transformers are
  285.      * found.
  286.      *
  287.      * @throws NullPointerException if {@code context} or {@code location} is {@code null}.
  288.      * @throws ModelException if getting the transformers fails.
  289.      */
  290.     public List<Transformer> findTransformers( final ModelContext context, final String location ) throws ModelException
  291.     {
  292.         if ( context == null )
  293.         {
  294.             throw new NullPointerException( "context" );
  295.         }
  296.         if ( location == null )
  297.         {
  298.             throw new NullPointerException( "location" );
  299.         }

  300.         try
  301.         {
  302.             final long t0 = System.nanoTime();
  303.             final List<Transformer> transformers = new LinkedList<Transformer>();
  304.             final Enumeration<URL> resources = context.findResources( location );
  305.             final ErrorListener errorListener = new ErrorListener()
  306.             {

  307.                 public void warning( final TransformerException exception ) throws TransformerException
  308.                 {
  309.                     if ( context.isLoggable( Level.WARNING ) )
  310.                     {
  311.                         context.log( Level.WARNING, getMessage( exception ), exception );
  312.                     }
  313.                 }

  314.                 public void error( final TransformerException exception ) throws TransformerException
  315.                 {
  316.                     if ( context.isLoggable( Level.SEVERE ) )
  317.                     {
  318.                         context.log( Level.SEVERE, getMessage( exception ), exception );
  319.                     }

  320.                     throw exception;
  321.                 }

  322.                 public void fatalError( final TransformerException exception ) throws TransformerException
  323.                 {
  324.                     if ( context.isLoggable( Level.SEVERE ) )
  325.                     {
  326.                         context.log( Level.SEVERE, getMessage( exception ), exception );
  327.                     }

  328.                     throw exception;
  329.                 }

  330.             };

  331.             final Properties parameters = getTransformerParameters();
  332.             final ThreadLocal<TransformerFactory> threadLocalTransformerFactory = new ThreadLocal<TransformerFactory>();

  333.             class CreateTansformerTask implements Callable<Transformer>
  334.             {

  335.                 private final URL resource;

  336.                 CreateTansformerTask( final URL resource )
  337.                 {
  338.                     super();
  339.                     this.resource = resource;
  340.                 }

  341.                 public Transformer call() throws ModelException
  342.                 {
  343.                     try
  344.                     {
  345.                         TransformerFactory transformerFactory = threadLocalTransformerFactory.get();
  346.                         if ( transformerFactory == null )
  347.                         {
  348.                             transformerFactory = TransformerFactory.newInstance();
  349.                             transformerFactory.setErrorListener( errorListener );
  350.                             threadLocalTransformerFactory.set( transformerFactory );
  351.                         }

  352.                         if ( context.isLoggable( Level.FINEST ) )
  353.                         {
  354.                             context.log( Level.FINEST, getMessage( "processing", this.resource.toExternalForm() ),
  355.                                          null );

  356.                         }

  357.                         final Transformer transformer = transformerFactory.newTransformer(
  358.                             new StreamSource( this.resource.toURI().toASCIIString() ) );

  359.                         transformer.setErrorListener( errorListener );

  360.                         for ( final Map.Entry<Object, Object> e : parameters.entrySet() )
  361.                         {
  362.                             transformer.setParameter( e.getKey().toString(), e.getValue() );
  363.                         }

  364.                         return transformer;
  365.                     }
  366.                     catch ( final TransformerConfigurationException e )
  367.                     {
  368.                         String message = getMessage( e );
  369.                         if ( message == null && e.getException() != null )
  370.                         {
  371.                             message = getMessage( e.getException() );
  372.                         }

  373.                         throw new ModelException( message, e );
  374.                     }
  375.                     catch ( final URISyntaxException e )
  376.                     {
  377.                         throw new ModelException( getMessage( e ), e );
  378.                     }
  379.                 }

  380.             }

  381.             final List<CreateTansformerTask> tasks = new LinkedList<CreateTansformerTask>();

  382.             while ( resources.hasMoreElements() )
  383.             {
  384.                 tasks.add( new CreateTansformerTask( resources.nextElement() ) );
  385.             }

  386.             if ( context.getExecutorService() != null && tasks.size() > 1 )
  387.             {
  388.                 for ( final Future<Transformer> task : context.getExecutorService().invokeAll( tasks ) )
  389.                 {
  390.                     transformers.add( task.get() );
  391.                 }
  392.             }
  393.             else
  394.             {
  395.                 for ( final CreateTansformerTask task : tasks )
  396.                 {
  397.                     transformers.add( task.call() );
  398.                 }
  399.             }

  400.             if ( context.isLoggable( Level.FINE ) )
  401.             {
  402.                 context.log( Level.FINE, getMessage( "contextReport", tasks.size(), location, System.nanoTime() - t0 ),
  403.                              null );

  404.             }

  405.             return transformers.isEmpty() ? null : transformers;
  406.         }
  407.         catch ( final CancellationException e )
  408.         {
  409.             throw new ModelException( getMessage( e ), e );
  410.         }
  411.         catch ( final InterruptedException e )
  412.         {
  413.             throw new ModelException( getMessage( e ), e );
  414.         }
  415.         catch ( final ExecutionException e )
  416.         {
  417.             if ( e.getCause() instanceof ModelException )
  418.             {
  419.                 throw (ModelException) e.getCause();
  420.             }
  421.             else if ( e.getCause() instanceof RuntimeException )
  422.             {
  423.                 // The fork-join framework breaks the exception handling contract of Callable by re-throwing any
  424.                 // exception caught using a runtime exception.
  425.                 if ( e.getCause().getCause() instanceof ModelException )
  426.                 {
  427.                     throw (ModelException) e.getCause().getCause();
  428.                 }
  429.                 else if ( e.getCause().getCause() instanceof RuntimeException )
  430.                 {
  431.                     throw (RuntimeException) e.getCause().getCause();
  432.                 }
  433.                 else if ( e.getCause().getCause() instanceof Error )
  434.                 {
  435.                     throw (Error) e.getCause().getCause();
  436.                 }
  437.                 else if ( e.getCause().getCause() instanceof Exception )
  438.                 {
  439.                     // Checked exception not declared to be thrown by the Callable's 'call' method.
  440.                     throw new UndeclaredThrowableException( e.getCause().getCause() );
  441.                 }
  442.                 else
  443.                 {
  444.                     throw (RuntimeException) e.getCause();
  445.                 }
  446.             }
  447.             else if ( e.getCause() instanceof Error )
  448.             {
  449.                 throw (Error) e.getCause();
  450.             }
  451.             else
  452.             {
  453.                 // Checked exception not declared to be thrown by the Callable's 'call' method.
  454.                 throw new UndeclaredThrowableException( e.getCause() );
  455.             }
  456.         }
  457.     }

  458.     /**
  459.      * {@inheritDoc}
  460.      *
  461.      * @see #isEnabled()
  462.      * @see #getTransformerLocation()
  463.      * @see #findTransformers(org.jomc.modlet.ModelContext, java.lang.String)
  464.      * @see #ENABLED_ATTRIBUTE_NAME
  465.      * @see #TRANSFORMER_LOCATION_ATTRIBUTE_NAME
  466.      */
  467.     public Model processModel( final ModelContext context, final Model model ) throws ModelException
  468.     {
  469.         if ( context == null )
  470.         {
  471.             throw new NullPointerException( "context" );
  472.         }
  473.         if ( model == null )
  474.         {
  475.             throw new NullPointerException( "model" );
  476.         }

  477.         try
  478.         {
  479.             Model processed = model;

  480.             boolean contextEnabled = this.isEnabled();
  481.             if ( DEFAULT_ENABLED == contextEnabled
  482.                      && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
  483.             {
  484.                 contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
  485.             }

  486.             String contextTransformerLocation = this.getTransformerLocation();
  487.             if ( DEFAULT_TRANSFORMER_LOCATION.equals( contextTransformerLocation )
  488.                      && context.getAttribute( TRANSFORMER_LOCATION_ATTRIBUTE_NAME ) instanceof String )
  489.             {
  490.                 contextTransformerLocation = (String) context.getAttribute( TRANSFORMER_LOCATION_ATTRIBUTE_NAME );
  491.             }

  492.             if ( contextEnabled )
  493.             {
  494.                 final org.jomc.modlet.ObjectFactory objectFactory = new org.jomc.modlet.ObjectFactory();
  495.                 final JAXBContext jaxbContext = context.createContext( model.getIdentifier() );
  496.                 final List<Transformer> transformers = this.findTransformers( context, contextTransformerLocation );
  497.                 processed = model.clone();

  498.                 if ( transformers != null )
  499.                 {
  500.                     for ( int i = 0, s0 = transformers.size(); i < s0; i++ )
  501.                     {
  502.                         final JAXBElement<Model> e = objectFactory.createModel( processed );
  503.                         final JAXBSource source = new JAXBSource( jaxbContext, e );
  504.                         final JAXBResult result = new JAXBResult( jaxbContext );
  505.                         transformers.get( i ).transform( source, result );

  506.                         if ( result.getResult() instanceof JAXBElement<?>
  507.                                  && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Model )
  508.                         {
  509.                             processed = (Model) ( (JAXBElement<?>) result.getResult() ).getValue();
  510.                         }
  511.                         else
  512.                         {
  513.                             throw new ModelException( getMessage(
  514.                                 "illegalTransformationResult", model.getIdentifier() ) );

  515.                         }
  516.                     }
  517.                 }
  518.             }
  519.             else if ( context.isLoggable( Level.FINER ) )
  520.             {
  521.                 context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName(),
  522.                                                       model.getIdentifier() ), null );

  523.             }

  524.             return processed;
  525.         }
  526.         catch ( final TransformerException e )
  527.         {
  528.             String message = getMessage( e );
  529.             if ( message == null && e.getException() != null )
  530.             {
  531.                 message = getMessage( e.getException() );
  532.             }

  533.             throw new ModelException( message, e );
  534.         }
  535.         catch ( final JAXBException e )

  536.         {
  537.             String message = getMessage( e );
  538.             if ( message == null && e.getLinkedException() != null )
  539.             {
  540.                 message = getMessage( e.getLinkedException() );
  541.             }

  542.             throw new ModelException( message, e );
  543.         }
  544.     }

  545.     private static Properties getTransformerParameters() throws ModelException
  546.     {
  547.         final Properties properties = new Properties();

  548.         ByteArrayInputStream in = null;
  549.         ByteArrayOutputStream out = null;
  550.         try
  551.         {
  552.             out = new ByteArrayOutputStream();
  553.             System.getProperties().store( out, DefaultModelProcessor.class.getName() );
  554.             out.close();
  555.             final byte[] bytes = out.toByteArray();
  556.             out = null;

  557.             in = new ByteArrayInputStream( bytes );
  558.             properties.load( in );
  559.             in.close();
  560.             in = null;
  561.         }
  562.         catch ( final IOException e )
  563.         {
  564.             throw new ModelException( getMessage( e ), e );
  565.         }
  566.         finally
  567.         {
  568.             try
  569.             {
  570.                 if ( out != null )
  571.                 {
  572.                     out.close();
  573.                 }
  574.             }
  575.             catch ( final IOException e )
  576.             {
  577.                 // Suppressed.
  578.             }
  579.             finally
  580.             {
  581.                 try
  582.                 {
  583.                     if ( in != null )
  584.                     {
  585.                         in.close();
  586.                     }
  587.                 }
  588.                 catch ( final IOException e )
  589.                 {
  590.                     // Suppressed.
  591.                 }
  592.             }
  593.         }

  594.         return properties;
  595.     }

  596.     private static String getMessage( final String key, final Object... args )
  597.     {
  598.         return MessageFormat.format( ResourceBundle.getBundle(
  599.             DefaultModelProcessor.class
  600.             .getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );

  601.     }

  602.     private static String getMessage( final Throwable t )
  603.     {
  604.         return t != null
  605.                    ? t.getMessage() != null && t.getMessage().trim().length() > 0
  606.                          ? t.getMessage()
  607.                          : getMessage( t.getCause() )
  608.                    : null;

  609.     }

  610. }