blob: 77d8688aba1992ba3663f9823ad01a23570900c0 [file] [log] [blame]
/*
* 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
*
* https://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.tools.ant;
import java.lang.reflect.Method;
import org.apache.tools.ant.dispatch.DispatchUtils;
import org.apache.tools.ant.dispatch.Dispatchable;
/**
* Uses introspection to "adapt" an arbitrary Bean which doesn't
* itself extend Task, but still contains an execute method and optionally
* a setProject method.
*
*/
public class TaskAdapter extends Task implements TypeAdapter {
/** Object to act as a proxy for. */
private Object proxy;
/**
* No-arg constructor for reflection.
*/
public TaskAdapter() {
}
/**
* Constructor for given proxy.
* So you could write easier code
* <pre>
* myTaskContainer.addTask(new TaskAdapter(myProxy));
* </pre>
*
* @param proxy The object which Ant should use as task.
*/
public TaskAdapter(Object proxy) {
this();
setProxy(proxy);
}
/**
* Checks whether or not a class is suitable to be adapted by TaskAdapter.
* If the class is of type Dispatchable, the check is not performed because
* the method that will be executed will be determined only at runtime of
* the actual task and not during parse time.
*
* This only checks conditions which are additionally required for
* tasks adapted by TaskAdapter. Thus, this method should be called by
* Project.checkTaskClass.
*
* Throws a BuildException and logs as Project.MSG_ERR for
* conditions that will cause the task execution to fail.
* Logs other suspicious conditions with Project.MSG_WARN.
*
* @param taskClass Class to test for suitability.
* Must not be <code>null</code>.
* @param project Project to log warnings/errors to.
* Must not be <code>null</code>.
*
* @see Project#checkTaskClass(Class)
*/
public static void checkTaskClass(final Class<?> taskClass,
final Project project) {
if (!Dispatchable.class.isAssignableFrom(taskClass)) {
// don't have to check for interface, since then
// taskClass would be abstract too.
try {
final Method executeM = taskClass.getMethod("execute");
// don't have to check for public, since
// getMethod finds public method only.
// don't have to check for abstract, since then
// taskClass would be abstract too.
if (!Void.TYPE.equals(executeM.getReturnType())) {
final String message = "return type of execute() should be "
+ "void but was \"" + executeM.getReturnType() + "\" in "
+ taskClass;
project.log(message, Project.MSG_WARN);
}
} catch (NoSuchMethodException e) {
final String message = "No public execute() in " + taskClass;
project.log(message, Project.MSG_ERR);
throw new BuildException(message);
} catch (LinkageError e) {
String message = "Could not load " + taskClass + ": " + e;
project.log(message, Project.MSG_ERR);
throw new BuildException(message, e);
}
}
}
/**
* Check if the proxy class is a valid class to use
* with this adapter.
* The class must have a public no-arg "execute()" method.
* @param proxyClass the class to check.
*/
public void checkProxyClass(Class<?> proxyClass) {
checkTaskClass(proxyClass, getProject());
}
/**
* Executes the proxied task.
*
* @exception BuildException if the project could not be set
* or the method could not be executed.
*/
public void execute() throws BuildException {
try {
Method setLocationM =
proxy.getClass().getMethod("setLocation", Location.class);
if (setLocationM != null) {
setLocationM.invoke(proxy, getLocation());
}
} catch (NoSuchMethodException e) {
// ignore this if the class being used as a task does not have
// a set location method.
} catch (Exception ex) {
log("Error setting location in " + proxy.getClass(),
Project.MSG_ERR);
throw new BuildException(ex);
}
try {
Method setProjectM =
proxy.getClass().getMethod("setProject", Project.class);
if (setProjectM != null) {
setProjectM.invoke(proxy, getProject());
}
} catch (NoSuchMethodException e) {
// ignore this if the class being used as a task does not have
// a set project method.
} catch (Exception ex) {
log("Error setting project in " + proxy.getClass(),
Project.MSG_ERR);
throw new BuildException(ex);
}
try {
DispatchUtils.execute(proxy);
} catch (BuildException be) {
throw be;
} catch (Exception ex) {
log("Error in " + proxy.getClass(), Project.MSG_VERBOSE);
throw new BuildException(ex);
}
}
/**
* Sets the target object to proxy for.
*
* @param o The target object. Must not be <code>null</code>.
*/
public void setProxy(Object o) {
this.proxy = o;
}
/**
* Returns the target object being proxied.
*
* @return the target proxy object.
*/
public Object getProxy() {
return proxy;
}
}