JomcModelTask.java

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

  32. import java.io.IOException;
  33. import java.io.InputStream;
  34. import java.net.SocketTimeoutException;
  35. import java.net.URISyntaxException;
  36. import java.net.URL;
  37. import java.net.URLConnection;
  38. import java.util.HashSet;
  39. import java.util.Set;
  40. import java.util.logging.Level;
  41. import javax.xml.bind.JAXBElement;
  42. import javax.xml.bind.JAXBException;
  43. import javax.xml.bind.Unmarshaller;
  44. import javax.xml.transform.Source;
  45. import javax.xml.transform.stream.StreamSource;
  46. import org.apache.tools.ant.BuildException;
  47. import org.apache.tools.ant.Project;
  48. import org.jomc.ant.types.KeyValueType;
  49. import org.jomc.ant.types.ModuleResourceType;
  50. import org.jomc.ant.types.ResourceType;
  51. import org.jomc.model.Module;
  52. import org.jomc.model.Modules;
  53. import org.jomc.model.modlet.DefaultModelProcessor;
  54. import org.jomc.model.modlet.DefaultModelProvider;
  55. import org.jomc.model.modlet.DefaultModelValidator;
  56. import org.jomc.model.modlet.ModelHelper;
  57. import org.jomc.modlet.Model;
  58. import org.jomc.modlet.ModelContext;
  59. import org.jomc.modlet.ModelException;
  60. import org.jomc.tools.modlet.ToolsModelProcessor;
  61. import org.jomc.tools.modlet.ToolsModelProvider;

  62. /**
  63.  * Base class for executing model based tasks.
  64.  *
  65.  * @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
  66.  * @version $JOMC: JomcModelTask.java 5043 2015-05-27 07:03:39Z schulte $
  67.  */
  68. public class JomcModelTask extends JomcTask
  69. {

  70.     /**
  71.      * Controls model object class path resolution.
  72.      */
  73.     private boolean modelObjectClasspathResolutionEnabled = true;

  74.     /**
  75.      * The location to search for modules.
  76.      */
  77.     private String moduleLocation;

  78.     /**
  79.      * The location to search for transformers.
  80.      */
  81.     private String transformerLocation;

  82.     /**
  83.      * Module resources.
  84.      */
  85.     private Set<ModuleResourceType> moduleResources;

  86.     /**
  87.      * The flag indicating JAXP schema validation of model resources is enabled.
  88.      */
  89.     private boolean modelResourceValidationEnabled = true;

  90.     /**
  91.      * The flag indicating Java validation is enabled.
  92.      */
  93.     private boolean javaValidationEnabled = true;

  94.     /**
  95.      * Creates a new {@code JomcModelTask} instance.
  96.      */
  97.     public JomcModelTask()
  98.     {
  99.         super();
  100.     }

  101.     /**
  102.      * Gets the location searched for modules.
  103.      *
  104.      * @return The location searched for modules or {@code null}.
  105.      *
  106.      * @see #setModuleLocation(java.lang.String)
  107.      */
  108.     public final String getModuleLocation()
  109.     {
  110.         return this.moduleLocation;
  111.     }

  112.     /**
  113.      * Sets the location to search for modules.
  114.      *
  115.      * @param value The new location to search for modules or {@code null}.
  116.      *
  117.      * @see #getModuleLocation()
  118.      */
  119.     public final void setModuleLocation( final String value )
  120.     {
  121.         this.moduleLocation = value;
  122.     }

  123.     /**
  124.      * Gets the location searched for transformers.
  125.      *
  126.      * @return The location searched for transformers or {@code null}.
  127.      *
  128.      * @see #setTransformerLocation(java.lang.String)
  129.      */
  130.     public final String getTransformerLocation()
  131.     {
  132.         return this.transformerLocation;
  133.     }

  134.     /**
  135.      * Sets the location to search for transformers.
  136.      *
  137.      * @param value The new location to search for transformers or {@code null}.
  138.      *
  139.      * @see #getTransformerLocation()
  140.      */
  141.     public final void setTransformerLocation( final String value )
  142.     {
  143.         this.transformerLocation = value;
  144.     }

  145.     /**
  146.      * Gets a flag indicating model object class path resolution is enabled.
  147.      *
  148.      * @return {@code true}, if model object class path resolution is enabled; {@code false}, else.
  149.      *
  150.      * @see #setModelObjectClasspathResolutionEnabled(boolean)
  151.      */
  152.     public final boolean isModelObjectClasspathResolutionEnabled()
  153.     {
  154.         return this.modelObjectClasspathResolutionEnabled;
  155.     }

  156.     /**
  157.      * Sets the flag indicating model object class path resolution is enabled.
  158.      *
  159.      * @param value {@code true}, to enable model object class path resolution; {@code false}, to disable model object
  160.      * class path resolution.
  161.      *
  162.      * @see #isModelObjectClasspathResolutionEnabled()
  163.      */
  164.     public final void setModelObjectClasspathResolutionEnabled( final boolean value )
  165.     {
  166.         this.modelObjectClasspathResolutionEnabled = value;
  167.     }

  168.     /**
  169.      * Gets a set of module resources.
  170.      * <p>
  171.      * This accessor method returns a reference to the live set, not a snapshot. Therefore any modification you make
  172.      * to the returned set will be present inside the object. This is why there is no {@code set} method for the
  173.      * module resources property.
  174.      * </p>
  175.      *
  176.      * @return A set of module resources.
  177.      *
  178.      * @see #createModuleResource()
  179.      */
  180.     public Set<ModuleResourceType> getModuleResources()
  181.     {
  182.         if ( this.moduleResources == null )
  183.         {
  184.             this.moduleResources = new HashSet<ModuleResourceType>();
  185.         }

  186.         return this.moduleResources;
  187.     }

  188.     /**
  189.      * Creates a new {@code moduleResource} element instance.
  190.      *
  191.      * @return A new {@code moduleResource} element instance.
  192.      *
  193.      * @see #getModuleResources()
  194.      */
  195.     public ModuleResourceType createModuleResource()
  196.     {
  197.         final ModuleResourceType moduleResource = new ModuleResourceType();
  198.         this.getModuleResources().add( moduleResource );
  199.         return moduleResource;
  200.     }

  201.     /**
  202.      * Gets a flag indicating JAXP schema validation of model resources is enabled.
  203.      *
  204.      * @return {@code true}, if JAXP schema validation of model resources is enabled; {@code false}, else.
  205.      *
  206.      * @see #setModelResourceValidationEnabled(boolean)
  207.      */
  208.     public final boolean isModelResourceValidationEnabled()
  209.     {
  210.         return this.modelResourceValidationEnabled;
  211.     }

  212.     /**
  213.      * Sets the flag indicating JAXP schema validation of model resources is enabled.
  214.      *
  215.      * @param value {@code true}, to enable JAXP schema validation of model resources; {@code false}, to disable JAXP
  216.      * schema validation of model resources.
  217.      *
  218.      * @see #isModelResourceValidationEnabled()
  219.      */
  220.     public final void setModelResourceValidationEnabled( final boolean value )
  221.     {
  222.         this.modelResourceValidationEnabled = value;
  223.     }

  224.     /**
  225.      * Gets a flag indicating Java validation is enabled.
  226.      *
  227.      * @return {@code true}, if Java validation is enabled; {@code false}, else.
  228.      *
  229.      * @see #setJavaValidationEnabled(boolean)
  230.      *
  231.      * @since 1.4
  232.      */
  233.     public final boolean isJavaValidationEnabled()
  234.     {
  235.         return this.javaValidationEnabled;
  236.     }

  237.     /**
  238.      * Sets the flag indicating Java validation is enabled.
  239.      *
  240.      * @param value {@code true}, to enable Java validation; {@code false}, to disable Java validation.
  241.      *
  242.      * @see #isJavaValidationEnabled()
  243.      *
  244.      * @since 1.4
  245.      */
  246.     public final void setJavaValidationEnabled( final boolean value )
  247.     {
  248.         this.javaValidationEnabled = value;
  249.     }

  250.     /**
  251.      * Gets a {@code Model} from a given {@code ModelContext}.
  252.      *
  253.      * @param context The context to get a {@code Model} from.
  254.      *
  255.      * @return The {@code Model} from {@code context}.
  256.      *
  257.      * @throws NullPointerException if {@code contexŧ} is {@code null}.
  258.      * @throws BuildException if no model is found.
  259.      * @throws ModelException if getting the model fails.
  260.      *
  261.      * @see #getModel()
  262.      * @see #isModelObjectClasspathResolutionEnabled()
  263.      * @see #isModelProcessingEnabled()
  264.      */
  265.     @Override
  266.     public Model getModel( final ModelContext context ) throws BuildException, ModelException
  267.     {
  268.         if ( context == null )
  269.         {
  270.             throw new NullPointerException( "context" );
  271.         }

  272.         Model model = new Model();
  273.         model.setIdentifier( this.getModel() );
  274.         Modules modules = new Modules();
  275.         ModelHelper.setModules( model, modules );
  276.         Unmarshaller unmarshaller = null;

  277.         for ( final ResourceType resource : this.getModuleResources() )
  278.         {
  279.             final URL[] urls = this.getResources( context, resource.getLocation() );

  280.             if ( urls.length == 0 )
  281.             {
  282.                 if ( resource.isOptional() )
  283.                 {
  284.                     this.logMessage( Level.WARNING, Messages.getMessage( "moduleResourceNotFound",
  285.                                                                          resource.getLocation() ) );

  286.                 }
  287.                 else
  288.                 {
  289.                     throw new BuildException( Messages.getMessage( "moduleResourceNotFound", resource.getLocation() ),
  290.                                               this.getLocation() );

  291.                 }
  292.             }

  293.             for ( int i = urls.length - 1; i >= 0; i-- )
  294.             {
  295.                 InputStream in = null;
  296.                 boolean suppressExceptionOnClose = true;

  297.                 try
  298.                 {
  299.                     this.logMessage( Level.FINEST, Messages.getMessage( "reading", urls[i].toExternalForm() ) );

  300.                     final URLConnection con = urls[i].openConnection();
  301.                     con.setConnectTimeout( resource.getConnectTimeout() );
  302.                     con.setReadTimeout( resource.getReadTimeout() );
  303.                     con.connect();
  304.                     in = con.getInputStream();

  305.                     final Source source = new StreamSource( in, urls[i].toURI().toASCIIString() );

  306.                     if ( unmarshaller == null )
  307.                     {
  308.                         unmarshaller = context.createUnmarshaller( this.getModel() );
  309.                         if ( this.isModelResourceValidationEnabled() )
  310.                         {
  311.                             unmarshaller.setSchema( context.createSchema( this.getModel() ) );
  312.                         }
  313.                     }

  314.                     Object o = unmarshaller.unmarshal( source );
  315.                     if ( o instanceof JAXBElement<?> )
  316.                     {
  317.                         o = ( (JAXBElement<?>) o ).getValue();
  318.                     }

  319.                     if ( o instanceof Module )
  320.                     {
  321.                         modules.getModule().add( (Module) o );
  322.                     }
  323.                     else
  324.                     {
  325.                         this.log( Messages.getMessage( "unsupportedModuleResource", urls[i].toExternalForm() ),
  326.                                   Project.MSG_WARN );

  327.                     }

  328.                     suppressExceptionOnClose = false;
  329.                 }
  330.                 catch ( final SocketTimeoutException e )
  331.                 {
  332.                     String message = Messages.getMessage( e );
  333.                     message = Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" );

  334.                     if ( resource.isOptional() )
  335.                     {
  336.                         this.getProject().log( message, e, Project.MSG_WARN );
  337.                     }
  338.                     else
  339.                     {
  340.                         throw new BuildException( message, e, this.getLocation() );
  341.                     }
  342.                 }
  343.                 catch ( final IOException e )
  344.                 {
  345.                     String message = Messages.getMessage( e );
  346.                     message = Messages.getMessage( "resourceFailure", message != null ? " " + message : "" );

  347.                     if ( resource.isOptional() )
  348.                     {
  349.                         this.getProject().log( message, e, Project.MSG_WARN );
  350.                     }
  351.                     else
  352.                     {
  353.                         throw new BuildException( message, e, this.getLocation() );
  354.                     }
  355.                 }
  356.                 catch ( final URISyntaxException e )
  357.                 {
  358.                     throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
  359.                 }
  360.                 catch ( final JAXBException e )
  361.                 {
  362.                     String message = Messages.getMessage( e );
  363.                     if ( message == null )
  364.                     {
  365.                         message = Messages.getMessage( e.getLinkedException() );
  366.                     }

  367.                     throw new BuildException( message, e, this.getLocation() );
  368.                 }
  369.                 finally
  370.                 {
  371.                     try
  372.                     {
  373.                         if ( in != null )
  374.                         {
  375.                             in.close();
  376.                         }
  377.                     }
  378.                     catch ( final IOException e )
  379.                     {
  380.                         if ( suppressExceptionOnClose )
  381.                         {
  382.                             this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
  383.                         }
  384.                         else
  385.                         {
  386.                             throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
  387.                         }
  388.                     }
  389.                 }
  390.             }
  391.         }

  392.         model = context.findModel( model );
  393.         modules = ModelHelper.getModules( model );

  394.         if ( modules != null && this.isModelObjectClasspathResolutionEnabled() )
  395.         {
  396.             final Module classpathModule =
  397.                 modules.getClasspathModule( Modules.getDefaultClasspathModuleName(), context.getClassLoader() );

  398.             if ( classpathModule != null && modules.getModule( Modules.getDefaultClasspathModuleName() ) == null )
  399.             {
  400.                 modules.getModule().add( classpathModule );
  401.             }
  402.         }

  403.         if ( this.isModelProcessingEnabled() )
  404.         {
  405.             model = context.processModel( model );
  406.         }

  407.         return model;
  408.     }

  409.     /**
  410.      * {@inheritDoc}
  411.      */
  412.     @Override
  413.     public void preExecuteTask() throws BuildException
  414.     {
  415.         super.preExecuteTask();
  416.         this.assertLocationsNotNull( this.getModuleResources() );
  417.     }

  418.     /**
  419.      * {@inheritDoc}
  420.      */
  421.     @Override
  422.     public ModelContext newModelContext( final ClassLoader classLoader ) throws ModelException
  423.     {
  424.         final ModelContext modelContext = super.newModelContext( classLoader );

  425.         if ( this.getTransformerLocation() != null )
  426.         {
  427.             modelContext.setAttribute( DefaultModelProcessor.TRANSFORMER_LOCATION_ATTRIBUTE_NAME,
  428.                                        this.getTransformerLocation() );

  429.         }

  430.         if ( this.getModuleLocation() != null )
  431.         {
  432.             modelContext.setAttribute( DefaultModelProvider.MODULE_LOCATION_ATTRIBUTE_NAME, this.getModuleLocation() );
  433.         }

  434.         modelContext.setAttribute( ToolsModelProvider.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
  435.                                    this.isModelObjectClasspathResolutionEnabled() );

  436.         modelContext.setAttribute( ToolsModelProcessor.MODEL_OBJECT_CLASSPATH_RESOLUTION_ENABLED_ATTRIBUTE_NAME,
  437.                                    this.isModelObjectClasspathResolutionEnabled() );

  438.         modelContext.setAttribute( DefaultModelProvider.VALIDATING_ATTRIBUTE_NAME,
  439.                                    this.isModelResourceValidationEnabled() );

  440.         modelContext.setAttribute( DefaultModelValidator.VALIDATE_JAVA_ATTRIBUTE_NAME, this.isJavaValidationEnabled() );

  441.         for ( int i = 0, s0 = this.getModelContextAttributes().size(); i < s0; i++ )
  442.         {
  443.             final KeyValueType kv = this.getModelContextAttributes().get( i );
  444.             final Object object = kv.getObject( this.getLocation() );

  445.             if ( object != null )
  446.             {
  447.                 modelContext.setAttribute( kv.getKey(), object );
  448.             }
  449.             else
  450.             {
  451.                 modelContext.clearAttribute( kv.getKey() );
  452.             }
  453.         }

  454.         return modelContext;
  455.     }

  456.     /**
  457.      * {@inheritDoc}
  458.      */
  459.     @Override
  460.     public JomcModelTask clone()
  461.     {
  462.         final JomcModelTask clone = (JomcModelTask) super.clone();

  463.         if ( this.moduleResources != null )
  464.         {
  465.             clone.moduleResources = new HashSet<ModuleResourceType>( this.moduleResources.size() );
  466.             for ( final ModuleResourceType e : this.moduleResources )
  467.             {
  468.                 clone.moduleResources.add( e.clone() );
  469.             }
  470.         }

  471.         return clone;
  472.     }

  473. }