DefaultModletProcessor.java

  1. /*
  2.  *   Copyright (C) Christian Schulte <cs@schulte.it>, 2014-005
  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: DefaultModletProcessor.java 5043 2015-05-27 07:03:39Z schulte $
  29.  *
  30.  */
  31. package org.jomc.modlet;

  32. import java.net.URISyntaxException;
  33. import java.net.URL;
  34. import java.text.MessageFormat;
  35. import java.util.Enumeration;
  36. import java.util.LinkedList;
  37. import java.util.List;
  38. import java.util.Locale;
  39. import java.util.Map;
  40. import java.util.ResourceBundle;
  41. import java.util.logging.Level;
  42. import javax.xml.bind.JAXBContext;
  43. import javax.xml.bind.JAXBElement;
  44. import javax.xml.bind.JAXBException;
  45. import javax.xml.bind.util.JAXBResult;
  46. import javax.xml.bind.util.JAXBSource;
  47. import javax.xml.transform.ErrorListener;
  48. import javax.xml.transform.Transformer;
  49. import javax.xml.transform.TransformerConfigurationException;
  50. import javax.xml.transform.TransformerException;
  51. import javax.xml.transform.TransformerFactory;
  52. import javax.xml.transform.stream.StreamSource;

  53. /**
  54.  * Default {@code ModletProcessor} implementation.
  55.  *
  56.  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
  57.  * @version $JOMC: DefaultModletProcessor.java 5043 2015-05-27 07:03:39Z schulte $
  58.  * @see ModelContext#processModlets(org.jomc.modlet.Modlets)
  59.  * @since 1.6
  60.  */
  61. public class DefaultModletProcessor implements ModletProcessor
  62. {

  63.     /**
  64.      * Constant for the name of the model context attribute backing property {@code enabled}.
  65.      *
  66.      * @see #processModlets(org.jomc.modlet.ModelContext, org.jomc.modlet.Modlets)
  67.      * @see ModelContext#getAttribute(java.lang.String)
  68.      */
  69.     public static final String ENABLED_ATTRIBUTE_NAME = "org.jomc.modlet.DefaultModletProcessor.enabledAttribute";

  70.     /**
  71.      * Constant for the name of the system property controlling property {@code defaultEnabled}.
  72.      *
  73.      * @see #isDefaultEnabled()
  74.      */
  75.     private static final String DEFAULT_ENABLED_PROPERTY_NAME =
  76.         "org.jomc.modlet.DefaultModletProcessor.defaultEnabled";

  77.     /**
  78.      * Default value of the flag indicating the processor is enabled by default.
  79.      *
  80.      * @see #isDefaultEnabled()
  81.      */
  82.     private static final Boolean DEFAULT_ENABLED = Boolean.TRUE;

  83.     /**
  84.      * Flag indicating the processor is enabled by default.
  85.      */
  86.     private static volatile Boolean defaultEnabled;

  87.     /**
  88.      * Flag indicating the processor is enabled.
  89.      */
  90.     private Boolean enabled;

  91.     /**
  92.      * Constant for the name of the system property controlling property {@code defaultOrdinal}.
  93.      *
  94.      * @see #getDefaultOrdinal()
  95.      */
  96.     private static final String DEFAULT_ORDINAL_PROPERTY_NAME =
  97.         "org.jomc.modlet.DefaultModletProcessor.defaultOrdinal";

  98.     /**
  99.      * Default value of the ordinal number of the processor.
  100.      *
  101.      * @see #getDefaultOrdinal()
  102.      */
  103.     private static final Integer DEFAULT_ORDINAL = 0;

  104.     /**
  105.      * Default ordinal number of the processor.
  106.      */
  107.     private static volatile Integer defaultOrdinal;

  108.     /**
  109.      * Ordinal number of the processor.
  110.      */
  111.     private Integer ordinal;

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

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

  128.     /**
  129.      * Class path location searched for transformers by default.
  130.      *
  131.      * @see #getDefaultTransformerLocation()
  132.      */
  133.     private static final String DEFAULT_TRANSFORMER_LOCATION = "META-INF/jomc-modlet.xsl";

  134.     /**
  135.      * Default transformer location.
  136.      */
  137.     private static volatile String defaultTransformerLocation;

  138.     /**
  139.      * Transformer location of the instance.
  140.      */
  141.     private String transformerLocation;

  142.     /**
  143.      * Creates a new {@code DefaultModletProcessor} instance.
  144.      */
  145.     public DefaultModletProcessor()
  146.     {
  147.         super();
  148.     }

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

  169.         }

  170.         return defaultEnabled;
  171.     }

  172.     /**
  173.      * Sets the flag indicating the processor is enabled by default.
  174.      *
  175.      * @param value The new value of the flag indicating the processor is enabled by default or {@code null}.
  176.      *
  177.      * @see #isDefaultEnabled()
  178.      */
  179.     public static void setDefaultEnabled( final Boolean value )
  180.     {
  181.         defaultEnabled = value;
  182.     }

  183.     /**
  184.      * Gets a flag indicating the processor is enabled.
  185.      *
  186.      * @return {@code true}, if the processor is enabled; {@code false}, if the processor is disabled.
  187.      *
  188.      * @see #isDefaultEnabled()
  189.      * @see #setEnabled(java.lang.Boolean)
  190.      */
  191.     public final boolean isEnabled()
  192.     {
  193.         if ( this.enabled == null )
  194.         {
  195.             this.enabled = isDefaultEnabled();
  196.         }

  197.         return this.enabled;
  198.     }

  199.     /**
  200.      * Sets the flag indicating the processor is enabled.
  201.      *
  202.      * @param value The new value of the flag indicating the processor is enabled or {@code null}.
  203.      *
  204.      * @see #isEnabled()
  205.      */
  206.     public final void setEnabled( final Boolean value )
  207.     {
  208.         this.enabled = value;
  209.     }

  210.     /**
  211.      * Gets the default ordinal number of the processor.
  212.      * <p>
  213.      * The default ordinal number is controlled by system property
  214.      * {@code org.jomc.modlet.DefaultModletProvider.defaultOrdinal} holding the default ordinal number of the processor.
  215.      * If that property is not set, the {@code 0} default is returned.
  216.      * </p>
  217.      *
  218.      * @return The default ordinal number of the processor.
  219.      *
  220.      * @see #setDefaultOrdinal(java.lang.Integer)
  221.      */
  222.     public static int getDefaultOrdinal()
  223.     {
  224.         if ( defaultOrdinal == null )
  225.         {
  226.             defaultOrdinal = Integer.getInteger( DEFAULT_ORDINAL_PROPERTY_NAME, DEFAULT_ORDINAL );
  227.         }

  228.         return defaultOrdinal;
  229.     }

  230.     /**
  231.      * Sets the default ordinal number of the processor.
  232.      *
  233.      * @param value The new default ordinal number of the processor or {@code null}.
  234.      *
  235.      * @see #getDefaultOrdinal()
  236.      */
  237.     public static void setDefaultOrdinal( final Integer value )
  238.     {
  239.         defaultOrdinal = value;
  240.     }

  241.     /**
  242.      * Gets the ordinal number of the processor.
  243.      *
  244.      * @return The ordinal number of the processor.
  245.      *
  246.      * @see #getDefaultOrdinal()
  247.      * @see #setOrdinal(java.lang.Integer)
  248.      */
  249.     public final int getOrdinal()
  250.     {
  251.         if ( this.ordinal == null )
  252.         {
  253.             this.ordinal = getDefaultOrdinal();
  254.         }

  255.         return this.ordinal;
  256.     }

  257.     /**
  258.      * Sets the ordinal number of the processor.
  259.      *
  260.      * @param value The new ordinal number of the processor or {@code null}.
  261.      *
  262.      * @see #getOrdinal()
  263.      */
  264.     public final void setOrdinal( final Integer value )
  265.     {
  266.         this.ordinal = value;
  267.     }

  268.     /**
  269.      * Gets the default location searched for transformer resources.
  270.      * <p>
  271.      * The default transformer location is controlled by system property
  272.      * {@code org.jomc.modlet.DefaultModletProcessor.defaultTransformerLocation} holding the location to search
  273.      * for transformer resources by default. If that property is not set, the {@code META-INF/jomc-modlet.xsl} default
  274.      * is returned.
  275.      * </p>
  276.      *
  277.      * @return The location searched for transformer resources by default.
  278.      *
  279.      * @see #setDefaultTransformerLocation(java.lang.String)
  280.      */
  281.     public static String getDefaultTransformerLocation()
  282.     {
  283.         if ( defaultTransformerLocation == null )
  284.         {
  285.             defaultTransformerLocation =
  286.                 System.getProperty( DEFAULT_TRANSFORMER_LOCATION_PROPERTY_NAME, DEFAULT_TRANSFORMER_LOCATION );

  287.         }

  288.         return defaultTransformerLocation;
  289.     }

  290.     /**
  291.      * Sets the default location searched for transformer resources.
  292.      *
  293.      * @param value The new default location to search for transformer resources or {@code null}.
  294.      *
  295.      * @see #getDefaultTransformerLocation()
  296.      */
  297.     public static void setDefaultTransformerLocation( final String value )
  298.     {
  299.         defaultTransformerLocation = value;
  300.     }

  301.     /**
  302.      * Gets the location searched for transformer resources.
  303.      *
  304.      * @return The location searched for transformer resources.
  305.      *
  306.      * @see #getDefaultTransformerLocation()
  307.      * @see #setTransformerLocation(java.lang.String)
  308.      */
  309.     public final String getTransformerLocation()
  310.     {
  311.         if ( this.transformerLocation == null )
  312.         {
  313.             this.transformerLocation = getDefaultTransformerLocation();
  314.         }

  315.         return this.transformerLocation;
  316.     }

  317.     /**
  318.      * Sets the location searched for transformer resources.
  319.      *
  320.      * @param value The new location to search for transformer resources or {@code null}.
  321.      *
  322.      * @see #getTransformerLocation()
  323.      */
  324.     public final void setTransformerLocation( final String value )
  325.     {
  326.         this.transformerLocation = value;
  327.     }

  328.     /**
  329.      * Searches a given context for transformers.
  330.      *
  331.      * @param context The context to search for transformers.
  332.      * @param location The location to search at.
  333.      *
  334.      * @return The transformers found at {@code location} in {@code context} or {@code null}, if no transformers are
  335.      * found.
  336.      *
  337.      * @throws NullPointerException if {@code context} or {@code location} is {@code null}.
  338.      * @throws ModelException if getting the transformers fails.
  339.      */
  340.     public List<Transformer> findTransformers( final ModelContext context, final String location ) throws ModelException
  341.     {
  342.         if ( context == null )
  343.         {
  344.             throw new NullPointerException( "context" );
  345.         }
  346.         if ( location == null )
  347.         {
  348.             throw new NullPointerException( "location" );
  349.         }

  350.         try
  351.         {
  352.             final long t0 = System.currentTimeMillis();
  353.             final List<Transformer> transformers = new LinkedList<Transformer>();
  354.             final TransformerFactory transformerFactory = TransformerFactory.newInstance();
  355.             final Enumeration<URL> resources = context.findResources( location );
  356.             final ErrorListener errorListener = new ErrorListener()
  357.             {

  358.                 public void warning( final TransformerException exception ) throws TransformerException
  359.                 {
  360.                     if ( context.isLoggable( Level.WARNING ) )
  361.                     {
  362.                         context.log( Level.WARNING, getMessage( exception ), exception );
  363.                     }
  364.                 }

  365.                 public void error( final TransformerException exception ) throws TransformerException
  366.                 {
  367.                     if ( context.isLoggable( Level.SEVERE ) )
  368.                     {
  369.                         context.log( Level.SEVERE, getMessage( exception ), exception );
  370.                     }

  371.                     throw exception;
  372.                 }

  373.                 public void fatalError( final TransformerException exception ) throws TransformerException
  374.                 {
  375.                     if ( context.isLoggable( Level.SEVERE ) )
  376.                     {
  377.                         context.log( Level.SEVERE, getMessage( exception ), exception );
  378.                     }

  379.                     throw exception;
  380.                 }

  381.             };

  382.             transformerFactory.setErrorListener( errorListener );

  383.             int count = 0;
  384.             while ( resources.hasMoreElements() )
  385.             {
  386.                 count++;
  387.                 final URL url = resources.nextElement();

  388.                 if ( context.isLoggable( Level.FINEST ) )
  389.                 {
  390.                     context.log( Level.FINEST, getMessage( "processing", url.toExternalForm() ), null );
  391.                 }

  392.                 final Transformer transformer =
  393.                     transformerFactory.newTransformer( new StreamSource( url.toURI().toASCIIString() ) );

  394.                 transformer.setErrorListener( errorListener );

  395.                 for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() )
  396.                 {
  397.                     transformer.setParameter( e.getKey().toString(), e.getValue() );
  398.                 }

  399.                 transformers.add( transformer );
  400.             }

  401.             if ( context.isLoggable( Level.FINE ) )
  402.             {
  403.                 context.log( Level.FINE, getMessage( "contextReport", count, location,
  404.                                                      Long.valueOf( System.currentTimeMillis() - t0 ) ), null );

  405.             }

  406.             return transformers.isEmpty() ? null : transformers;
  407.         }
  408.         catch ( final URISyntaxException e )
  409.         {
  410.             throw new ModelException( getMessage( e ), e );
  411.         }
  412.         catch ( final TransformerConfigurationException e )
  413.         {
  414.             String message = getMessage( e );
  415.             if ( message == null && e.getException() != null )
  416.             {
  417.                 message = getMessage( e.getException() );
  418.             }

  419.             throw new ModelException( message, e );
  420.         }
  421.     }

  422.     /**
  423.      * {@inheritDoc}
  424.      *
  425.      * @see #isEnabled()
  426.      * @see #getTransformerLocation()
  427.      * @see #findTransformers(org.jomc.modlet.ModelContext, java.lang.String)
  428.      * @see #ENABLED_ATTRIBUTE_NAME
  429.      * @see #TRANSFORMER_LOCATION_ATTRIBUTE_NAME
  430.      */
  431.     public Modlets processModlets( final ModelContext context, final Modlets modlets ) throws ModelException
  432.     {
  433.         if ( context == null )
  434.         {
  435.             throw new NullPointerException( "context" );
  436.         }
  437.         if ( modlets == null )
  438.         {
  439.             throw new NullPointerException( "modlets" );
  440.         }

  441.         try
  442.         {
  443.             Modlets processed = null;

  444.             boolean contextEnabled = this.isEnabled();
  445.             if ( DEFAULT_ENABLED == contextEnabled
  446.                      && context.getAttribute( ENABLED_ATTRIBUTE_NAME ) instanceof Boolean )
  447.             {
  448.                 contextEnabled = (Boolean) context.getAttribute( ENABLED_ATTRIBUTE_NAME );
  449.             }

  450.             String contextTransformerLocation = this.getTransformerLocation();
  451.             if ( DEFAULT_TRANSFORMER_LOCATION.equals( contextTransformerLocation )
  452.                      && context.getAttribute( TRANSFORMER_LOCATION_ATTRIBUTE_NAME ) instanceof String )
  453.             {
  454.                 contextTransformerLocation = (String) context.getAttribute( TRANSFORMER_LOCATION_ATTRIBUTE_NAME );
  455.             }

  456.             if ( contextEnabled )
  457.             {
  458.                 final org.jomc.modlet.ObjectFactory objectFactory = new org.jomc.modlet.ObjectFactory();
  459.                 final JAXBContext jaxbContext = context.createContext( ModletObject.MODEL_PUBLIC_ID );
  460.                 final List<Transformer> transformers = this.findTransformers( context, contextTransformerLocation );

  461.                 if ( transformers != null )
  462.                 {
  463.                     processed = modlets.clone();

  464.                     for ( int i = 0, s0 = transformers.size(); i < s0; i++ )
  465.                     {
  466.                         final JAXBElement<Modlets> e = objectFactory.createModlets( processed );
  467.                         final JAXBSource source = new JAXBSource( jaxbContext, e );
  468.                         final JAXBResult result = new JAXBResult( jaxbContext );
  469.                         transformers.get( i ).transform( source, result );

  470.                         if ( result.getResult() instanceof JAXBElement<?>
  471.                                  && ( (JAXBElement<?>) result.getResult() ).getValue() instanceof Modlets )
  472.                         {
  473.                             processed = (Modlets) ( (JAXBElement<?>) result.getResult() ).getValue();
  474.                         }
  475.                         else
  476.                         {
  477.                             throw new ModelException( getMessage( "illegalTransformationResult" ) );
  478.                         }
  479.                     }
  480.                 }
  481.             }
  482.             else if ( context.isLoggable( Level.FINER ) )
  483.             {
  484.                 context.log( Level.FINER, getMessage( "disabled", this.getClass().getSimpleName() ), null );
  485.             }

  486.             return processed;
  487.         }
  488.         catch ( final TransformerException e )
  489.         {
  490.             String message = getMessage( e );
  491.             if ( message == null && e.getException() != null )
  492.             {
  493.                 message = getMessage( e.getException() );
  494.             }

  495.             throw new ModelException( message, e );
  496.         }
  497.         catch ( final JAXBException e )
  498.         {
  499.             String message = getMessage( e );
  500.             if ( message == null && e.getLinkedException() != null )
  501.             {
  502.                 message = getMessage( e.getLinkedException() );
  503.             }

  504.             throw new ModelException( message, e );
  505.         }
  506.     }

  507.     private static String getMessage( final String key, final Object... args )
  508.     {
  509.         return MessageFormat.format( ResourceBundle.getBundle(
  510.             DefaultModletProcessor.class.getName().replace( '.', '/' ), Locale.getDefault() ).getString( key ), args );

  511.     }

  512.     private static String getMessage( final Throwable t )
  513.     {
  514.         return t != null
  515.                    ? t.getMessage() != null && t.getMessage().trim().length() > 0
  516.                          ? t.getMessage()
  517.                          : getMessage( t.getCause() )
  518.                    : null;

  519.     }

  520. }