JomcTask.java

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

  32. import java.io.BufferedReader;
  33. import java.io.File;
  34. import java.io.IOException;
  35. import java.io.InputStream;
  36. import java.io.StringReader;
  37. import java.io.StringWriter;
  38. import java.net.HttpURLConnection;
  39. import java.net.MalformedURLException;
  40. import java.net.SocketTimeoutException;
  41. import java.net.URI;
  42. import java.net.URISyntaxException;
  43. import java.net.URL;
  44. import java.net.URLConnection;
  45. import java.util.ArrayList;
  46. import java.util.Collection;
  47. import java.util.Enumeration;
  48. import java.util.HashSet;
  49. import java.util.Iterator;
  50. import java.util.LinkedList;
  51. import java.util.List;
  52. import java.util.Locale;
  53. import java.util.Map;
  54. import java.util.Properties;
  55. import java.util.Set;
  56. import java.util.concurrent.ExecutorService;
  57. import java.util.concurrent.Executors;
  58. import java.util.concurrent.ThreadFactory;
  59. import java.util.concurrent.atomic.AtomicInteger;
  60. import java.util.logging.Level;
  61. import javax.xml.bind.JAXBException;
  62. import javax.xml.bind.Marshaller;
  63. import javax.xml.transform.ErrorListener;
  64. import javax.xml.transform.Transformer;
  65. import javax.xml.transform.TransformerConfigurationException;
  66. import javax.xml.transform.TransformerException;
  67. import javax.xml.transform.TransformerFactory;
  68. import javax.xml.transform.stream.StreamSource;
  69. import org.apache.tools.ant.BuildException;
  70. import org.apache.tools.ant.Project;
  71. import org.apache.tools.ant.PropertyHelper;
  72. import org.apache.tools.ant.Task;
  73. import org.apache.tools.ant.types.Path;
  74. import org.apache.tools.ant.types.Reference;
  75. import org.jomc.ant.types.KeyValueType;
  76. import org.jomc.ant.types.NameType;
  77. import org.jomc.ant.types.PropertiesFormatType;
  78. import org.jomc.ant.types.PropertiesResourceType;
  79. import org.jomc.ant.types.ResourceType;
  80. import org.jomc.ant.types.TransformerResourceType;
  81. import org.jomc.model.ModelObject;
  82. import org.jomc.modlet.DefaultModelContext;
  83. import org.jomc.modlet.DefaultModletProvider;
  84. import org.jomc.modlet.Model;
  85. import org.jomc.modlet.ModelContext;
  86. import org.jomc.modlet.ModelContextFactory;
  87. import org.jomc.modlet.ModelException;
  88. import org.jomc.modlet.ModelValidationReport;
  89. import org.jomc.modlet.ModletProcessor;
  90. import org.jomc.modlet.ModletProvider;
  91. import org.jomc.modlet.ModletValidator;
  92. import org.jomc.modlet.ServiceFactory;

  93. /**
  94.  * Base class for executing tasks.
  95.  *
  96.  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
  97.  * @version $JOMC: JomcTask.java 5301 2016-08-30 02:04:33Z schulte $
  98.  * @see #execute()
  99.  */
  100. public class JomcTask extends Task
  101. {

  102.     /**
  103.      * The class path to process.
  104.      */
  105.     private Path classpath;

  106.     /**
  107.      * The identifier of the model to process.
  108.      */
  109.     private String model;

  110.     /**
  111.      * {@code ModelContext} attributes to apply.
  112.      */
  113.     private List<KeyValueType> modelContextAttributes;

  114.     /**
  115.      * The name of the {@code ModelContextFactory} implementation class backing the task.
  116.      */
  117.     private String modelContextFactoryClassName;

  118.     /**
  119.      * Controls processing of models.
  120.      */
  121.     private boolean modelProcessingEnabled = true;

  122.     /**
  123.      * The location to search for modlets.
  124.      */
  125.     private String modletLocation;

  126.     /**
  127.      * The {@code http://jomc.org/modlet} namespace schema system id of the context backing the task.
  128.      */
  129.     private String modletSchemaSystemId;

  130.     /**
  131.      * The location to search for providers.
  132.      */
  133.     private String providerLocation;

  134.     /**
  135.      * The location to search for platform providers.
  136.      */
  137.     private String platformProviderLocation;

  138.     /**
  139.      * The global transformation parameters to apply.
  140.      */
  141.     private List<KeyValueType> transformationParameters;

  142.     /**
  143.      * The global transformation parameter resources to apply.
  144.      */
  145.     private List<PropertiesResourceType> transformationParameterResources;

  146.     /**
  147.      * The global transformation output properties to apply.
  148.      */
  149.     private List<KeyValueType> transformationOutputProperties;

  150.     /**
  151.      * The flag indicating JAXP schema validation of modlet resources is enabled.
  152.      */
  153.     private boolean modletResourceValidationEnabled = true;

  154.     /**
  155.      * Property controlling the execution of the task.
  156.      */
  157.     private Object _if;

  158.     /**
  159.      * Property controlling the execution of the task.
  160.      */
  161.     private Object unless;

  162.     /**
  163.      * Formula used to calculate the maximum number of threads to create for running tasks in parallel.
  164.      *
  165.      * @since 1.10
  166.      */
  167.     private String threads = "1.0C";

  168.     /**
  169.      * The {@code ExecutorService} of the task.
  170.      *
  171.      * @since 1.10
  172.      */
  173.     private ExecutorService executorService;

  174.     /**
  175.      * Creates a new {@code JomcTask} instance.
  176.      */
  177.     public JomcTask()
  178.     {
  179.         super();
  180.     }

  181.     /**
  182.      * Gets an object controlling the execution of the task.
  183.      *
  184.      * @return An object controlling the execution of the task or {@code null}.
  185.      *
  186.      * @see #setIf(java.lang.Object)
  187.      */
  188.     public final Object getIf()
  189.     {
  190.         return this._if;
  191.     }

  192.     /**
  193.      * Sets an object controlling the execution of the task.
  194.      *
  195.      * @param value The new object controlling the execution of the task or {@code null}.
  196.      *
  197.      * @see #getIf()
  198.      */
  199.     public final void setIf( final Object value )
  200.     {
  201.         this._if = value;
  202.     }

  203.     /**
  204.      * Gets an object controlling the execution of the task.
  205.      *
  206.      * @return An object controlling the execution of the task or {@code null}.
  207.      *
  208.      * @see #setUnless(java.lang.Object)
  209.      */
  210.     public final Object getUnless()
  211.     {
  212.         if ( this.unless == null )
  213.         {
  214.             this.unless = Boolean.TRUE;
  215.         }

  216.         return this.unless;
  217.     }

  218.     /**
  219.      * Sets an object controlling the execution of the task.
  220.      *
  221.      * @param value The new object controlling the execution of the task or {@code null}.
  222.      *
  223.      * @see #getUnless()
  224.      */
  225.     public final void setUnless( final Object value )
  226.     {
  227.         this.unless = value;
  228.     }

  229.     /**
  230.      * Creates a new {@code classpath} element instance.
  231.      *
  232.      * @return A new {@code classpath} element instance.
  233.      */
  234.     public final Path createClasspath()
  235.     {
  236.         return this.getClasspath().createPath();
  237.     }

  238.     /**
  239.      * Gets the class path to process.
  240.      *
  241.      * @return The class path to process.
  242.      *
  243.      * @see #setClasspath(org.apache.tools.ant.types.Path)
  244.      */
  245.     public final Path getClasspath()
  246.     {
  247.         if ( this.classpath == null )
  248.         {
  249.             this.classpath = new Path( this.getProject() );
  250.         }

  251.         return this.classpath;
  252.     }

  253.     /**
  254.      * Adds to the class path to process.
  255.      *
  256.      * @param value The path to add to the list of class path elements.
  257.      *
  258.      * @see #getClasspath()
  259.      */
  260.     public final void setClasspath( final Path value )
  261.     {
  262.         this.getClasspath().add( value );
  263.     }

  264.     /**
  265.      * Adds a reference to a class path defined elsewhere.
  266.      *
  267.      * @param value A reference to a class path.
  268.      *
  269.      * @see #getClasspath()
  270.      */
  271.     public final void setClasspathRef( final Reference value )
  272.     {
  273.         this.getClasspath().setRefid( value );
  274.     }

  275.     /**
  276.      * Gets the identifier of the model to process.
  277.      *
  278.      * @return The identifier of the model to process.
  279.      *
  280.      * @see #setModel(java.lang.String)
  281.      */
  282.     public final String getModel()
  283.     {
  284.         if ( this.model == null )
  285.         {
  286.             this.model = ModelObject.MODEL_PUBLIC_ID;
  287.         }

  288.         return this.model;
  289.     }

  290.     /**
  291.      * Sets the identifier of the model to process.
  292.      *
  293.      * @param value The new identifier of the model to process or {@code null}.
  294.      *
  295.      * @see #getModel()
  296.      */
  297.     public final void setModel( final String value )
  298.     {
  299.         this.model = value;
  300.     }

  301.     /**
  302.      * Gets the {@code ModelContext} attributes to apply.
  303.      * <p>
  304.      * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
  305.      * to the returned list will be present inside the object. This is why there is no {@code set} method for the
  306.      * model context attributes property.
  307.      * </p>
  308.      *
  309.      * @return The {@code ModelContext} attributes to apply.
  310.      *
  311.      * @see #createModelContextAttribute()
  312.      * @see #newModelContext(java.lang.ClassLoader)
  313.      */
  314.     public final List<KeyValueType> getModelContextAttributes()
  315.     {
  316.         if ( this.modelContextAttributes == null )
  317.         {
  318.             this.modelContextAttributes = new LinkedList<KeyValueType>();
  319.         }

  320.         return this.modelContextAttributes;
  321.     }

  322.     /**
  323.      * Creates a new {@code modelContextAttribute} element instance.
  324.      *
  325.      * @return A new {@code modelContextAttribute} element instance.
  326.      *
  327.      * @see #getModelContextAttributes()
  328.      */
  329.     public KeyValueType createModelContextAttribute()
  330.     {
  331.         final KeyValueType modelContextAttribute = new KeyValueType();
  332.         this.getModelContextAttributes().add( modelContextAttribute );
  333.         return modelContextAttribute;
  334.     }

  335.     /**
  336.      * Gets the name of the {@code ModelContextFactory} implementation class backing the task.
  337.      *
  338.      * @return The name of the {@code ModelContextFactory} implementation class backing the task or {@code null}.
  339.      *
  340.      * @see #setModelContextFactoryClassName(java.lang.String)
  341.      */
  342.     public final String getModelContextFactoryClassName()
  343.     {
  344.         return this.modelContextFactoryClassName;
  345.     }

  346.     /**
  347.      * Sets the name of the {@code ModelContextFactory} implementation class backing the task.
  348.      *
  349.      * @param value The new name of the {@code ModelContextFactory} implementation class backing the task or
  350.      * {@code null}.
  351.      *
  352.      * @see #getModelContextFactoryClassName()
  353.      */
  354.     public final void setModelContextFactoryClassName( final String value )
  355.     {
  356.         this.modelContextFactoryClassName = value;
  357.     }

  358.     /**
  359.      * Gets a flag indicating the processing of models is enabled.
  360.      *
  361.      * @return {@code true}, if processing of models is enabled; {@code false}, else.
  362.      *
  363.      * @see #setModelProcessingEnabled(boolean)
  364.      */
  365.     public final boolean isModelProcessingEnabled()
  366.     {
  367.         return this.modelProcessingEnabled;
  368.     }

  369.     /**
  370.      * Sets the flag indicating the processing of models is enabled.
  371.      *
  372.      * @param value {@code true}, to enable processing of models; {@code false}, to disable processing of models.
  373.      *
  374.      * @see #isModelProcessingEnabled()
  375.      */
  376.     public final void setModelProcessingEnabled( final boolean value )
  377.     {
  378.         this.modelProcessingEnabled = value;
  379.     }

  380.     /**
  381.      * Gets the location searched for modlets.
  382.      *
  383.      * @return The location searched for modlets or {@code null}.
  384.      *
  385.      * @see #setModletLocation(java.lang.String)
  386.      */
  387.     public final String getModletLocation()
  388.     {
  389.         return this.modletLocation;
  390.     }

  391.     /**
  392.      * Sets the location to search for modlets.
  393.      *
  394.      * @param value The new location to search for modlets or {@code null}.
  395.      *
  396.      * @see #getModletLocation()
  397.      */
  398.     public final void setModletLocation( final String value )
  399.     {
  400.         this.modletLocation = value;
  401.     }

  402.     /**
  403.      * Gets the {@code http://jomc.org/modlet} namespace schema system id of the context backing the task.
  404.      *
  405.      * @return The {@code http://jomc.org/modlet} namespace schema system id of the context backing the task or
  406.      * {@code null}.
  407.      *
  408.      * @see #setModletSchemaSystemId(java.lang.String)
  409.      */
  410.     public final String getModletSchemaSystemId()
  411.     {
  412.         return this.modletSchemaSystemId;
  413.     }

  414.     /**
  415.      * Sets the {@code http://jomc.org/modlet} namespace schema system id of the context backing the task.
  416.      *
  417.      * @param value The new {@code http://jomc.org/modlet} namespace schema system id of the context backing the task or
  418.      * {@code null}.
  419.      *
  420.      * @see #getModletSchemaSystemId()
  421.      */
  422.     public final void setModletSchemaSystemId( final String value )
  423.     {
  424.         this.modletSchemaSystemId = value;
  425.     }

  426.     /**
  427.      * Gets the location searched for providers.
  428.      *
  429.      * @return The location searched for providers or {@code null}.
  430.      *
  431.      * @see #setProviderLocation(java.lang.String)
  432.      */
  433.     public final String getProviderLocation()
  434.     {
  435.         return this.providerLocation;
  436.     }

  437.     /**
  438.      * Sets the location to search for providers.
  439.      *
  440.      * @param value The new location to search for providers or {@code null}.
  441.      *
  442.      * @see #getProviderLocation()
  443.      */
  444.     public final void setProviderLocation( final String value )
  445.     {
  446.         this.providerLocation = value;
  447.     }

  448.     /**
  449.      * Gets the location searched for platform provider resources.
  450.      *
  451.      * @return The location searched for platform provider resources or {@code null}.
  452.      *
  453.      * @see #setPlatformProviderLocation(java.lang.String)
  454.      */
  455.     public final String getPlatformProviderLocation()
  456.     {
  457.         return this.platformProviderLocation;
  458.     }

  459.     /**
  460.      * Sets the location to search for platform provider resources.
  461.      *
  462.      * @param value The new location to search for platform provider resources or {@code null}.
  463.      *
  464.      * @see #getPlatformProviderLocation()
  465.      */
  466.     public final void setPlatformProviderLocation( final String value )
  467.     {
  468.         this.platformProviderLocation = value;
  469.     }

  470.     /**
  471.      * Gets the global transformation parameters to apply.
  472.      * <p>
  473.      * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
  474.      * to the returned list will be present inside the object. This is why there is no {@code set} method for the
  475.      * transformation parameters property.
  476.      * </p>
  477.      *
  478.      * @return The global transformation parameters to apply.
  479.      *
  480.      * @see #createTransformationParameter()
  481.      * @see #getTransformer(org.jomc.ant.types.TransformerResourceType)
  482.      */
  483.     public final List<KeyValueType> getTransformationParameters()
  484.     {
  485.         if ( this.transformationParameters == null )
  486.         {
  487.             this.transformationParameters = new LinkedList<KeyValueType>();
  488.         }

  489.         return this.transformationParameters;
  490.     }

  491.     /**
  492.      * Creates a new {@code transformationParameter} element instance.
  493.      *
  494.      * @return A new {@code transformationParameter} element instance.
  495.      *
  496.      * @see #getTransformationParameters()
  497.      */
  498.     public KeyValueType createTransformationParameter()
  499.     {
  500.         final KeyValueType transformationParameter = new KeyValueType();
  501.         this.getTransformationParameters().add( transformationParameter );
  502.         return transformationParameter;
  503.     }

  504.     /**
  505.      * Gets the global transformation parameter resources to apply.
  506.      * <p>
  507.      * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
  508.      * to the returned list will be present inside the object. This is why there is no {@code set} method for the
  509.      * transformation parameter resources property.
  510.      * </p>
  511.      *
  512.      * @return The global transformation parameter resources to apply.
  513.      *
  514.      * @see #createTransformationParameterResource()
  515.      * @see #getTransformer(org.jomc.ant.types.TransformerResourceType)
  516.      */
  517.     public final List<PropertiesResourceType> getTransformationParameterResources()
  518.     {
  519.         if ( this.transformationParameterResources == null )
  520.         {
  521.             this.transformationParameterResources = new LinkedList<PropertiesResourceType>();
  522.         }

  523.         return this.transformationParameterResources;
  524.     }

  525.     /**
  526.      * Creates a new {@code transformationParameterResource} element instance.
  527.      *
  528.      * @return A new {@code transformationParameterResource} element instance.
  529.      *
  530.      * @see #getTransformationParameterResources()
  531.      */
  532.     public PropertiesResourceType createTransformationParameterResource()
  533.     {
  534.         final PropertiesResourceType transformationParameterResource = new PropertiesResourceType();
  535.         this.getTransformationParameterResources().add( transformationParameterResource );
  536.         return transformationParameterResource;
  537.     }

  538.     /**
  539.      * Gets the global transformation output properties to apply.
  540.      * <p>
  541.      * This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
  542.      * to the returned list will be present inside the object. This is why there is no {@code set} method for the
  543.      * transformation output properties property.
  544.      * </p>
  545.      *
  546.      * @return The global transformation output properties to apply.
  547.      *
  548.      * @see #createTransformationOutputProperty()
  549.      */
  550.     public final List<KeyValueType> getTransformationOutputProperties()
  551.     {
  552.         if ( this.transformationOutputProperties == null )
  553.         {
  554.             this.transformationOutputProperties = new LinkedList<KeyValueType>();
  555.         }

  556.         return this.transformationOutputProperties;
  557.     }

  558.     /**
  559.      * Creates a new {@code transformationOutputProperty} element instance.
  560.      *
  561.      * @return A new {@code transformationOutputProperty} element instance.
  562.      *
  563.      * @see #getTransformationOutputProperties()
  564.      */
  565.     public KeyValueType createTransformationOutputProperty()
  566.     {
  567.         final KeyValueType transformationOutputProperty = new KeyValueType();
  568.         this.getTransformationOutputProperties().add( transformationOutputProperty );
  569.         return transformationOutputProperty;
  570.     }

  571.     /**
  572.      * Gets a flag indicating JAXP schema validation of modlet resources is enabled.
  573.      *
  574.      * @return {@code true}, if JAXP schema validation of modlet resources is enabled; {@code false}, else.
  575.      *
  576.      * @see #setModletResourceValidationEnabled(boolean)
  577.      */
  578.     public final boolean isModletResourceValidationEnabled()
  579.     {
  580.         return this.modletResourceValidationEnabled;
  581.     }

  582.     /**
  583.      * Sets the flag indicating JAXP schema validation of modlet resources is enabled.
  584.      *
  585.      * @param value {@code true}, to enable JAXP schema validation of modlet resources; {@code false}, to disable JAXP
  586.      * schema validation of modlet resources.
  587.      *
  588.      * @see #isModletResourceValidationEnabled()
  589.      */
  590.     public final void setModletResourceValidationEnabled( final boolean value )
  591.     {
  592.         this.modletResourceValidationEnabled = value;
  593.     }

  594.     /**
  595.      * Gets a formula used to calculate the maximum number of threads to create for running tasks in parallel. If the
  596.      * formular contains the character {@code C}, the number of threads will be calculated by multiplying the value by
  597.      * the number of available processors. The default number of threads is the number of available processors (1.0C).
  598.      *
  599.      * @return A formula used to calculate the number of threads.
  600.      *
  601.      * @see Runtime#availableProcessors()
  602.      *
  603.      * @since 1.10
  604.      */
  605.     public final String getThreads()
  606.     {
  607.         return this.threads;
  608.     }

  609.     /**
  610.      * Sets the formula to use to calculate the maximum number of threads to create for running tasks in parallel. If
  611.      * the formular contains the character {@code C}, the number of threads will be calculated by multiplying the value
  612.      * by the number of available processors. The default number of threads is the number of available processors
  613.      * (1.0C).
  614.      *
  615.      * @param value The formula to use to calculate the maximum number of threads or {@code null}, to disable any
  616.      * parallelism.
  617.      *
  618.      * @since 1.10
  619.      */
  620.     public final void setThreads( final String value )
  621.     {
  622.         this.threads = value;
  623.     }

  624.     /**
  625.      * Gets the {@code ExecutorService} used to run tasks in parallel.
  626.      *
  627.      * @return The {@code ExecutorService} used to run tasks in parallel or {@code null}, if the maximum number of
  628.      * threads to create for running tasks in parallel is not greater than 1.
  629.      *
  630.      * @since 1.10
  631.      */
  632.     protected final ExecutorService getExecutorService()
  633.     {
  634.         if ( this.executorService == null )
  635.         {
  636.             final Double parallelism =
  637.                 this.getThreads().toLowerCase( new Locale( "" ) ).contains( "c" )
  638.                     ? Double.valueOf( this.getThreads().toLowerCase( new Locale( "" ) ).replace( "c", "" ) )
  639.                           * Runtime.getRuntime().availableProcessors()
  640.                     : Double.valueOf( this.getThreads() );

  641.             if ( parallelism.intValue() > 1 )
  642.             {
  643.                 this.executorService = Executors.newFixedThreadPool(
  644.                     parallelism.intValue(), new ThreadFactory()
  645.                 {

  646.                     private final ThreadGroup group;

  647.                     private final AtomicInteger threadNumber = new AtomicInteger( 1 );


  648.                     {
  649.                         final SecurityManager s = System.getSecurityManager();
  650.                         this.group = s != null
  651.                                          ? s.getThreadGroup()
  652.                                          : Thread.currentThread().getThreadGroup();

  653.                     }

  654.                     @Override
  655.                     public Thread newThread( final Runnable r )
  656.                     {
  657.                         final Thread t =
  658.                             new Thread( this.group, r, "jomc-ant-tasks-" + this.threadNumber.getAndIncrement(), 0 );

  659.                         if ( t.isDaemon() )
  660.                         {
  661.                             t.setDaemon( false );
  662.                         }
  663.                         if ( t.getPriority() != Thread.NORM_PRIORITY )
  664.                         {
  665.                             t.setPriority( Thread.NORM_PRIORITY );
  666.                         }

  667.                         return t;
  668.                     }

  669.                 } );
  670.             }
  671.         }

  672.         return this.executorService;
  673.     }

  674.     /**
  675.      * Called by the project to let the task do its work.
  676.      *
  677.      * @throws BuildException if execution fails.
  678.      *
  679.      * @see #getIf()
  680.      * @see #getUnless()
  681.      * @see #preExecuteTask()
  682.      * @see #executeTask()
  683.      * @see #postExecuteTask()
  684.      */
  685.     @Override
  686.     public final void execute() throws BuildException
  687.     {
  688.         final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper( this.getProject() );

  689.         if ( propertyHelper.testIfCondition( this.getIf() ) && !propertyHelper.testUnlessCondition( this.getUnless() ) )
  690.         {
  691.             try
  692.             {
  693.                 this.preExecuteTask();
  694.                 this.executeTask();
  695.             }
  696.             finally
  697.             {
  698.                 try
  699.                 {
  700.                     this.postExecuteTask();
  701.                 }
  702.                 finally
  703.                 {
  704.                     if ( this.executorService != null )
  705.                     {
  706.                         this.executorService.shutdown();
  707.                         this.executorService = null;
  708.                     }
  709.                 }
  710.             }
  711.         }
  712.     }

  713.     /**
  714.      * Called by the {@code execute} method prior to the {@code executeTask} method.
  715.      *
  716.      * @throws BuildException if execution fails.
  717.      *
  718.      * @see #execute()
  719.      */
  720.     public void preExecuteTask() throws BuildException
  721.     {
  722.         this.logSeparator();
  723.         this.log( Messages.getMessage( "title" ) );
  724.         this.logSeparator();

  725.         this.assertNotNull( "model", this.getModel() );
  726.         this.assertKeysNotNull( this.getModelContextAttributes() );
  727.         this.assertKeysNotNull( this.getTransformationParameters() );
  728.         this.assertKeysNotNull( this.getTransformationOutputProperties() );
  729.         this.assertLocationsNotNull( this.getTransformationParameterResources() );
  730.     }

  731.     /**
  732.      * Called by the {@code execute} method prior to the {@code postExecuteTask} method.
  733.      *
  734.      * @throws BuildException if execution fails.
  735.      *
  736.      * @see #execute()
  737.      */
  738.     public void executeTask() throws BuildException
  739.     {
  740.         this.getProject().log( Messages.getMessage( "unimplementedTask", this.getClass().getName(), "executeTask" ),
  741.                                Project.MSG_WARN );

  742.     }

  743.     /**
  744.      * Called by the {@code execute} method after the {@code preExecuteTask}/{@code executeTask} methods even if those
  745.      * methods threw an exception.
  746.      *
  747.      * @throws BuildException if execution fails.
  748.      *
  749.      * @see #execute()
  750.      */
  751.     public void postExecuteTask() throws BuildException
  752.     {
  753.         this.logSeparator();
  754.     }

  755.     /**
  756.      * Gets a {@code Model} from a given {@code ModelContext}.
  757.      *
  758.      * @param context The context to get a {@code Model} from.
  759.      *
  760.      * @return The {@code Model} from {@code context}.
  761.      *
  762.      * @throws NullPointerException if {@code contexŧ} is {@code null}.
  763.      * @throws ModelException if getting the model fails.
  764.      *
  765.      * @see #getModel()
  766.      * @see #isModelProcessingEnabled()
  767.      */
  768.     public Model getModel( final ModelContext context ) throws ModelException
  769.     {
  770.         if ( context == null )
  771.         {
  772.             throw new NullPointerException( "context" );
  773.         }

  774.         Model foundModel = context.findModel( this.getModel() );

  775.         if ( foundModel != null && this.isModelProcessingEnabled() )
  776.         {
  777.             foundModel = context.processModel( foundModel );
  778.         }

  779.         return foundModel;
  780.     }

  781.     /**
  782.      * Creates an {@code URL} for a given resource location.
  783.      * <p>
  784.      * This method first searches the class path of the task for a single resource matching {@code location}. If
  785.      * such a resource is found, the URL of that resource is returned. If no such resource is found, an attempt is made
  786.      * to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given
  787.      * location is interpreted as a file name relative to the project's base directory. If that file is found, the URL
  788.      * of that file is returned. Otherwise {@code null} is returned.
  789.      * </p>
  790.      *
  791.      * @param location The resource location to create an {@code URL} from.
  792.      *
  793.      * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
  794.      * {@code location} points to a non-existent resource.
  795.      *
  796.      * @throws NullPointerException if {@code location} is {@code null}.
  797.      * @throws BuildException if creating an URL fails.
  798.      */
  799.     public URL getResource( final String location ) throws BuildException
  800.     {
  801.         if ( location == null )
  802.         {
  803.             throw new NullPointerException( "location" );
  804.         }

  805.         try
  806.         {
  807.             String absolute = location;
  808.             if ( !absolute.startsWith( "/" ) )
  809.             {
  810.                 absolute = "/" + absolute;
  811.             }

  812.             URL resource = this.getClass().getResource( absolute );
  813.             if ( resource == null )
  814.             {
  815.                 try
  816.                 {
  817.                     resource = new URL( location );
  818.                 }
  819.                 catch ( final MalformedURLException e )
  820.                 {
  821.                     this.log( e, Project.MSG_DEBUG );
  822.                     resource = null;
  823.                 }
  824.             }

  825.             if ( resource == null )
  826.             {
  827.                 final File f = this.getProject().resolveFile( location );

  828.                 if ( f.isFile() )
  829.                 {
  830.                     resource = f.toURI().toURL();
  831.                 }
  832.             }

  833.             return resource;
  834.         }
  835.         catch ( final MalformedURLException e )
  836.         {
  837.             String m = Messages.getMessage( e );
  838.             m = m == null ? "" : " " + m;

  839.             throw new BuildException( Messages.getMessage( "malformedLocation", location, m ), e, this.getLocation() );
  840.         }
  841.     }

  842.     /**
  843.      * Creates an array of {@code URL}s for a given resource location.
  844.      * <p>
  845.      * This method first searches the given context for resources matching {@code location}. If such resources are
  846.      * found, an array of URLs of those resources is returned. If no such resources are found, an attempt is made
  847.      * to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given
  848.      * location is interpreted as a file name relative to the project's base directory. If that file is found, the URL
  849.      * of that file is returned. Otherwise an empty array is returned.
  850.      * </p>
  851.      *
  852.      * @param context The context to search for resources.
  853.      * @param location The resource location to create an array of {@code URL}s from.
  854.      *
  855.      * @return An array of {@code URL}s for {@code location} or an empty array if parsing {@code location} to an URL
  856.      * fails and {@code location} points to non-existent resources.
  857.      *
  858.      * @throws NullPointerException if {@code context} or {@code location} is {@code null}.
  859.      * @throws BuildException if creating an URL array fails.
  860.      */
  861.     public URL[] getResources( final ModelContext context, final String location ) throws BuildException
  862.     {
  863.         if ( context == null )
  864.         {
  865.             throw new NullPointerException( "context" );
  866.         }
  867.         if ( location == null )
  868.         {
  869.             throw new NullPointerException( "location" );
  870.         }

  871.         final Set<URI> uris = new HashSet<URI>( 128 );

  872.         try
  873.         {
  874.             for ( final Enumeration<URL> e = context.findResources( location ); e.hasMoreElements(); )
  875.             {
  876.                 uris.add( e.nextElement().toURI() );
  877.             }
  878.         }
  879.         catch ( final URISyntaxException e )
  880.         {
  881.             this.log( e, Project.MSG_DEBUG );
  882.         }
  883.         catch ( final ModelException e )
  884.         {
  885.             this.log( e, Project.MSG_DEBUG );
  886.         }

  887.         if ( uris.isEmpty() )
  888.         {
  889.             try
  890.             {
  891.                 uris.add( new URL( location ).toURI() );
  892.             }
  893.             catch ( final MalformedURLException e )
  894.             {
  895.                 this.log( e, Project.MSG_DEBUG );
  896.             }
  897.             catch ( final URISyntaxException e )
  898.             {
  899.                 this.log( e, Project.MSG_DEBUG );
  900.             }
  901.         }

  902.         if ( uris.isEmpty() )
  903.         {
  904.             final File f = this.getProject().resolveFile( location );

  905.             if ( f.isFile() )
  906.             {
  907.                 uris.add( f.toURI() );
  908.             }
  909.         }

  910.         int i = 0;
  911.         final URL[] urls = new URL[ uris.size() ];

  912.         for ( final URI uri : uris )
  913.         {
  914.             try
  915.             {
  916.                 urls[i++] = uri.toURL();
  917.             }
  918.             catch ( final MalformedURLException e )
  919.             {
  920.                 String m = Messages.getMessage( e );
  921.                 m = m == null ? "" : " " + m;

  922.                 throw new BuildException( Messages.getMessage( "malformedLocation", uri.toASCIIString(), m ), e,
  923.                                           this.getLocation() );

  924.             }
  925.         }

  926.         return urls;
  927.     }

  928.     /**
  929.      * Creates an {@code URL} for a given directory location.
  930.      * <p>
  931.      * This method first attempts to parse the given location to an URL. On successful parsing, that URL is returned.
  932.      * Failing that, the given location is interpreted as a directory name relative to the project's base directory. If
  933.      * that directory is found, the URL of that directory is returned. Otherwise {@code null} is returned.
  934.      * </p>
  935.      *
  936.      * @param location The directory location to create an {@code URL} from.
  937.      *
  938.      * @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
  939.      * {@code location} points to a non-existent directory.
  940.      *
  941.      * @throws NullPointerException if {@code location} is {@code null}.
  942.      * @throws BuildException if creating an URL fails.
  943.      */
  944.     public URL getDirectory( final String location ) throws BuildException
  945.     {
  946.         if ( location == null )
  947.         {
  948.             throw new NullPointerException( "location" );
  949.         }

  950.         try
  951.         {
  952.             URL resource;

  953.             try
  954.             {
  955.                 resource = new URL( location );
  956.             }
  957.             catch ( final MalformedURLException e )
  958.             {
  959.                 this.log( e, Project.MSG_DEBUG );
  960.                 resource = null;
  961.             }

  962.             if ( resource == null )
  963.             {
  964.                 final File f = this.getProject().resolveFile( location );

  965.                 if ( f.isDirectory() )
  966.                 {
  967.                     resource = f.toURI().toURL();
  968.                 }
  969.             }

  970.             return resource;
  971.         }
  972.         catch ( final MalformedURLException e )
  973.         {
  974.             String m = Messages.getMessage( e );
  975.             m = m == null ? "" : " " + m;

  976.             throw new BuildException( Messages.getMessage( "malformedLocation", location, m ), e, this.getLocation() );
  977.         }
  978.     }

  979.     /**
  980.      * Creates a new {@code Transformer} for a given {@code TransformerResourceType}.
  981.      *
  982.      * @param resource The resource to create a {@code Transformer} of.
  983.      *
  984.      * @return A new {@code Transformer} for {@code resource} or {@code null}, if {@code resource} is not found and
  985.      * flagged optional.
  986.      *
  987.      * @throws TransformerConfigurationException if creating a new {@code Transformer} fails.
  988.      *
  989.      * @see #getTransformationParameterResources()
  990.      * @see #getTransformationParameters()
  991.      * @see #getResource(java.lang.String)
  992.      */
  993.     public Transformer getTransformer( final TransformerResourceType resource ) throws TransformerConfigurationException
  994.     {
  995.         if ( resource == null )
  996.         {
  997.             throw new NullPointerException( "resource" );
  998.         }

  999.         URLConnection con = null;
  1000.         InputStream in = null;
  1001.         final URL url = this.getResource( resource.getLocation() );

  1002.         try
  1003.         {
  1004.             if ( url != null )
  1005.             {
  1006.                 final ErrorListener errorListener = new ErrorListener()
  1007.                 {

  1008.                     public void warning( final TransformerException exception ) throws TransformerException
  1009.                     {
  1010.                         if ( getProject() != null )
  1011.                         {
  1012.                             getProject().log( Messages.getMessage( exception ), exception, Project.MSG_WARN );
  1013.                         }
  1014.                     }

  1015.                     public void error( final TransformerException exception ) throws TransformerException
  1016.                     {
  1017.                         throw exception;
  1018.                     }

  1019.                     public void fatalError( final TransformerException exception ) throws TransformerException
  1020.                     {
  1021.                         throw exception;
  1022.                     }

  1023.                 };

  1024.                 con = url.openConnection();
  1025.                 con.setConnectTimeout( resource.getConnectTimeout() );
  1026.                 con.setReadTimeout( resource.getReadTimeout() );
  1027.                 con.connect();
  1028.                 in = con.getInputStream();

  1029.                 final TransformerFactory f = TransformerFactory.newInstance();
  1030.                 f.setErrorListener( errorListener );
  1031.                 final Transformer transformer = f.newTransformer( new StreamSource( in, url.toURI().toASCIIString() ) );
  1032.                 transformer.setErrorListener( errorListener );

  1033.                 for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() )
  1034.                 {
  1035.                     transformer.setParameter( e.getKey().toString(), e.getValue() );
  1036.                 }

  1037.                 for ( final Iterator<Map.Entry<?, ?>> it = this.getProject().getProperties().entrySet().iterator();
  1038.                       it.hasNext(); )
  1039.                 {
  1040.                     final Map.Entry<?, ?> e = it.next();
  1041.                     transformer.setParameter( e.getKey().toString(), e.getValue() );
  1042.                 }

  1043.                 for ( int i = 0, s0 = this.getTransformationParameterResources().size(); i < s0; i++ )
  1044.                 {
  1045.                     for ( final Map.Entry<Object, Object> e
  1046.                               : this.getProperties( this.getTransformationParameterResources().get( i ) ).entrySet() )
  1047.                     {
  1048.                         transformer.setParameter( e.getKey().toString(), e.getValue() );
  1049.                     }
  1050.                 }

  1051.                 for ( int i = 0, s0 = this.getTransformationParameters().size(); i < s0; i++ )
  1052.                 {
  1053.                     final KeyValueType p = this.getTransformationParameters().get( i );
  1054.                     transformer.setParameter( p.getKey(), p.getObject( this.getLocation() ) );
  1055.                 }

  1056.                 for ( int i = 0, s0 = this.getTransformationOutputProperties().size(); i < s0; i++ )
  1057.                 {
  1058.                     final KeyValueType p = this.getTransformationOutputProperties().get( i );
  1059.                     transformer.setOutputProperty( p.getKey(), p.getValue() );
  1060.                 }

  1061.                 for ( int i = 0, s0 = resource.getTransformationParameterResources().size(); i < s0; i++ )
  1062.                 {
  1063.                     for ( final Map.Entry<Object, Object> e
  1064.                               : this.getProperties( resource.getTransformationParameterResources().get( i ) ).
  1065.                         entrySet() )
  1066.                     {
  1067.                         transformer.setParameter( e.getKey().toString(), e.getValue() );
  1068.                     }
  1069.                 }

  1070.                 for ( int i = 0, s0 = resource.getTransformationParameters().size(); i < s0; i++ )
  1071.                 {
  1072.                     final KeyValueType p = resource.getTransformationParameters().get( i );
  1073.                     transformer.setParameter( p.getKey(), p.getObject( this.getLocation() ) );
  1074.                 }

  1075.                 for ( int i = 0, s0 = resource.getTransformationOutputProperties().size(); i < s0; i++ )
  1076.                 {
  1077.                     final KeyValueType p = resource.getTransformationOutputProperties().get( i );
  1078.                     transformer.setOutputProperty( p.getKey(), p.getValue() );
  1079.                 }

  1080.                 in.close();
  1081.                 in = null;

  1082.                 return transformer;
  1083.             }
  1084.             else if ( resource.isOptional() )
  1085.             {
  1086.                 this.log( Messages.getMessage( "transformerNotFound", resource.getLocation() ), Project.MSG_WARN );
  1087.             }
  1088.             else
  1089.             {
  1090.                 throw new BuildException( Messages.getMessage( "transformerNotFound", resource.getLocation() ),
  1091.                                           this.getLocation() );

  1092.             }
  1093.         }
  1094.         catch ( final URISyntaxException e )
  1095.         {
  1096.             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
  1097.         }
  1098.         catch ( final SocketTimeoutException e )
  1099.         {
  1100.             final String message = Messages.getMessage( e );

  1101.             if ( resource.isOptional() )
  1102.             {
  1103.                 this.getProject().log( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ),
  1104.                                        e, Project.MSG_WARN );

  1105.             }
  1106.             else
  1107.             {
  1108.                 throw new BuildException( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ),
  1109.                                           e, this.getLocation() );

  1110.             }
  1111.         }
  1112.         catch ( final IOException e )
  1113.         {
  1114.             final String message = Messages.getMessage( e );

  1115.             if ( resource.isOptional() )
  1116.             {
  1117.                 this.getProject().log( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ),
  1118.                                        e, Project.MSG_WARN );

  1119.             }
  1120.             else
  1121.             {
  1122.                 throw new BuildException( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ),
  1123.                                           e, this.getLocation() );

  1124.             }
  1125.         }
  1126.         finally
  1127.         {
  1128.             try
  1129.             {
  1130.                 if ( in != null )
  1131.                 {
  1132.                     in.close();
  1133.                 }
  1134.             }
  1135.             catch ( final IOException e )
  1136.             {
  1137.                 this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
  1138.             }
  1139.             finally
  1140.             {
  1141.                 if ( con instanceof HttpURLConnection )
  1142.                 {
  1143.                     ( (HttpURLConnection) con ).disconnect();
  1144.                 }
  1145.             }
  1146.         }

  1147.         return null;
  1148.     }

  1149.     /**
  1150.      * Creates a new {@code Properties} instance from a {@code PropertiesResourceType}.
  1151.      *
  1152.      * @param propertiesResourceType The {@code PropertiesResourceType} specifying the properties to create.
  1153.      *
  1154.      * @return The properties for {@code propertiesResourceType}.
  1155.      *
  1156.      * @throws NullPointerException if {@code propertiesResourceType} is {@code null}.
  1157.      * @throws BuildException if loading properties fails.
  1158.      */
  1159.     public Properties getProperties( final PropertiesResourceType propertiesResourceType ) throws BuildException
  1160.     {
  1161.         if ( propertiesResourceType == null )
  1162.         {
  1163.             throw new NullPointerException( "propertiesResourceType" );
  1164.         }

  1165.         URLConnection con = null;
  1166.         InputStream in = null;
  1167.         final Properties properties = new Properties();
  1168.         final URL url = this.getResource( propertiesResourceType.getLocation() );

  1169.         try
  1170.         {
  1171.             if ( url != null )
  1172.             {
  1173.                 con = url.openConnection();
  1174.                 con.setConnectTimeout( propertiesResourceType.getConnectTimeout() );
  1175.                 con.setReadTimeout( propertiesResourceType.getReadTimeout() );
  1176.                 con.connect();

  1177.                 in = con.getInputStream();

  1178.                 if ( propertiesResourceType.getFormat() == PropertiesFormatType.PLAIN )
  1179.                 {
  1180.                     properties.load( in );
  1181.                 }
  1182.                 else if ( propertiesResourceType.getFormat() == PropertiesFormatType.XML )
  1183.                 {
  1184.                     properties.loadFromXML( in );
  1185.                 }

  1186.                 in.close();
  1187.                 in = null;
  1188.             }
  1189.             else if ( propertiesResourceType.isOptional() )
  1190.             {
  1191.                 this.log( Messages.getMessage( "propertiesNotFound", propertiesResourceType.getLocation() ),
  1192.                           Project.MSG_WARN );

  1193.             }
  1194.             else
  1195.             {
  1196.                 throw new BuildException( Messages.getMessage(
  1197.                     "propertiesNotFound", propertiesResourceType.getLocation() ), this.getLocation() );

  1198.             }
  1199.         }
  1200.         catch ( final SocketTimeoutException e )
  1201.         {
  1202.             final String message = Messages.getMessage( e );

  1203.             if ( propertiesResourceType.isOptional() )
  1204.             {
  1205.                 this.getProject().log( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ),
  1206.                                        e, Project.MSG_WARN );

  1207.             }
  1208.             else
  1209.             {
  1210.                 throw new BuildException( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ),
  1211.                                           e, this.getLocation() );

  1212.             }
  1213.         }
  1214.         catch ( final IOException e )
  1215.         {
  1216.             final String message = Messages.getMessage( e );

  1217.             if ( propertiesResourceType.isOptional() )
  1218.             {
  1219.                 this.getProject().log( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ),
  1220.                                        e, Project.MSG_WARN );

  1221.             }
  1222.             else
  1223.             {
  1224.                 throw new BuildException( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ),
  1225.                                           e, this.getLocation() );

  1226.             }
  1227.         }
  1228.         finally
  1229.         {
  1230.             try
  1231.             {
  1232.                 if ( in != null )
  1233.                 {
  1234.                     in.close();
  1235.                 }
  1236.             }
  1237.             catch ( final IOException e )
  1238.             {
  1239.                 this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
  1240.             }
  1241.             finally
  1242.             {
  1243.                 if ( con instanceof HttpURLConnection )
  1244.                 {
  1245.                     ( (HttpURLConnection) con ).disconnect();
  1246.                 }
  1247.             }
  1248.         }

  1249.         return properties;
  1250.     }

  1251.     /**
  1252.      * Creates a new {@code ProjectClassLoader} instance.
  1253.      *
  1254.      * @return A new {@code ProjectClassLoader} instance.
  1255.      *
  1256.      * @throws BuildException if creating a new class loader instance fails.
  1257.      */
  1258.     public ProjectClassLoader newProjectClassLoader() throws BuildException
  1259.     {
  1260.         try
  1261.         {
  1262.             final ProjectClassLoader classLoader = new ProjectClassLoader( this.getProject(), this.getClasspath() );

  1263.             // Assumes the default modlet location matches the location of resources of the tasks' dependencies.
  1264.             classLoader.getModletResourceLocations().add( DefaultModletProvider.getDefaultModletLocation() );
  1265.             classLoader.getModletExcludes().addAll( ProjectClassLoader.getDefaultModletExcludes() );
  1266.             classLoader.getSchemaExcludes().addAll( ProjectClassLoader.getDefaultSchemaExcludes() );
  1267.             classLoader.getServiceExcludes().addAll( ProjectClassLoader.getDefaultServiceExcludes() );

  1268.             // Assumes the default provider location matches the location of resources of the tasks' dependencies.
  1269.             final String providerLocationPrefix = DefaultModelContext.getDefaultProviderLocation() + "/";
  1270.             classLoader.getProviderResourceLocations().add( providerLocationPrefix + ModletProcessor.class.getName() );
  1271.             classLoader.getProviderResourceLocations().add( providerLocationPrefix + ModletProvider.class.getName() );
  1272.             classLoader.getProviderResourceLocations().add( providerLocationPrefix + ModletValidator.class.getName() );
  1273.             classLoader.getProviderResourceLocations().add( providerLocationPrefix + ServiceFactory.class.getName() );
  1274.             classLoader.getProviderExcludes().addAll( ProjectClassLoader.getDefaultProviderExcludes() );

  1275.             return classLoader;
  1276.         }
  1277.         catch ( final IOException e )
  1278.         {
  1279.             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
  1280.         }
  1281.     }

  1282.     /**
  1283.      * Creates a new {@code ModelContext} instance using a given class loader.
  1284.      *
  1285.      * @param classLoader The class loader to create a new {@code ModelContext} instance with.
  1286.      *
  1287.      * @return A new {@code ModelContext} instance backed by {@code classLoader}.
  1288.      *
  1289.      * @throws ModelException if creating a new {@code ModelContext} instance fails.
  1290.      */
  1291.     public ModelContext newModelContext( final ClassLoader classLoader ) throws ModelException
  1292.     {
  1293.         final ModelContextFactory modelContextFactory =
  1294.             this.modelContextFactoryClassName != null
  1295.                 ? ModelContextFactory.newInstance( this.getModelContextFactoryClassName() )
  1296.                 : ModelContextFactory.newInstance();

  1297.         final ModelContext modelContext = modelContextFactory.newModelContext( classLoader );
  1298.         modelContext.setExecutorService( this.getExecutorService() );
  1299.         modelContext.setLogLevel( Level.ALL );
  1300.         modelContext.setModletSchemaSystemId( this.getModletSchemaSystemId() );

  1301.         modelContext.getListeners().add( new ModelContext.Listener()
  1302.         {

  1303.             @Override
  1304.             public void onLog( final Level level, final String message, final Throwable t )
  1305.             {
  1306.                 super.onLog( level, message, t );
  1307.                 logMessage( level, message, t );
  1308.             }

  1309.         } );

  1310.         if ( this.getProviderLocation() != null )
  1311.         {
  1312.             modelContext.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME,
  1313.                                        this.getProviderLocation() );

  1314.         }

  1315.         if ( this.getPlatformProviderLocation() != null )
  1316.         {
  1317.             modelContext.setAttribute( DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME,
  1318.                                        this.getPlatformProviderLocation() );

  1319.         }

  1320.         if ( this.getModletLocation() != null )
  1321.         {
  1322.             modelContext.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME, this.getModletLocation() );
  1323.         }

  1324.         modelContext.setAttribute( DefaultModletProvider.VALIDATING_ATTRIBUTE_NAME,
  1325.                                    this.isModletResourceValidationEnabled() );

  1326.         for ( int i = 0, s0 = this.getModelContextAttributes().size(); i < s0; i++ )
  1327.         {
  1328.             final KeyValueType kv = this.getModelContextAttributes().get( i );
  1329.             final Object object = kv.getObject( this.getLocation() );

  1330.             if ( object != null )
  1331.             {
  1332.                 modelContext.setAttribute( kv.getKey(), object );
  1333.             }
  1334.             else
  1335.             {
  1336.                 modelContext.clearAttribute( kv.getKey() );
  1337.             }
  1338.         }

  1339.         return modelContext;
  1340.     }

  1341.     /**
  1342.      * Throws a {@code BuildException} on a given {@code null} value.
  1343.      *
  1344.      * @param attributeName The name of a mandatory attribute.
  1345.      * @param value The value of that attribute.
  1346.      *
  1347.      * @throws NullPointerException if {@code attributeName} is {@code null}.
  1348.      * @throws BuildException if {@code value} is {@code null}.
  1349.      */
  1350.     public final void assertNotNull( final String attributeName, final Object value ) throws BuildException
  1351.     {
  1352.         if ( attributeName == null )
  1353.         {
  1354.             throw new NullPointerException( "attributeName" );
  1355.         }

  1356.         if ( value == null )
  1357.         {
  1358.             throw new BuildException( Messages.getMessage( "mandatoryAttribute", attributeName ), this.getLocation() );
  1359.         }
  1360.     }

  1361.     /**
  1362.      * Throws a {@code BuildException} on a {@code null} value of a {@code name} property of a given {@code NameType}
  1363.      * collection.
  1364.      *
  1365.      * @param names The collection holding the {@code NameType} instances to test.
  1366.      *
  1367.      * @throws NullPointerException if {@code names} is {@code null}.
  1368.      * @throws BuildException if a {@code name} property of a given {@code NameType} from the {@code names} collection
  1369.      * holds a {@code null} value.
  1370.      */
  1371.     public final void assertNamesNotNull( final Collection<? extends NameType> names ) throws BuildException
  1372.     {
  1373.         if ( names == null )
  1374.         {
  1375.             throw new NullPointerException( "names" );
  1376.         }

  1377.         for ( final NameType n : names )
  1378.         {
  1379.             this.assertNotNull( "name", n.getName() );
  1380.         }
  1381.     }

  1382.     /**
  1383.      * Throws a {@code BuildException} on a {@code null} value of a {@code key} property of a given {@code KeyValueType}
  1384.      * collection.
  1385.      *
  1386.      * @param keys The collection holding the {@code KeyValueType} instances to test.
  1387.      *
  1388.      * @throws NullPointerException if {@code keys} is {@code null}.
  1389.      * @throws BuildException if a {@code key} property of a given {@code KeyValueType} from the {@code keys} collection
  1390.      * holds a {@code null} value.
  1391.      */
  1392.     public final void assertKeysNotNull( final Collection<? extends KeyValueType> keys ) throws BuildException
  1393.     {
  1394.         if ( keys == null )
  1395.         {
  1396.             throw new NullPointerException( "keys" );
  1397.         }

  1398.         for ( final KeyValueType k : keys )
  1399.         {
  1400.             this.assertNotNull( "key", k.getKey() );
  1401.         }
  1402.     }

  1403.     /**
  1404.      * Throws a {@code BuildException} on a {@code null} value of a {@code location} property of a given
  1405.      * {@code ResourceType} collection.
  1406.      *
  1407.      * @param locations The collection holding the {@code ResourceType} instances to test.
  1408.      *
  1409.      * @throws NullPointerException if {@code locations} is {@code null}.
  1410.      * @throws BuildException if a {@code location} property of a given {@code ResourceType} from the {@code locations}
  1411.      * collection holds a {@code null} value.
  1412.      */
  1413.     public final void assertLocationsNotNull( final Collection<? extends ResourceType> locations )
  1414.         throws BuildException
  1415.     {
  1416.         if ( locations == null )
  1417.         {
  1418.             throw new NullPointerException( "locations" );
  1419.         }

  1420.         for ( final ResourceType r : locations )
  1421.         {
  1422.             assertNotNull( "location", r.getLocation() );

  1423.             if ( r instanceof TransformerResourceType )
  1424.             {
  1425.                 assertKeysNotNull( ( (TransformerResourceType) r ).getTransformationParameters() );
  1426.                 assertLocationsNotNull( ( (TransformerResourceType) r ).getTransformationParameterResources() );
  1427.                 assertKeysNotNull( ( (TransformerResourceType) r ).getTransformationOutputProperties() );
  1428.             }
  1429.         }
  1430.     }

  1431.     /**
  1432.      * Logs a separator string.
  1433.      */
  1434.     public final void logSeparator()
  1435.     {
  1436.         this.log( Messages.getMessage( "separator" ) );
  1437.     }

  1438.     /**
  1439.      * Logs a message at a given level.
  1440.      *
  1441.      * @param level The level to log at.
  1442.      * @param message The message to log.
  1443.      *
  1444.      * @throws BuildException if logging fails.
  1445.      */
  1446.     public final void logMessage( final Level level, final String message ) throws BuildException
  1447.     {
  1448.         BufferedReader reader = null;

  1449.         try
  1450.         {
  1451.             reader = new BufferedReader( new StringReader( message ) );

  1452.             for ( String line = reader.readLine(); line != null; line = reader.readLine() )
  1453.             {
  1454.                 if ( level.intValue() >= Level.SEVERE.intValue() )
  1455.                 {
  1456.                     log( line, Project.MSG_ERR );
  1457.                 }
  1458.                 else if ( level.intValue() >= Level.WARNING.intValue() )
  1459.                 {
  1460.                     log( line, Project.MSG_WARN );
  1461.                 }
  1462.                 else if ( level.intValue() >= Level.INFO.intValue() )
  1463.                 {
  1464.                     log( line, Project.MSG_INFO );
  1465.                 }
  1466.                 else
  1467.                 {
  1468.                     log( line, Project.MSG_DEBUG );
  1469.                 }
  1470.             }

  1471.             reader.close();
  1472.             reader = null;
  1473.         }
  1474.         catch ( final IOException e )
  1475.         {
  1476.             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
  1477.         }
  1478.         finally
  1479.         {
  1480.             try
  1481.             {
  1482.                 if ( reader != null )
  1483.                 {
  1484.                     reader.close();
  1485.                 }
  1486.             }
  1487.             catch ( final IOException e )
  1488.             {
  1489.                 this.log( e, Project.MSG_ERR );
  1490.             }
  1491.         }
  1492.     }

  1493.     /**
  1494.      * Logs a message at a given level.
  1495.      *
  1496.      * @param level The level to log at.
  1497.      * @param message The message to log.
  1498.      * @param throwable The throwable to log.
  1499.      *
  1500.      * @throws BuildException if logging fails.
  1501.      */
  1502.     public final void logMessage( final Level level, final String message, final Throwable throwable )
  1503.         throws BuildException
  1504.     {
  1505.         this.logMessage( level, message );

  1506.         if ( level.intValue() >= Level.SEVERE.intValue() )
  1507.         {
  1508.             log( throwable, Project.MSG_ERR );
  1509.         }
  1510.         else if ( level.intValue() >= Level.WARNING.intValue() )
  1511.         {
  1512.             log( throwable, Project.MSG_WARN );
  1513.         }
  1514.         else if ( level.intValue() >= Level.INFO.intValue() )
  1515.         {
  1516.             log( throwable, Project.MSG_INFO );
  1517.         }
  1518.         else
  1519.         {
  1520.             log( throwable, Project.MSG_DEBUG );
  1521.         }
  1522.     }

  1523.     /**
  1524.      * Logs a validation report.
  1525.      *
  1526.      * @param context The context to use for logging the report.
  1527.      * @param report The report to log.
  1528.      *
  1529.      * @throws NullPointerException if {@code context} or {@code report} is {@code null}.
  1530.      * @throws BuildException if logging fails.
  1531.      */
  1532.     public final void logValidationReport( final ModelContext context, final ModelValidationReport report )
  1533.     {
  1534.         try
  1535.         {
  1536.             if ( !report.getDetails().isEmpty() )
  1537.             {
  1538.                 this.logSeparator();
  1539.                 Marshaller marshaller = null;

  1540.                 for ( final ModelValidationReport.Detail detail : report.getDetails() )
  1541.                 {
  1542.                     this.logMessage( detail.getLevel(), "o " + detail.getMessage() );

  1543.                     if ( detail.getElement() != null )
  1544.                     {
  1545.                         if ( marshaller == null )
  1546.                         {
  1547.                             marshaller = context.createMarshaller( this.getModel() );
  1548.                             marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
  1549.                         }

  1550.                         final StringWriter stringWriter = new StringWriter();
  1551.                         marshaller.marshal( detail.getElement(), stringWriter );
  1552.                         this.logMessage( Level.FINEST, stringWriter.toString() );
  1553.                     }
  1554.                 }
  1555.             }
  1556.         }
  1557.         catch ( final ModelException e )
  1558.         {
  1559.             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
  1560.         }
  1561.         catch ( final JAXBException e )
  1562.         {
  1563.             String message = Messages.getMessage( e );
  1564.             if ( message == null && e.getLinkedException() != null )
  1565.             {
  1566.                 message = Messages.getMessage( e.getLinkedException() );
  1567.             }

  1568.             throw new BuildException( message, e, this.getLocation() );
  1569.         }
  1570.     }

  1571.     /**
  1572.      * Creates and returns a copy of this object.
  1573.      *
  1574.      * @return A copy of this object.
  1575.      */
  1576.     @Override
  1577.     public JomcTask clone()
  1578.     {
  1579.         try
  1580.         {
  1581.             final JomcTask clone = (JomcTask) super.clone();
  1582.             clone.executorService = this.executorService;
  1583.             clone.classpath = (Path) ( this.classpath != null ? this.classpath.clone() : null );

  1584.             if ( this.modelContextAttributes != null )
  1585.             {
  1586.                 clone.modelContextAttributes = new ArrayList<KeyValueType>( this.modelContextAttributes.size() );

  1587.                 for ( final KeyValueType e : this.modelContextAttributes )
  1588.                 {
  1589.                     clone.modelContextAttributes.add( e.clone() );
  1590.                 }
  1591.             }

  1592.             if ( this.transformationParameters != null )
  1593.             {
  1594.                 clone.transformationParameters =
  1595.                     new ArrayList<KeyValueType>( this.transformationParameters.size() );

  1596.                 for ( final KeyValueType e : this.transformationParameters )
  1597.                 {
  1598.                     clone.transformationParameters.add( e.clone() );
  1599.                 }
  1600.             }

  1601.             if ( this.transformationParameterResources != null )
  1602.             {
  1603.                 clone.transformationParameterResources =
  1604.                     new ArrayList<PropertiesResourceType>( this.transformationParameterResources.size() );

  1605.                 for ( final PropertiesResourceType e : this.transformationParameterResources )
  1606.                 {
  1607.                     clone.transformationParameterResources.add( e.clone() );
  1608.                 }
  1609.             }

  1610.             if ( this.transformationOutputProperties != null )
  1611.             {
  1612.                 clone.transformationOutputProperties =
  1613.                     new ArrayList<KeyValueType>( this.transformationOutputProperties.size() );

  1614.                 for ( final KeyValueType e : this.transformationOutputProperties )
  1615.                 {
  1616.                     clone.transformationOutputProperties.add( e.clone() );
  1617.                 }
  1618.             }

  1619.             return clone;
  1620.         }
  1621.         catch ( final CloneNotSupportedException e )
  1622.         {
  1623.             throw new AssertionError( e );
  1624.         }
  1625.     }

  1626. }