1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 *
17 */
18
19 package org.apache.tools.ant;
20
21 import java.lang.reflect.Method;
22 import org.apache.tools.ant.dispatch.Dispatchable;
23 import org.apache.tools.ant.dispatch.DispatchUtils;
24
25 /**
26 * Uses introspection to "adapt" an arbitrary Bean which doesn't
27 * itself extend Task, but still contains an execute method and optionally
28 * a setProject method.
29 *
30 */
31 public class TaskAdapter extends Task implements TypeAdapter {
32
33 /** Object to act as a proxy for. */
34 private Object proxy;
35
36 /**
37 * No-arg constructor for reflection.
38 */
39 public TaskAdapter() {
40 }
41
42 /**
43 * Constructor for given proxy.
44 * So you could write easier code
45 * <pre>
46 * myTaskContainer.addTask( new TaskAdapter(myProxy) );
47 * </pre>
48 *
49 * @param proxy The object which Ant should use as task.
50 */
51 public TaskAdapter(Object proxy) {
52 this();
53 setProxy(proxy);
54 }
55
56 /**
57 * Checks whether or not a class is suitable to be adapted by TaskAdapter.
58 * If the class is of type Dispatchable, the check is not performed because
59 * the method that will be executed will be determined only at runtime of
60 * the actual task and not during parse time.
61 *
62 * This only checks conditions which are additionally required for
63 * tasks adapted by TaskAdapter. Thus, this method should be called by
64 * Project.checkTaskClass.
65 *
66 * Throws a BuildException and logs as Project.MSG_ERR for
67 * conditions that will cause the task execution to fail.
68 * Logs other suspicious conditions with Project.MSG_WARN.
69 *
70 * @param taskClass Class to test for suitability.
71 * Must not be <code>null</code>.
72 * @param project Project to log warnings/errors to.
73 * Must not be <code>null</code>.
74 *
75 * @see Project#checkTaskClass(Class)
76 */
77 public static void checkTaskClass(final Class taskClass,
78 final Project project) {
79 if (!Dispatchable.class.isAssignableFrom(taskClass)) {
80 // don't have to check for interface, since then
81 // taskClass would be abstract too.
82 try {
83 final Method executeM = taskClass.getMethod("execute", (Class[]) null);
84 // don't have to check for public, since
85 // getMethod finds public method only.
86 // don't have to check for abstract, since then
87 // taskClass would be abstract too.
88 if (!Void.TYPE.equals(executeM.getReturnType())) {
89 final String message = "return type of execute() should be "
90 + "void but was \"" + executeM.getReturnType() + "\" in "
91 + taskClass;
92 project.log(message, Project.MSG_WARN);
93 }
94 } catch (NoSuchMethodException e) {
95 final String message = "No public execute() in " + taskClass;
96 project.log(message, Project.MSG_ERR);
97 throw new BuildException(message);
98 } catch (LinkageError e) {
99 String message = "Could not load " + taskClass + ": " + e;
100 project.log(message, Project.MSG_ERR);
101 throw new BuildException(message, e);
102 }
103 }
104 }
105
106 /**
107 * Check if the proxy class is a valid class to use
108 * with this adapter.
109 * The class must have a public no-arg "execute()" method.
110 * @param proxyClass the class to check.
111 */
112 public void checkProxyClass(Class proxyClass) {
113 checkTaskClass(proxyClass, getProject());
114 }
115
116 /**
117 * Executes the proxied task.
118 *
119 * @exception BuildException if the project could not be set
120 * or the method could not be executed.
121 */
122 public void execute() throws BuildException {
123 try {
124 Method setLocationM = proxy.getClass().getMethod(
125 "setLocation", new Class[] {Location.class});
126 if (setLocationM != null) {
127 setLocationM.invoke(proxy, new Object[] {getLocation()});
128 }
129 } catch (NoSuchMethodException e) {
130 // ignore this if the class being used as a task does not have
131 // a set location method.
132 } catch (Exception ex) {
133 log("Error setting location in " + proxy.getClass(),
134 Project.MSG_ERR);
135 throw new BuildException(ex);
136 }
137
138 try {
139 Method setProjectM = proxy.getClass().getMethod(
140 "setProject", new Class[] {Project.class});
141 if (setProjectM != null) {
142 setProjectM.invoke(proxy, new Object[] {getProject()});
143 }
144 } catch (NoSuchMethodException e) {
145 // ignore this if the class being used as a task does not have
146 // a set project method.
147 } catch (Exception ex) {
148 log("Error setting project in " + proxy.getClass(),
149 Project.MSG_ERR);
150 throw new BuildException(ex);
151 }
152
153 try {
154 DispatchUtils.execute(proxy);
155 } catch (BuildException be) {
156 throw be;
157 } catch (Exception ex) {
158 log("Error in " + proxy.getClass(), Project.MSG_VERBOSE);
159 throw new BuildException(ex);
160 }
161 }
162
163 /**
164 * Sets the target object to proxy for.
165 *
166 * @param o The target object. Must not be <code>null</code>.
167 */
168 public void setProxy(Object o) {
169 this.proxy = o;
170 }
171
172 /**
173 * Returns the target object being proxied.
174 *
175 * @return the target proxy object.
176 */
177 public Object getProxy() {
178 return proxy;
179 }
180
181 }