001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant;
020:
021: import java.lang.reflect.Method;
022: import org.apache.tools.ant.dispatch.Dispatchable;
023: import org.apache.tools.ant.dispatch.DispatchUtils;
024:
025: /**
026: * Uses introspection to "adapt" an arbitrary Bean which doesn't
027: * itself extend Task, but still contains an execute method and optionally
028: * a setProject method.
029: *
030: */
031: public class TaskAdapter extends Task implements TypeAdapter {
032:
033: /** Object to act as a proxy for. */
034: private Object proxy;
035:
036: /**
037: * Checks whether or not a class is suitable to be adapted by TaskAdapter.
038: * If the class is of type Dispatchable, the check is not performed because
039: * the method that will be executed will be determined only at runtime of
040: * the actual task and not during parse time.
041: *
042: * This only checks conditions which are additionally required for
043: * tasks adapted by TaskAdapter. Thus, this method should be called by
044: * Project.checkTaskClass.
045: *
046: * Throws a BuildException and logs as Project.MSG_ERR for
047: * conditions that will cause the task execution to fail.
048: * Logs other suspicious conditions with Project.MSG_WARN.
049: *
050: * @param taskClass Class to test for suitability.
051: * Must not be <code>null</code>.
052: * @param project Project to log warnings/errors to.
053: * Must not be <code>null</code>.
054: *
055: * @see Project#checkTaskClass(Class)
056: */
057: public static void checkTaskClass(final Class taskClass,
058: final Project project) {
059: if (!Dispatchable.class.isAssignableFrom(taskClass)) {
060: // don't have to check for interface, since then
061: // taskClass would be abstract too.
062: try {
063: final Method executeM = taskClass.getMethod("execute",
064: (Class[]) null);
065: // don't have to check for public, since
066: // getMethod finds public method only.
067: // don't have to check for abstract, since then
068: // taskClass would be abstract too.
069: if (!Void.TYPE.equals(executeM.getReturnType())) {
070: final String message = "return type of execute() should be "
071: + "void but was \""
072: + executeM.getReturnType()
073: + "\" in "
074: + taskClass;
075: project.log(message, Project.MSG_WARN);
076: }
077: } catch (NoSuchMethodException e) {
078: final String message = "No public execute() in "
079: + taskClass;
080: project.log(message, Project.MSG_ERR);
081: throw new BuildException(message);
082: } catch (LinkageError e) {
083: String message = "Could not load " + taskClass + ": "
084: + e;
085: project.log(message, Project.MSG_ERR);
086: throw new BuildException(message, e);
087: }
088: }
089: }
090:
091: /**
092: * Check if the proxy class is a valid class to use
093: * with this adapter.
094: * The class must have a public no-arg "execute()" method.
095: * @param proxyClass the class to check.
096: */
097: public void checkProxyClass(Class proxyClass) {
098: checkTaskClass(proxyClass, getProject());
099: }
100:
101: /**
102: * Executes the proxied task.
103: *
104: * @exception BuildException if the project could not be set
105: * or the method could not be executed.
106: */
107: public void execute() throws BuildException {
108: try {
109: Method setLocationM = proxy.getClass().getMethod(
110: "setLocation", new Class[] { Location.class });
111: if (setLocationM != null) {
112: setLocationM.invoke(proxy,
113: new Object[] { getLocation() });
114: }
115: } catch (NoSuchMethodException e) {
116: // ignore this if the class being used as a task does not have
117: // a set location method.
118: } catch (Exception ex) {
119: log("Error setting location in " + proxy.getClass(),
120: Project.MSG_ERR);
121: throw new BuildException(ex);
122: }
123:
124: try {
125: Method setProjectM = proxy.getClass().getMethod(
126: "setProject", new Class[] { Project.class });
127: if (setProjectM != null) {
128: setProjectM
129: .invoke(proxy, new Object[] { getProject() });
130: }
131: } catch (NoSuchMethodException e) {
132: // ignore this if the class being used as a task does not have
133: // a set project method.
134: } catch (Exception ex) {
135: log("Error setting project in " + proxy.getClass(),
136: Project.MSG_ERR);
137: throw new BuildException(ex);
138: }
139:
140: try {
141: DispatchUtils.execute(proxy);
142: } catch (BuildException be) {
143: throw be;
144: } catch (Exception ex) {
145: log("Error in " + proxy.getClass(), Project.MSG_VERBOSE);
146: throw new BuildException(ex);
147: }
148: }
149:
150: /**
151: * Sets the target object to proxy for.
152: *
153: * @param o The target object. Must not be <code>null</code>.
154: */
155: public void setProxy(Object o) {
156: this .proxy = o;
157: }
158:
159: /**
160: * Returns the target object being proxied.
161: *
162: * @return the target proxy object.
163: */
164: public Object getProxy() {
165: return proxy;
166: }
167:
168: }
|