| /* |
| * |
| * 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 org.apache.flex.compiler.ant; |
| |
| 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; |
| |
| 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.Environment.Variable; |
| import org.apache.tools.ant.types.Path; |
| import org.apache.tools.ant.taskdefs.Java; |
| |
| import org.apache.flex.compiler.ant.config.ConfigVariable; |
| import org.apache.flex.compiler.ant.config.NestedAttributeElement; |
| import org.apache.flex.compiler.ant.config.IOptionSource; |
| import org.apache.flex.compiler.ant.config.OptionSpec; |
| |
| /** |
| * This class contains common data and logic used by all the Flex Ant tasks. |
| */ |
| public abstract class FlexTask extends Java |
| { |
| protected static OptionSpec RUNTIME_SHARED_LIBRARY_PATH = |
| new OptionSpec("runtime-shared-library-path", "rslp"); |
| |
| /** |
| * Constructor. |
| * |
| * @param taskName The name of the Ant task. |
| * @param configVariables An array of ConfigVariables that will be set by attributes of the task. |
| * @param toolJARFileName The build tool's jar file. |
| * @param toolClassName The build tool's class name. |
| * @param toolMethodName The build tool's method name. |
| * @param toolFailureMethodName The build tool's method to determine |
| * whether an exit code means failure. |
| */ |
| protected FlexTask(String taskName, ConfigVariable[] configVariables, |
| String toolJARFileName, String toolClassName, |
| String toolMethodName, String toolFailureMethodName) |
| { |
| this.taskName = taskName; |
| this.configVariables = configVariables; |
| this.toolJARFileName = toolJARFileName; |
| this.toolClassName = toolClassName; |
| this.toolMethodName = toolMethodName; |
| this.toolFailureMethodName = toolFailureMethodName; |
| |
| cmdline = new Commandline(); |
| } |
| |
| /** |
| * The name of the Ant task. |
| */ |
| private final String taskName; |
| |
| /** |
| * An array of ConfigVariabes that are set by setDynamicAttribute(). |
| */ |
| protected final ConfigVariable[] configVariables; |
| |
| /** |
| * The build tool's jar file name. |
| */ |
| private final String toolJARFileName; |
| |
| /** |
| * The build tool's class name. |
| */ |
| private final String toolClassName; |
| |
| /** |
| * The build tool's entry point. |
| * It must be a static method that takes a String[] |
| * and returns an int exit code. |
| */ |
| private final String toolMethodName; |
| |
| /** |
| * The build tools' method that determines whether the |
| * exit code should make the Ant task fail. |
| * It must be a static method that takes an int |
| * and returns a boolean. |
| */ |
| private final String toolFailureMethodName; |
| |
| /** |
| * The commandline used in execute() |
| */ |
| protected final Commandline cmdline; |
| |
| protected List<IOptionSource> nestedAttribs; |
| |
| /** |
| * fork attribute |
| */ |
| protected boolean fork; |
| |
| private ClassLoader originalContextClassLoader; |
| |
| 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; |
| } |
| |
| /*=======================================================================* |
| * 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 < configVariables.length && var == null; i++) |
| { |
| if (configVariables[i].matches(attributeName)) |
| var = configVariables[i]; |
| } |
| |
| if (var != null) |
| { |
| var.set(value); |
| } |
| else |
| { |
| throw new BuildException("The <" + taskName + "> type doesn't support the \"" + |
| attributeName + "\" attribute.", getLocation()); |
| } |
| } |
| |
| /*=======================================================================* |
| * Dynamic Elements * |
| *=======================================================================*/ |
| |
| public Object createDynamicElement(String elementName) |
| { |
| ConfigVariable var = null; |
| |
| for (int i = 0; i < configVariables.length && var == null; i++) |
| { |
| if (configVariables[i].matches(elementName)) |
| var = configVariables[i]; |
| } |
| |
| if (var != null) |
| { |
| return createElem(elementName, var.getSpec()); |
| } |
| else |
| { |
| throw new BuildException("The <" + taskName + "> type doesn't support the \"" + |
| elementName + "\" nested element.", 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"); |
| |
| String falconHomeProperty = getProject().getProperty("FALCON_HOME"); |
| if (falconHomeProperty == null) |
| throw new BuildException("FALCON_HOME must be set to use the Flex Ant Tasks"); |
| |
| System.setProperty("FLEX_HOME", flexHomeProperty); |
| String flexlibProperty = flexHomeProperty.concat("/frameworks/"); |
| System.setProperty("flexlib", flexlibProperty); |
| |
| final Variable variable = new Variable(); |
| variable.setKey("flexlib"); |
| variable.setValue(flexHomeProperty); |
| addSysproperty(variable); |
| |
| // This allows the tool to find the default config file. |
| cmdline.createArgument().setValue("+flexlib=" + flexlibProperty); |
| |
| prepareCommandline(); |
| |
| if (fork) |
| executeOutOfProcess(); |
| else |
| executeInProcess(); |
| } |
| |
| /** |
| * Executes the task in a separate VM |
| */ |
| private void executeOutOfProcess() throws BuildException |
| { |
| try |
| { |
| // Without this, the tool class won't be found by executeJava(). |
| Class<?> toolClass = resolveClass(toolClassName); |
| |
| super.setClassname(toolClassName); |
| |
| // convert arguments into a string for use by executeJava() |
| // also auto-quotes arguments with spaces |
| String line = Commandline.toString(cmdline.getArguments()); |
| super.createArg().setLine(line); |
| |
| int exitCode = super.executeJava(); |
| |
| // Check exit code. |
| if (isFatalFailure(toolClass, exitCode)) |
| throw new BuildException(taskName + " 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); |
| |
| log("FlexTask.execute: " + cmdline, Project.MSG_DEBUG); |
| |
| int exitCode = -1; |
| |
| try |
| { |
| Method toolMethod = toolClass.getMethod(toolMethodName, new Class[] {String[].class}); |
| Object result = toolMethod.invoke(null, new Object[] {cmdline.getArguments()}); |
| exitCode = ((Integer)result).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 " + toolMethodName + ": " + e.getMessage(), e); |
| } |
| |
| if (isFatalFailure(toolClass, exitCode)) |
| throw new BuildException(taskName + " task failed");// } |
| } |
| finally |
| { |
| if (originalContextClassLoader != null) |
| Thread.currentThread().setContextClassLoader(originalContextClassLoader); |
| } |
| } |
| |
| private boolean isFatalFailure(Class<?> toolClass, int exitCode) |
| { |
| boolean fatal = true; |
| |
| try |
| { |
| Method toolFailureMethod = toolClass.getMethod(toolFailureMethodName, new Class[] {int.class}); |
| Object result = toolFailureMethod.invoke(null, new Object[] {exitCode}); |
| fatal = ((Boolean)result).booleanValue(); |
| } |
| 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 " + toolFailureMethodName + ": " + e.getMessage(), e); |
| } |
| |
| return fatal; |
| } |
| |
| private Class<?> resolveClass(String className) |
| { |
| Class<?> result = null; |
| |
| try |
| { |
| result = Class.forName(className, true, Thread.currentThread().getContextClassLoader()); |
| } |
| catch (ClassNotFoundException ignoredClassNotFoundException) |
| { |
| String flexHomeProperty = getProject().getProperty("FALCON_HOME"); |
| |
| if (flexHomeProperty != null) |
| { |
| File flexHome = new File(flexHomeProperty); |
| |
| if ( flexHome.exists() ) |
| { |
| File jarFile = new File(flexHome + "/lib", toolJARFileName); |
| |
| if (jarFile.exists()) |
| { |
| try |
| { |
| URL url = jarFile.toURI().toURL(); |
| URLClassLoader urlClassLoader = new URLClassLoader(new URL[] {url}); |
| 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 " + className + " was not found in jar file " + toolJARFileName, |
| getLocation()); |
| } |
| } |
| else |
| { |
| throw new BuildException("File does not exist: " + toolJARFileName, getLocation()); |
| } |
| } |
| else |
| { |
| throw new BuildException("FALCON_HOME does not exist.", getLocation()); |
| } |
| } |
| else |
| { |
| throw new BuildException("The class, " + className + |
| ", must be in the classpath or the FALCON_HOME property must be set.", |
| getLocation()); |
| } |
| } |
| |
| return result; |
| } |
| } |