/*
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
*/
package flex.ant;
import flex.ant.config.ConfigVariable;
import flex.ant.config.NestedAttributeElement;
import flex.ant.config.OptionSpec;
import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.types.Commandline;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.taskdefs.Java;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.List;
/**
* This class contains common data and logic used by all the Flex Ant tasks.
*/
public abstract class FlexTask extends Java
{
/**
* The build tool's name.
*/
protected final String toolName;
/**
* The build tool's class name.
*/
private final String toolClassName;
/**
* The build tool's jar file name.
*/
private final String jarFileName;
/**
* The commandline used in execute()
*/
protected final Commandline cmdl;
/**
* An array of ConfigVariabes that are set by setDynamicAttribute().
*/
protected final ConfigVariable[] variables;
protected List nestedAttribs;
/**
* fork attribute
*/
protected boolean fork;
/**
* max memory attribute
*/
private String maxmemory;
private ClassLoader originalContextClassLoader;
protected static OptionSpec rslpSpec = new OptionSpec(null, "runtime-shared-library-path", "rslp");
/**
* @param toolName The build tool's name.
* @param toolClassName The build tool's class name.
* @param jarFileName The build tool's jar file.
* @param vars An array of ConfigVariables that will be set by attributes of the task
*/
protected FlexTask(String toolName, String toolClassName, String jarFileName, ConfigVariable[] vars)
{
this.toolName = toolName;
this.toolClassName = toolClassName;
this.jarFileName = jarFileName;
this.variables = vars;
cmdl = new Commandline();
}
public Object createDynamicElement(String elementName)
{
ConfigVariable var = null;
for (int i = 0; i < variables.length && var == null; i++)
{
if (variables[i].matches(elementName))
{
var = variables[i];
}
}
if (var != null)
{
return createElem(elementName, var.getSpec());
}
else
{
throw new BuildException("The <" + toolName + "> type doesn't support the \"" +
elementName + "\" nested element.", getLocation());
}
}
protected NestedAttributeElement createElem(String attrib, OptionSpec spec)
{
NestedAttributeElement e = new NestedAttributeElement(attrib, spec, this);
nestedAttribs.add(e);
return e;
}
protected NestedAttributeElement createElem(String[] attribs, OptionSpec spec)
{
NestedAttributeElement e = new NestedAttributeElement(attribs, spec, this);
nestedAttribs.add(e);
return e;
}
protected NestedAttributeElement createElemAllowAppend(String[] attribs, OptionSpec spec)
{
NestedAttributeElement e = new NestedAttributeElement(attribs, spec, this, true);
nestedAttribs.add(e);
return e;
}
/*=======================================================================*
* Static Attributes *
*=======================================================================*/
/**
* Sets whether to run the task in a separate VM.
*
* @param f if true then run in a separate VM.
*/
public void setFork(boolean f)
{
super.setFork(f);
this.fork = f;
}
/**
* Sets the max memory of each VM
*
* @param max max memory parameter
*/
public void setMaxmemory(String max)
{
super.setMaxmemory(max);
this.maxmemory = max;
}
/*=======================================================================*
* Dynamic Attributes *
*=======================================================================*/
/**
* Set the named attribute to the given value.
*
* @param attributeName The name of the attribute to set
* @param value The value to set the named attribute to
*/
public void setDynamicAttribute(String attributeName, String value)
{
ConfigVariable var = null;
for (int i = 0; i < variables.length && var == null; i++)
{
if (variables[i].matches(attributeName))
{
var = variables[i];
}
}
if (var != null)
{
var.set(value);
}
else
{
throw new BuildException("The <" + toolName + "> type doesn't support the \"" +
attributeName + "\" attribute.", getLocation());
}
}
/*=======================================================================*
* Execute and Related Functions *
*=======================================================================*/
/**
* Called by execute after the set ConfigVariables in <code>vars</code> has
* been added to the commandline. This function is responsible for adding
* all tool-specific options to the commandline as well as setting the
* default options of a build tool.
*/
protected abstract void prepareCommandline() throws BuildException;
/**
* Execute the task
*
* @throws BuildException If running build tool failed
*/
public final void execute() throws BuildException
{
String flexHomeProperty = getProject().getProperty("FLEX_HOME");
if (flexHomeProperty == null)
{
throw new BuildException("FLEX_HOME must be set to use the Flex Ant Tasks");
}
System.setProperty("FLEX_HOME", flexHomeProperty);
cmdl.createArgument().setValue("+flexlib=" + flexHomeProperty + "/frameworks");
prepareCommandline();
if (fork)
executeOutOfProcess();
else
executeInProcess();
}
/**
* Executes the task in a separate VM
*/
private void executeOutOfProcess() throws BuildException
{
try
{
Class toolClass = resolveClass(toolClassName);
super.setClassname(toolClassName);
// convert arguments into a string for use by executeJava()
// also auto-quotes arguments with spaces
super.createArg().setLine(
Commandline.toString(cmdl.getArguments()));
int err = super.executeJava();
//check error code
if (err > 0)
{
throw new BuildException(toolName + " task failed.");
}
}
finally
{
if (originalContextClassLoader != null)
{
Thread.currentThread().setContextClassLoader(originalContextClassLoader);
}
}
}
/**
* Executes the task in the same VM
*/
private void executeInProcess() throws BuildException
{
try
{
Class toolClass = resolveClass(toolClassName);
Class threadLocalToolkitClass = resolveClass("flex2.compiler.util.ThreadLocalToolkit");
log("FlexTask.execute: " + cmdl, Project.MSG_DEBUG);
int errorCount = 0;
try
{
Method toolMethod = toolClass.getMethod(toolName, new Class[] {String[].class});
toolMethod.invoke(null, new Object[] {cmdl.getArguments()});
Method errorCountMethod = threadLocalToolkitClass.getMethod("errorCount", (Class[]) null);
errorCount = ((Integer) errorCountMethod.invoke(null, (Object[]) null)).intValue();
}
catch (Exception e)
{
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
e.printStackTrace(printWriter);
log(stringWriter.toString(), Project.MSG_DEBUG);
throw new BuildException("Unable to run " + toolName + ": " + e.getMessage(), e);
}
if (errorCount > 0)
{
throw new BuildException(toolName + " task failed");
}
}
finally
{
if (originalContextClassLoader != null)
{
Thread.currentThread().setContextClassLoader(originalContextClassLoader);
}
}
}
private Class resolveClass(String className)
{
Class result = null;
try
{
result = Class.forName(className, true, Thread.currentThread().getContextClassLoader());
}
catch (ClassNotFoundException ignoredClassNotFoundException)
{
String flexHomeProperty = getProject().getProperty("FLEX_HOME");
if (flexHomeProperty != null)
{
File flexHome = new File(flexHomeProperty);
if ( flexHome.exists() )
{
File jarFile = new File(flexHome + "/lib", jarFileName);
if (jarFile.exists())
{
try
{
URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {jarFile.toURL()});
result = Class.forName(className, true, urlClassLoader);
originalContextClassLoader = Thread.currentThread().getContextClassLoader();
Thread.currentThread().setContextClassLoader(urlClassLoader);
if (fork)
{
super.setClasspath(new Path(getProject(), jarFile.getAbsolutePath()));
}
}
catch (MalformedURLException malformedURLException)
{
// We shouldn't really get here, but just in case.
malformedURLException.printStackTrace();
}
catch (ClassNotFoundException classNotFoundException)
{
throw new BuildException("The class not found in jar file: " + jarFileName,
getLocation());
}
}
else
{
throw new BuildException("File does not exist: " + jarFileName, getLocation());
}
}
else
{
throw new BuildException("FLEX_HOME does not exist.", getLocation());
}
}
else
{
throw new BuildException("The class, " + className +
", must be in the classpath or the FLEX_HOME property must be set.",
getLocation());
}
}
return result;
}
} //End of FlexTask