JomcTask.java
/*
* Copyright (C) Christian Schulte <cs@schulte.it>, 2005-206
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* o Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* o Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
* INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
* AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* $JOMC: JomcTask.java 5043 2015-05-27 07:03:39Z schulte $
*
*/
package org.jomc.ant;
import java.io.BufferedReader;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.StringReader;
import java.io.StringWriter;
import java.net.MalformedURLException;
import java.net.SocketTimeoutException;
import java.net.URI;
import java.net.URISyntaxException;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.logging.Level;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Marshaller;
import javax.xml.transform.ErrorListener;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.stream.StreamSource;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.PropertyHelper;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.jomc.ant.types.KeyValueType;
import org.jomc.ant.types.NameType;
import org.jomc.ant.types.PropertiesFormatType;
import org.jomc.ant.types.PropertiesResourceType;
import org.jomc.ant.types.ResourceType;
import org.jomc.ant.types.TransformerResourceType;
import org.jomc.model.ModelObject;
import org.jomc.modlet.DefaultModelContext;
import org.jomc.modlet.DefaultModletProvider;
import org.jomc.modlet.Model;
import org.jomc.modlet.ModelContext;
import org.jomc.modlet.ModelContextFactory;
import org.jomc.modlet.ModelException;
import org.jomc.modlet.ModelValidationReport;
import org.jomc.modlet.ModletProvider;
/**
* Base class for executing tasks.
*
* @author <a href="mailto:cs@schulte.it">Christian Schulte</a>
* @version $JOMC: JomcTask.java 5043 2015-05-27 07:03:39Z schulte $
* @see #execute()
*/
public class JomcTask extends Task
{
/**
* The class path to process.
*/
private Path classpath;
/**
* The identifier of the model to process.
*/
private String model;
/**
* {@code ModelContext} attributes to apply.
*/
private List<KeyValueType> modelContextAttributes;
/**
* The name of the {@code ModelContextFactory} implementation class backing the task.
*/
private String modelContextFactoryClassName;
/**
* Controls processing of models.
*/
private boolean modelProcessingEnabled = true;
/**
* The location to search for modlets.
*/
private String modletLocation;
/**
* The {@code http://jomc.org/modlet} namespace schema system id of the context backing the task.
*/
private String modletSchemaSystemId;
/**
* The location to search for providers.
*/
private String providerLocation;
/**
* The location to search for platform providers.
*/
private String platformProviderLocation;
/**
* The global transformation parameters to apply.
*/
private List<KeyValueType> transformationParameters;
/**
* The global transformation parameter resources to apply.
*/
private List<PropertiesResourceType> transformationParameterResources;
/**
* The global transformation output properties to apply.
*/
private List<KeyValueType> transformationOutputProperties;
/**
* The flag indicating JAXP schema validation of modlet resources is enabled.
*/
private boolean modletResourceValidationEnabled = true;
/**
* Property controlling the execution of the task.
*/
private Object _if;
/**
* Property controlling the execution of the task.
*/
private Object unless;
/**
* Creates a new {@code JomcTask} instance.
*/
public JomcTask()
{
super();
}
/**
* Gets an object controlling the execution of the task.
*
* @return An object controlling the execution of the task or {@code null}.
*
* @see #setIf(java.lang.Object)
*/
public final Object getIf()
{
return this._if;
}
/**
* Sets an object controlling the execution of the task.
*
* @param value The new object controlling the execution of the task or {@code null}.
*
* @see #getIf()
*/
public final void setIf( final Object value )
{
this._if = value;
}
/**
* Gets an object controlling the execution of the task.
*
* @return An object controlling the execution of the task or {@code null}.
*
* @see #setUnless(java.lang.Object)
*/
public final Object getUnless()
{
if ( this.unless == null )
{
this.unless = Boolean.TRUE;
}
return this.unless;
}
/**
* Sets an object controlling the execution of the task.
*
* @param value The new object controlling the execution of the task or {@code null}.
*
* @see #getUnless()
*/
public final void setUnless( final Object value )
{
this.unless = value;
}
/**
* Creates a new {@code classpath} element instance.
*
* @return A new {@code classpath} element instance.
*/
public final Path createClasspath()
{
return this.getClasspath().createPath();
}
/**
* Gets the class path to process.
*
* @return The class path to process.
*
* @see #setClasspath(org.apache.tools.ant.types.Path)
*/
public final Path getClasspath()
{
if ( this.classpath == null )
{
this.classpath = new Path( this.getProject() );
}
return this.classpath;
}
/**
* Adds to the class path to process.
*
* @param value The path to add to the list of class path elements.
*
* @see #getClasspath()
*/
public final void setClasspath( final Path value )
{
this.getClasspath().add( value );
}
/**
* Adds a reference to a class path defined elsewhere.
*
* @param value A reference to a class path.
*
* @see #getClasspath()
*/
public final void setClasspathRef( final Reference value )
{
this.getClasspath().setRefid( value );
}
/**
* Gets the identifier of the model to process.
*
* @return The identifier of the model to process.
*
* @see #setModel(java.lang.String)
*/
public final String getModel()
{
if ( this.model == null )
{
this.model = ModelObject.MODEL_PUBLIC_ID;
}
return this.model;
}
/**
* Sets the identifier of the model to process.
*
* @param value The new identifier of the model to process or {@code null}.
*
* @see #getModel()
*/
public final void setModel( final String value )
{
this.model = value;
}
/**
* Gets the {@code ModelContext} attributes to apply.
* <p>
* This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
* to the returned list will be present inside the object. This is why there is no {@code set} method for the
* model context attributes property.
* </p>
*
* @return The {@code ModelContext} attributes to apply.
*
* @see #createModelContextAttribute()
* @see #newModelContext(java.lang.ClassLoader)
*/
public final List<KeyValueType> getModelContextAttributes()
{
if ( this.modelContextAttributes == null )
{
this.modelContextAttributes = new LinkedList<KeyValueType>();
}
return this.modelContextAttributes;
}
/**
* Creates a new {@code modelContextAttribute} element instance.
*
* @return A new {@code modelContextAttribute} element instance.
*
* @see #getModelContextAttributes()
*/
public KeyValueType createModelContextAttribute()
{
final KeyValueType modelContextAttribute = new KeyValueType();
this.getModelContextAttributes().add( modelContextAttribute );
return modelContextAttribute;
}
/**
* Gets the name of the {@code ModelContextFactory} implementation class backing the task.
*
* @return The name of the {@code ModelContextFactory} implementation class backing the task or {@code null}.
*
* @see #setModelContextFactoryClassName(java.lang.String)
*/
public final String getModelContextFactoryClassName()
{
return this.modelContextFactoryClassName;
}
/**
* Sets the name of the {@code ModelContextFactory} implementation class backing the task.
*
* @param value The new name of the {@code ModelContextFactory} implementation class backing the task or
* {@code null}.
*
* @see #getModelContextFactoryClassName()
*/
public final void setModelContextFactoryClassName( final String value )
{
this.modelContextFactoryClassName = value;
}
/**
* Gets a flag indicating the processing of models is enabled.
*
* @return {@code true}, if processing of models is enabled; {@code false}, else.
*
* @see #setModelProcessingEnabled(boolean)
*/
public final boolean isModelProcessingEnabled()
{
return this.modelProcessingEnabled;
}
/**
* Sets the flag indicating the processing of models is enabled.
*
* @param value {@code true}, to enable processing of models; {@code false}, to disable processing of models.
*
* @see #isModelProcessingEnabled()
*/
public final void setModelProcessingEnabled( final boolean value )
{
this.modelProcessingEnabled = value;
}
/**
* Gets the location searched for modlets.
*
* @return The location searched for modlets or {@code null}.
*
* @see #setModletLocation(java.lang.String)
*/
public final String getModletLocation()
{
return this.modletLocation;
}
/**
* Sets the location to search for modlets.
*
* @param value The new location to search for modlets or {@code null}.
*
* @see #getModletLocation()
*/
public final void setModletLocation( final String value )
{
this.modletLocation = value;
}
/**
* Gets the {@code http://jomc.org/modlet} namespace schema system id of the context backing the task.
*
* @return The {@code http://jomc.org/modlet} namespace schema system id of the context backing the task or
* {@code null}.
*
* @see #setModletSchemaSystemId(java.lang.String)
*/
public final String getModletSchemaSystemId()
{
return this.modletSchemaSystemId;
}
/**
* Sets the {@code http://jomc.org/modlet} namespace schema system id of the context backing the task.
*
* @param value The new {@code http://jomc.org/modlet} namespace schema system id of the context backing the task or
* {@code null}.
*
* @see #getModletSchemaSystemId()
*/
public final void setModletSchemaSystemId( final String value )
{
this.modletSchemaSystemId = value;
}
/**
* Gets the location searched for providers.
*
* @return The location searched for providers or {@code null}.
*
* @see #setProviderLocation(java.lang.String)
*/
public final String getProviderLocation()
{
return this.providerLocation;
}
/**
* Sets the location to search for providers.
*
* @param value The new location to search for providers or {@code null}.
*
* @see #getProviderLocation()
*/
public final void setProviderLocation( final String value )
{
this.providerLocation = value;
}
/**
* Gets the location searched for platform provider resources.
*
* @return The location searched for platform provider resources or {@code null}.
*
* @see #setPlatformProviderLocation(java.lang.String)
*/
public final String getPlatformProviderLocation()
{
return this.platformProviderLocation;
}
/**
* Sets the location to search for platform provider resources.
*
* @param value The new location to search for platform provider resources or {@code null}.
*
* @see #getPlatformProviderLocation()
*/
public final void setPlatformProviderLocation( final String value )
{
this.platformProviderLocation = value;
}
/**
* Gets the global transformation parameters to apply.
* <p>
* This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
* to the returned list will be present inside the object. This is why there is no {@code set} method for the
* transformation parameters property.
* </p>
*
* @return The global transformation parameters to apply.
*
* @see #createTransformationParameter()
* @see #getTransformer(org.jomc.ant.types.TransformerResourceType)
*/
public final List<KeyValueType> getTransformationParameters()
{
if ( this.transformationParameters == null )
{
this.transformationParameters = new LinkedList<KeyValueType>();
}
return this.transformationParameters;
}
/**
* Creates a new {@code transformationParameter} element instance.
*
* @return A new {@code transformationParameter} element instance.
*
* @see #getTransformationParameters()
*/
public KeyValueType createTransformationParameter()
{
final KeyValueType transformationParameter = new KeyValueType();
this.getTransformationParameters().add( transformationParameter );
return transformationParameter;
}
/**
* Gets the global transformation parameter resources to apply.
* <p>
* This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
* to the returned list will be present inside the object. This is why there is no {@code set} method for the
* transformation parameter resources property.
* </p>
*
* @return The global transformation parameter resources to apply.
*
* @see #createTransformationParameterResource()
* @see #getTransformer(org.jomc.ant.types.TransformerResourceType)
*/
public final List<PropertiesResourceType> getTransformationParameterResources()
{
if ( this.transformationParameterResources == null )
{
this.transformationParameterResources = new LinkedList<PropertiesResourceType>();
}
return this.transformationParameterResources;
}
/**
* Creates a new {@code transformationParameterResource} element instance.
*
* @return A new {@code transformationParameterResource} element instance.
*
* @see #getTransformationParameterResources()
*/
public PropertiesResourceType createTransformationParameterResource()
{
final PropertiesResourceType transformationParameterResource = new PropertiesResourceType();
this.getTransformationParameterResources().add( transformationParameterResource );
return transformationParameterResource;
}
/**
* Gets the global transformation output properties to apply.
* <p>
* This accessor method returns a reference to the live list, not a snapshot. Therefore any modification you make
* to the returned list will be present inside the object. This is why there is no {@code set} method for the
* transformation output properties property.
* </p>
*
* @return The global transformation output properties to apply.
*
* @see #createTransformationOutputProperty()
*/
public final List<KeyValueType> getTransformationOutputProperties()
{
if ( this.transformationOutputProperties == null )
{
this.transformationOutputProperties = new LinkedList<KeyValueType>();
}
return this.transformationOutputProperties;
}
/**
* Creates a new {@code transformationOutputProperty} element instance.
*
* @return A new {@code transformationOutputProperty} element instance.
*
* @see #getTransformationOutputProperties()
*/
public KeyValueType createTransformationOutputProperty()
{
final KeyValueType transformationOutputProperty = new KeyValueType();
this.getTransformationOutputProperties().add( transformationOutputProperty );
return transformationOutputProperty;
}
/**
* Gets a flag indicating JAXP schema validation of modlet resources is enabled.
*
* @return {@code true}, if JAXP schema validation of modlet resources is enabled; {@code false}, else.
*
* @see #setModletResourceValidationEnabled(boolean)
*/
public final boolean isModletResourceValidationEnabled()
{
return this.modletResourceValidationEnabled;
}
/**
* Sets the flag indicating JAXP schema validation of modlet resources is enabled.
*
* @param value {@code true}, to enable JAXP schema validation of modlet resources; {@code false}, to disable JAXP
* schema validation of modlet resources.
*
* @see #isModletResourceValidationEnabled()
*/
public final void setModletResourceValidationEnabled( final boolean value )
{
this.modletResourceValidationEnabled = value;
}
/**
* Called by the project to let the task do its work.
*
* @throws BuildException if execution fails.
*
* @see #getIf()
* @see #getUnless()
* @see #preExecuteTask()
* @see #executeTask()
* @see #postExecuteTask()
*/
@Override
public final void execute() throws BuildException
{
final PropertyHelper propertyHelper = PropertyHelper.getPropertyHelper( this.getProject() );
if ( propertyHelper.testIfCondition( this.getIf() ) && !propertyHelper.testUnlessCondition( this.getUnless() ) )
{
try
{
this.preExecuteTask();
this.executeTask();
}
finally
{
this.postExecuteTask();
}
}
}
/**
* Called by the {@code execute} method prior to the {@code executeTask} method.
*
* @throws BuildException if execution fails.
*
* @see #execute()
*/
public void preExecuteTask() throws BuildException
{
this.logSeparator();
this.log( Messages.getMessage( "title" ) );
this.logSeparator();
this.assertNotNull( "model", this.getModel() );
this.assertKeysNotNull( this.getModelContextAttributes() );
this.assertKeysNotNull( this.getTransformationParameters() );
this.assertKeysNotNull( this.getTransformationOutputProperties() );
this.assertLocationsNotNull( this.getTransformationParameterResources() );
}
/**
* Called by the {@code execute} method prior to the {@code postExecuteTask} method.
*
* @throws BuildException if execution fails.
*
* @see #execute()
*/
public void executeTask() throws BuildException
{
this.getProject().log( Messages.getMessage( "unimplementedTask", this.getClass().getName(), "executeTask" ),
Project.MSG_WARN );
}
/**
* Called by the {@code execute} method after the {@code preExecuteTask}/{@code executeTask} methods even if those
* methods threw an exception.
*
* @throws BuildException if execution fails.
*
* @see #execute()
*/
public void postExecuteTask() throws BuildException
{
this.logSeparator();
}
/**
* Gets a {@code Model} from a given {@code ModelContext}.
*
* @param context The context to get a {@code Model} from.
*
* @return The {@code Model} from {@code context}.
*
* @throws NullPointerException if {@code contexŧ} is {@code null}.
* @throws ModelException if getting the model fails.
*
* @see #getModel()
* @see #isModelProcessingEnabled()
*/
public Model getModel( final ModelContext context ) throws ModelException
{
if ( context == null )
{
throw new NullPointerException( "context" );
}
Model foundModel = context.findModel( this.getModel() );
if ( foundModel != null && this.isModelProcessingEnabled() )
{
foundModel = context.processModel( foundModel );
}
return foundModel;
}
/**
* Creates an {@code URL} for a given resource location.
* <p>
* This method first searches the class path of the task for a single resource matching {@code location}. If
* such a resource is found, the URL of that resource is returned. If no such resource is found, an attempt is made
* to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given
* location is interpreted as a file name relative to the project's base directory. If that file is found, the URL
* of that file is returned. Otherwise {@code null} is returned.
* </p>
*
* @param location The resource location to create an {@code URL} from.
*
* @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
* {@code location} points to a non-existent resource.
*
* @throws NullPointerException if {@code location} is {@code null}.
* @throws BuildException if creating an URL fails.
*/
public URL getResource( final String location ) throws BuildException
{
if ( location == null )
{
throw new NullPointerException( "location" );
}
try
{
String absolute = location;
if ( !absolute.startsWith( "/" ) )
{
absolute = "/" + absolute;
}
URL resource = this.getClass().getResource( absolute );
if ( resource == null )
{
try
{
resource = new URL( location );
}
catch ( final MalformedURLException e )
{
this.log( e, Project.MSG_DEBUG );
resource = null;
}
}
if ( resource == null )
{
final File f = this.getProject().resolveFile( location );
if ( f.isFile() )
{
resource = f.toURI().toURL();
}
}
return resource;
}
catch ( final MalformedURLException e )
{
String m = Messages.getMessage( e );
m = m == null ? "" : " " + m;
throw new BuildException( Messages.getMessage( "malformedLocation", location, m ), e, this.getLocation() );
}
}
/**
* Creates an array of {@code URL}s for a given resource location.
* <p>
* This method first searches the given context for resources matching {@code location}. If such resources are
* found, an array of URLs of those resources is returned. If no such resources are found, an attempt is made
* to parse the given location to an URL. On successful parsing, that URL is returned. Failing that, the given
* location is interpreted as a file name relative to the project's base directory. If that file is found, the URL
* of that file is returned. Otherwise an empty array is returned.
* </p>
*
* @param context The context to search for resources.
* @param location The resource location to create an array of {@code URL}s from.
*
* @return An array of {@code URL}s for {@code location} or an empty array if parsing {@code location} to an URL
* fails and {@code location} points to non-existent resources.
*
* @throws NullPointerException if {@code context} or {@code location} is {@code null}.
* @throws BuildException if creating an URL array fails.
*/
public URL[] getResources( final ModelContext context, final String location ) throws BuildException
{
if ( context == null )
{
throw new NullPointerException( "context" );
}
if ( location == null )
{
throw new NullPointerException( "location" );
}
final Set<URI> uris = new HashSet<URI>();
try
{
for ( final Enumeration<URL> e = context.findResources( location ); e.hasMoreElements(); )
{
uris.add( e.nextElement().toURI() );
}
}
catch ( final URISyntaxException e )
{
this.log( e, Project.MSG_DEBUG );
}
catch ( final ModelException e )
{
this.log( e, Project.MSG_DEBUG );
}
if ( uris.isEmpty() )
{
try
{
uris.add( new URL( location ).toURI() );
}
catch ( final MalformedURLException e )
{
this.log( e, Project.MSG_DEBUG );
}
catch ( final URISyntaxException e )
{
this.log( e, Project.MSG_DEBUG );
}
}
if ( uris.isEmpty() )
{
final File f = this.getProject().resolveFile( location );
if ( f.isFile() )
{
uris.add( f.toURI() );
}
}
int i = 0;
final URL[] urls = new URL[ uris.size() ];
for ( final URI uri : uris )
{
try
{
urls[i++] = uri.toURL();
}
catch ( final MalformedURLException e )
{
String m = Messages.getMessage( e );
m = m == null ? "" : " " + m;
throw new BuildException( Messages.getMessage( "malformedLocation", uri.toASCIIString(), m ), e,
this.getLocation() );
}
}
return urls;
}
/**
* Creates an {@code URL} for a given directory location.
* <p>
* This method first attempts to parse the given location to an URL. On successful parsing, that URL is returned.
* Failing that, the given location is interpreted as a directory name relative to the project's base directory. If
* that directory is found, the URL of that directory is returned. Otherwise {@code null} is returned.
* </p>
*
* @param location The directory location to create an {@code URL} from.
*
* @return An {@code URL} for {@code location} or {@code null}, if parsing {@code location} to an URL fails and
* {@code location} points to a non-existent directory.
*
* @throws NullPointerException if {@code location} is {@code null}.
* @throws BuildException if creating an URL fails.
*/
public URL getDirectory( final String location ) throws BuildException
{
if ( location == null )
{
throw new NullPointerException( "location" );
}
try
{
URL resource = null;
try
{
resource = new URL( location );
}
catch ( final MalformedURLException e )
{
this.log( e, Project.MSG_DEBUG );
resource = null;
}
if ( resource == null )
{
final File f = this.getProject().resolveFile( location );
if ( f.isDirectory() )
{
resource = f.toURI().toURL();
}
}
return resource;
}
catch ( final MalformedURLException e )
{
String m = Messages.getMessage( e );
m = m == null ? "" : " " + m;
throw new BuildException( Messages.getMessage( "malformedLocation", location, m ), e, this.getLocation() );
}
}
/**
* Creates a new {@code Transformer} for a given {@code TransformerResourceType}.
*
* @param resource The resource to create a {@code Transformer} of.
*
* @return A new {@code Transformer} for {@code resource} or {@code null}, if {@code resource} is not found and
* flagged optional.
*
* @throws TransformerConfigurationException if creating a new {@code Transformer} fails.
*
* @see #getTransformationParameterResources()
* @see #getTransformationParameters()
* @see #getResource(java.lang.String)
*/
public Transformer getTransformer( final TransformerResourceType resource ) throws TransformerConfigurationException
{
if ( resource == null )
{
throw new NullPointerException( "resource" );
}
InputStream in = null;
boolean suppressExceptionOnClose = true;
final URL url = this.getResource( resource.getLocation() );
try
{
if ( url != null )
{
final ErrorListener errorListener = new ErrorListener()
{
public void warning( final TransformerException exception ) throws TransformerException
{
if ( getProject() != null )
{
getProject().log( Messages.getMessage( exception ), exception, Project.MSG_WARN );
}
}
public void error( final TransformerException exception ) throws TransformerException
{
throw exception;
}
public void fatalError( final TransformerException exception ) throws TransformerException
{
throw exception;
}
};
final URLConnection con = url.openConnection();
con.setConnectTimeout( resource.getConnectTimeout() );
con.setReadTimeout( resource.getReadTimeout() );
con.connect();
in = con.getInputStream();
final TransformerFactory f = TransformerFactory.newInstance();
f.setErrorListener( errorListener );
final Transformer transformer = f.newTransformer( new StreamSource( in, url.toURI().toASCIIString() ) );
transformer.setErrorListener( errorListener );
for ( final Map.Entry<Object, Object> e : System.getProperties().entrySet() )
{
transformer.setParameter( e.getKey().toString(), e.getValue() );
}
for ( final Iterator<Map.Entry<?, ?>> it = this.getProject().getProperties().entrySet().iterator();
it.hasNext(); )
{
final Map.Entry<?, ?> e = it.next();
transformer.setParameter( e.getKey().toString(), e.getValue() );
}
for ( int i = 0, s0 = this.getTransformationParameterResources().size(); i < s0; i++ )
{
for ( final Map.Entry<Object, Object> e
: this.getProperties( this.getTransformationParameterResources().get( i ) ).entrySet() )
{
transformer.setParameter( e.getKey().toString(), e.getValue() );
}
}
for ( int i = 0, s0 = this.getTransformationParameters().size(); i < s0; i++ )
{
final KeyValueType p = this.getTransformationParameters().get( i );
transformer.setParameter( p.getKey(), p.getObject( this.getLocation() ) );
}
for ( int i = 0, s0 = this.getTransformationOutputProperties().size(); i < s0; i++ )
{
final KeyValueType p = this.getTransformationOutputProperties().get( i );
transformer.setOutputProperty( p.getKey(), p.getValue() );
}
for ( int i = 0, s0 = resource.getTransformationParameterResources().size(); i < s0; i++ )
{
for ( final Map.Entry<Object, Object> e
: this.getProperties( resource.getTransformationParameterResources().get( i ) ).
entrySet() )
{
transformer.setParameter( e.getKey().toString(), e.getValue() );
}
}
for ( int i = 0, s0 = resource.getTransformationParameters().size(); i < s0; i++ )
{
final KeyValueType p = resource.getTransformationParameters().get( i );
transformer.setParameter( p.getKey(), p.getObject( this.getLocation() ) );
}
for ( int i = 0, s0 = resource.getTransformationOutputProperties().size(); i < s0; i++ )
{
final KeyValueType p = resource.getTransformationOutputProperties().get( i );
transformer.setOutputProperty( p.getKey(), p.getValue() );
}
suppressExceptionOnClose = false;
return transformer;
}
else if ( resource.isOptional() )
{
this.log( Messages.getMessage( "transformerNotFound", resource.getLocation() ), Project.MSG_WARN );
}
else
{
throw new BuildException( Messages.getMessage( "transformerNotFound", resource.getLocation() ),
this.getLocation() );
}
}
catch ( final URISyntaxException e )
{
throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
}
catch ( final SocketTimeoutException e )
{
final String message = Messages.getMessage( e );
if ( resource.isOptional() )
{
this.getProject().log( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ),
e, Project.MSG_WARN );
}
else
{
throw new BuildException( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ),
e, this.getLocation() );
}
}
catch ( final IOException e )
{
final String message = Messages.getMessage( e );
if ( resource.isOptional() )
{
this.getProject().log( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ),
e, Project.MSG_WARN );
}
else
{
throw new BuildException( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ),
e, this.getLocation() );
}
}
finally
{
try
{
if ( in != null )
{
in.close();
}
}
catch ( final IOException e )
{
if ( suppressExceptionOnClose )
{
this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
}
else
{
throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
}
}
}
return null;
}
/**
* Creates a new {@code Properties} instance from a {@code PropertiesResourceType}.
*
* @param propertiesResourceType The {@code PropertiesResourceType} specifying the properties to create.
*
* @return The properties for {@code propertiesResourceType}.
*
* @throws NullPointerException if {@code propertiesResourceType} is {@code null}.
* @throws BuildException if loading properties fails.
*/
public Properties getProperties( final PropertiesResourceType propertiesResourceType ) throws BuildException
{
if ( propertiesResourceType == null )
{
throw new NullPointerException( "propertiesResourceType" );
}
InputStream in = null;
boolean suppressExceptionOnClose = true;
final Properties properties = new Properties();
final URL url = this.getResource( propertiesResourceType.getLocation() );
try
{
if ( url != null )
{
final URLConnection con = url.openConnection();
con.setConnectTimeout( propertiesResourceType.getConnectTimeout() );
con.setReadTimeout( propertiesResourceType.getReadTimeout() );
con.connect();
in = con.getInputStream();
if ( propertiesResourceType.getFormat() == PropertiesFormatType.PLAIN )
{
properties.load( in );
}
else if ( propertiesResourceType.getFormat() == PropertiesFormatType.XML )
{
properties.loadFromXML( in );
}
}
else if ( propertiesResourceType.isOptional() )
{
this.log( Messages.getMessage( "propertiesNotFound", propertiesResourceType.getLocation() ),
Project.MSG_WARN );
}
else
{
throw new BuildException( Messages.getMessage(
"propertiesNotFound", propertiesResourceType.getLocation() ), this.getLocation() );
}
suppressExceptionOnClose = false;
}
catch ( final SocketTimeoutException e )
{
final String message = Messages.getMessage( e );
if ( propertiesResourceType.isOptional() )
{
this.getProject().log( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ),
e, Project.MSG_WARN );
}
else
{
throw new BuildException( Messages.getMessage( "resourceTimeout", message != null ? " " + message : "" ),
e, this.getLocation() );
}
}
catch ( final IOException e )
{
final String message = Messages.getMessage( e );
if ( propertiesResourceType.isOptional() )
{
this.getProject().log( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ),
e, Project.MSG_WARN );
}
else
{
throw new BuildException( Messages.getMessage( "resourceFailure", message != null ? " " + message : "" ),
e, this.getLocation() );
}
}
finally
{
try
{
if ( in != null )
{
in.close();
}
}
catch ( final IOException e )
{
if ( suppressExceptionOnClose )
{
this.logMessage( Level.SEVERE, Messages.getMessage( e ), e );
}
else
{
throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
}
}
}
return properties;
}
/**
* Creates a new {@code ProjectClassLoader} instance.
*
* @return A new {@code ProjectClassLoader} instance.
*
* @throws BuildException if creating a new class loader instance fails.
*/
public ProjectClassLoader newProjectClassLoader() throws BuildException
{
try
{
final ProjectClassLoader classLoader = new ProjectClassLoader( this.getProject(), this.getClasspath() );
classLoader.getModletExcludes().addAll( ProjectClassLoader.getDefaultModletExcludes() );
classLoader.getProviderExcludes().addAll( ProjectClassLoader.getDefaultProviderExcludes() );
classLoader.getSchemaExcludes().addAll( ProjectClassLoader.getDefaultSchemaExcludes() );
classLoader.getServiceExcludes().addAll( ProjectClassLoader.getDefaultServiceExcludes() );
if ( this.getModletLocation() != null )
{
classLoader.getModletResourceLocations().add( this.getModletLocation() );
}
else
{
classLoader.getModletResourceLocations().add( DefaultModletProvider.getDefaultModletLocation() );
}
if ( this.getProviderLocation() != null )
{
classLoader.getProviderResourceLocations().add(
this.getProviderLocation() + "/" + ModletProvider.class.getName() );
}
else
{
classLoader.getProviderResourceLocations().add(
DefaultModelContext.getDefaultProviderLocation() + "/" + ModletProvider.class.getName() );
}
return classLoader;
}
catch ( final IOException e )
{
throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
}
}
/**
* Creates a new {@code ModelContext} instance using a given class loader.
*
* @param classLoader The class loader to create a new {@code ModelContext} instance with.
*
* @return A new {@code ModelContext} instance backed by {@code classLoader}.
*
* @throws ModelException if creating a new {@code ModelContext} instance fails.
*/
public ModelContext newModelContext( final ClassLoader classLoader ) throws ModelException
{
final ModelContextFactory modelContextFactory;
if ( this.modelContextFactoryClassName != null )
{
modelContextFactory = ModelContextFactory.newInstance( this.getModelContextFactoryClassName() );
}
else
{
modelContextFactory = ModelContextFactory.newInstance();
}
final ModelContext modelContext = modelContextFactory.newModelContext( classLoader );
modelContext.setLogLevel( Level.ALL );
modelContext.setModletSchemaSystemId( this.getModletSchemaSystemId() );
modelContext.getListeners().add( new ModelContext.Listener()
{
@Override
public void onLog( final Level level, final String message, final Throwable t )
{
super.onLog( level, message, t );
logMessage( level, message, t );
}
} );
if ( this.getProviderLocation() != null )
{
modelContext.setAttribute( DefaultModelContext.PROVIDER_LOCATION_ATTRIBUTE_NAME,
this.getProviderLocation() );
}
if ( this.getPlatformProviderLocation() != null )
{
modelContext.setAttribute( DefaultModelContext.PLATFORM_PROVIDER_LOCATION_ATTRIBUTE_NAME,
this.getPlatformProviderLocation() );
}
if ( this.getModletLocation() != null )
{
modelContext.setAttribute( DefaultModletProvider.MODLET_LOCATION_ATTRIBUTE_NAME, this.getModletLocation() );
}
modelContext.setAttribute( DefaultModletProvider.VALIDATING_ATTRIBUTE_NAME,
this.isModletResourceValidationEnabled() );
for ( int i = 0, s0 = this.getModelContextAttributes().size(); i < s0; i++ )
{
final KeyValueType kv = this.getModelContextAttributes().get( i );
final Object object = kv.getObject( this.getLocation() );
if ( object != null )
{
modelContext.setAttribute( kv.getKey(), object );
}
else
{
modelContext.clearAttribute( kv.getKey() );
}
}
return modelContext;
}
/**
* Throws a {@code BuildException} on a given {@code null} value.
*
* @param attributeName The name of a mandatory attribute.
* @param value The value of that attribute.
*
* @throws NullPointerException if {@code attributeName} is {@code null}.
* @throws BuildException if {@code value} is {@code null}.
*/
public final void assertNotNull( final String attributeName, final Object value ) throws BuildException
{
if ( attributeName == null )
{
throw new NullPointerException( "attributeName" );
}
if ( value == null )
{
throw new BuildException( Messages.getMessage( "mandatoryAttribute", attributeName ), this.getLocation() );
}
}
/**
* Throws a {@code BuildException} on a {@code null} value of a {@code name} property of a given {@code NameType}
* collection.
*
* @param names The collection holding the {@code NameType} instances to test.
*
* @throws NullPointerException if {@code names} is {@code null}.
* @throws BuildException if a {@code name} property of a given {@code NameType} from the {@code names} collection
* holds a {@code null} value.
*/
public final void assertNamesNotNull( final Collection<? extends NameType> names ) throws BuildException
{
if ( names == null )
{
throw new NullPointerException( "names" );
}
for ( final NameType n : names )
{
this.assertNotNull( "name", n.getName() );
}
}
/**
* Throws a {@code BuildException} on a {@code null} value of a {@code key} property of a given {@code KeyValueType}
* collection.
*
* @param keys The collection holding the {@code KeyValueType} instances to test.
*
* @throws NullPointerException if {@code keys} is {@code null}.
* @throws BuildException if a {@code key} property of a given {@code KeyValueType} from the {@code keys} collection
* holds a {@code null} value.
*/
public final void assertKeysNotNull( final Collection<? extends KeyValueType> keys ) throws BuildException
{
if ( keys == null )
{
throw new NullPointerException( "keys" );
}
for ( final KeyValueType k : keys )
{
this.assertNotNull( "key", k.getKey() );
}
}
/**
* Throws a {@code BuildException} on a {@code null} value of a {@code location} property of a given
* {@code ResourceType} collection.
*
* @param locations The collection holding the {@code ResourceType} instances to test.
*
* @throws NullPointerException if {@code locations} is {@code null}.
* @throws BuildException if a {@code location} property of a given {@code ResourceType} from the {@code locations}
* collection holds a {@code null} value.
*/
public final void assertLocationsNotNull( final Collection<? extends ResourceType> locations )
throws BuildException
{
if ( locations == null )
{
throw new NullPointerException( "locations" );
}
for ( final ResourceType r : locations )
{
assertNotNull( "location", r.getLocation() );
if ( r instanceof TransformerResourceType )
{
assertKeysNotNull( ( (TransformerResourceType) r ).getTransformationParameters() );
assertLocationsNotNull( ( (TransformerResourceType) r ).getTransformationParameterResources() );
assertKeysNotNull( ( (TransformerResourceType) r ).getTransformationOutputProperties() );
}
}
}
/**
* Logs a separator string.
*/
public final void logSeparator()
{
this.log( Messages.getMessage( "separator" ) );
}
/**
* Logs a message at a given level.
*
* @param level The level to log at.
* @param message The message to log.
*
* @throws BuildException if logging fails.
*/
public final void logMessage( final Level level, final String message ) throws BuildException
{
BufferedReader reader = null;
boolean suppressExceptionOnClose = true;
try
{
String line = null;
reader = new BufferedReader( new StringReader( message ) );
while ( ( line = reader.readLine() ) != null )
{
if ( level.intValue() >= Level.SEVERE.intValue() )
{
log( line, Project.MSG_ERR );
}
else if ( level.intValue() >= Level.WARNING.intValue() )
{
log( line, Project.MSG_WARN );
}
else if ( level.intValue() >= Level.INFO.intValue() )
{
log( line, Project.MSG_INFO );
}
else
{
log( line, Project.MSG_DEBUG );
}
}
suppressExceptionOnClose = false;
}
catch ( final IOException e )
{
throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
}
finally
{
try
{
if ( reader != null )
{
reader.close();
}
}
catch ( final IOException e )
{
if ( suppressExceptionOnClose )
{
this.log( e, Project.MSG_ERR );
}
else
{
throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
}
}
}
}
/**
* Logs a message at a given level.
*
* @param level The level to log at.
* @param message The message to log.
* @param throwable The throwable to log.
*
* @throws BuildException if logging fails.
*/
public final void logMessage( final Level level, final String message, final Throwable throwable )
throws BuildException
{
this.logMessage( level, message );
if ( level.intValue() >= Level.SEVERE.intValue() )
{
log( throwable, Project.MSG_ERR );
}
else if ( level.intValue() >= Level.WARNING.intValue() )
{
log( throwable, Project.MSG_WARN );
}
else if ( level.intValue() >= Level.INFO.intValue() )
{
log( throwable, Project.MSG_INFO );
}
else
{
log( throwable, Project.MSG_DEBUG );
}
}
/**
* Logs a validation report.
*
* @param context The context to use for logging the report.
* @param report The report to log.
*
* @throws NullPointerException if {@code context} or {@code report} is {@code null}.
* @throws BuildException if logging fails.
*/
public final void logValidationReport( final ModelContext context, final ModelValidationReport report )
{
try
{
if ( !report.getDetails().isEmpty() )
{
this.logSeparator();
Marshaller marshaller = null;
for ( final ModelValidationReport.Detail detail : report.getDetails() )
{
this.logMessage( detail.getLevel(), "o " + detail.getMessage() );
if ( detail.getElement() != null )
{
if ( marshaller == null )
{
marshaller = context.createMarshaller( this.getModel() );
marshaller.setProperty( Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE );
}
final StringWriter stringWriter = new StringWriter();
marshaller.marshal( detail.getElement(), stringWriter );
this.logMessage( Level.FINEST, stringWriter.toString() );
}
}
}
}
catch ( final ModelException e )
{
throw new BuildException( Messages.getMessage( e ), e, this.getLocation() );
}
catch ( final JAXBException e )
{
String message = Messages.getMessage( e );
if ( message == null && e.getLinkedException() != null )
{
message = Messages.getMessage( e.getLinkedException() );
}
throw new BuildException( message, e, this.getLocation() );
}
}
/**
* Creates and returns a copy of this object.
*
* @return A copy of this object.
*/
@Override
public JomcTask clone()
{
try
{
final JomcTask clone = (JomcTask) super.clone();
clone.classpath = (Path) ( this.classpath != null ? this.classpath.clone() : null );
if ( this.modelContextAttributes != null )
{
clone.modelContextAttributes = new ArrayList<KeyValueType>( this.modelContextAttributes.size() );
for ( final KeyValueType e : this.modelContextAttributes )
{
clone.modelContextAttributes.add( e.clone() );
}
}
if ( this.transformationParameters != null )
{
clone.transformationParameters =
new ArrayList<KeyValueType>( this.transformationParameters.size() );
for ( final KeyValueType e : this.transformationParameters )
{
clone.transformationParameters.add( e.clone() );
}
}
if ( this.transformationParameterResources != null )
{
clone.transformationParameterResources =
new ArrayList<PropertiesResourceType>( this.transformationParameterResources.size() );
for ( final PropertiesResourceType e : this.transformationParameterResources )
{
clone.transformationParameterResources.add( e.clone() );
}
}
if ( this.transformationOutputProperties != null )
{
clone.transformationOutputProperties =
new ArrayList<KeyValueType>( this.transformationOutputProperties.size() );
for ( final KeyValueType e : this.transformationOutputProperties )
{
clone.transformationOutputProperties.add( e.clone() );
}
}
return clone;
}
catch ( final CloneNotSupportedException e )
{
throw new AssertionError( e );
}
}
}