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 org.apache.tools.ant.dispatch.DispatchUtils;
22
23 import java.util.Enumeration;
24 import java.io.IOException;
25
26 /**
27 * Base class for all tasks.
28 *
29 * Use Project.createTask to create a new task instance rather than
30 * using this class directly for construction.
31 *
32 * @see Project#createTask
33 */
34 public abstract class Task extends ProjectComponent {
35 // CheckStyle:VisibilityModifier OFF - bc
36 /**
37 * Target this task belongs to, if any.
38 * @deprecated since 1.6.x.
39 * You should not be accessing this variable directly.
40 * Please use the {@link #getOwningTarget()} method.
41 */
42 protected Target target;
43
44 /**
45 * Name of this task to be used for logging purposes.
46 * This defaults to the same as the type, but may be
47 * overridden by the user. For instance, the name "java"
48 * isn't terribly descriptive for a task used within
49 * another task - the outer task code can probably
50 * provide a better one.
51 * @deprecated since 1.6.x.
52 * You should not be accessing this variable directly.
53 * Please use the {@link #getTaskName()} method.
54 */
55 protected String taskName;
56
57 /**
58 * Type of this task.
59 *
60 * @deprecated since 1.6.x.
61 * You should not be accessing this variable directly.
62 * Please use the {@link #getTaskType()} method.
63 */
64 protected String taskType;
65
66 /**
67 * Wrapper for this object, used to configure it at runtime.
68 *
69 * @deprecated since 1.6.x.
70 * You should not be accessing this variable directly.
71 * Please use the {@link #getWrapper()} method.
72 */
73 protected RuntimeConfigurable wrapper;
74
75 // CheckStyle:VisibilityModifier ON
76
77 /**
78 * Whether or not this task is invalid. A task becomes invalid
79 * if a conflicting class is specified as the implementation for
80 * its type.
81 */
82 private boolean invalid;
83
84 /** Sole constructor. */
85 public Task() {
86 }
87
88 /**
89 * Sets the target container of this task.
90 *
91 * @param target Target in whose scope this task belongs.
92 * May be <code>null</code>, indicating a top-level task.
93 */
94 public void setOwningTarget(Target target) {
95 this.target = target;
96 }
97
98 /**
99 * Returns the container target of this task.
100 *
101 * @return The target containing this task, or <code>null</code> if
102 * this task is a top-level task.
103 */
104 public Target getOwningTarget() {
105 return target;
106 }
107
108 /**
109 * Sets the name to use in logging messages.
110 *
111 * @param name The name to use in logging messages.
112 * Should not be <code>null</code>.
113 */
114 public void setTaskName(String name) {
115 this.taskName = name;
116 }
117
118 /**
119 * Returns the name to use in logging messages.
120 *
121 * @return the name to use in logging messages.
122 */
123 public String getTaskName() {
124 return taskName;
125 }
126
127 /**
128 * Sets the name with which the task has been invoked.
129 *
130 * @param type The name the task has been invoked as.
131 * Should not be <code>null</code>.
132 */
133 public void setTaskType(String type) {
134 this.taskType = type;
135 }
136
137 /**
138 * Called by the project to let the task initialize properly.
139 * The default implementation is a no-op.
140 *
141 * @exception BuildException if something goes wrong with the build
142 */
143 public void init() throws BuildException {
144 }
145
146 /**
147 * Called by the project to let the task do its work. This method may be
148 * called more than once, if the task is invoked more than once.
149 * For example,
150 * if target1 and target2 both depend on target3, then running
151 * "ant target1 target2" will run all tasks in target3 twice.
152 *
153 * @exception BuildException if something goes wrong with the build.
154 */
155 public void execute() throws BuildException {
156 }
157
158 /**
159 * Returns the wrapper used for runtime configuration.
160 *
161 * @return the wrapper used for runtime configuration. This
162 * method will generate a new wrapper (and cache it)
163 * if one isn't set already.
164 */
165 public RuntimeConfigurable getRuntimeConfigurableWrapper() {
166 if (wrapper == null) {
167 wrapper = new RuntimeConfigurable(this, getTaskName());
168 }
169 return wrapper;
170 }
171
172 /**
173 * Sets the wrapper to be used for runtime configuration.
174 *
175 * This method should be used only by the ProjectHelper and Ant internals.
176 * It is public to allow helper plugins to operate on tasks, normal tasks
177 * should never use it.
178 *
179 * @param wrapper The wrapper to be used for runtime configuration.
180 * May be <code>null</code>, in which case the next call
181 * to getRuntimeConfigurableWrapper will generate a new
182 * wrapper.
183 */
184 public void setRuntimeConfigurableWrapper(RuntimeConfigurable wrapper) {
185 this.wrapper = wrapper;
186 }
187
188 // XXX: (Jon Skeet) The comment "if it hasn't been done already" may
189 // not be strictly true. wrapper.maybeConfigure() won't configure the same
190 // attributes/text more than once, but it may well add the children again,
191 // unless I've missed something.
192 /**
193 * Configures this task - if it hasn't been done already.
194 * If the task has been invalidated, it is replaced with an
195 * UnknownElement task which uses the new definition in the project.
196 *
197 * @exception BuildException if the task cannot be configured.
198 */
199 public void maybeConfigure() throws BuildException {
200 if (!invalid) {
201 if (wrapper != null) {
202 wrapper.maybeConfigure(getProject());
203 }
204 } else {
205 getReplacement();
206 }
207 }
208
209 /**
210 * Force the task to be reconfigured from its RuntimeConfigurable.
211 */
212 public void reconfigure() {
213 if (wrapper != null) {
214 wrapper.reconfigure(getProject());
215 }
216 }
217
218 /**
219 * Handles output by logging it with the INFO priority.
220 *
221 * @param output The output to log. Should not be <code>null</code>.
222 */
223 protected void handleOutput(String output) {
224 log(output, Project.MSG_INFO);
225 }
226
227 /**
228 * Handles output by logging it with the INFO priority.
229 *
230 * @param output The output to log. Should not be <code>null</code>.
231 *
232 * @since Ant 1.5.2
233 */
234 protected void handleFlush(String output) {
235 handleOutput(output);
236 }
237
238 /**
239 * Handle an input request by this task.
240 *
241 * @param buffer the buffer into which data is to be read.
242 * @param offset the offset into the buffer at which data is stored.
243 * @param length the amount of data to read.
244 *
245 * @return the number of bytes read.
246 *
247 * @exception IOException if the data cannot be read.
248 * @since Ant 1.6
249 */
250 protected int handleInput(byte[] buffer, int offset, int length)
251 throws IOException {
252 return getProject().defaultInput(buffer, offset, length);
253 }
254
255 /**
256 * Handles an error output by logging it with the WARN priority.
257 *
258 * @param output The error output to log. Should not be <code>null</code>.
259 */
260 protected void handleErrorOutput(String output) {
261 log(output, Project.MSG_WARN);
262 }
263
264 /**
265 * Handles an error line by logging it with the WARN priority.
266 *
267 * @param output The error output to log. Should not be <code>null</code>.
268 *
269 * @since Ant 1.5.2
270 */
271 protected void handleErrorFlush(String output) {
272 handleErrorOutput(output);
273 }
274
275 /**
276 * Logs a message with the default (INFO) priority.
277 *
278 * @param msg The message to be logged. Should not be <code>null</code>.
279 */
280 public void log(String msg) {
281 log(msg, Project.MSG_INFO);
282 }
283
284 /**
285 * Logs a message with the given priority. This delegates
286 * the actual logging to the project.
287 *
288 * @param msg The message to be logged. Should not be <code>null</code>.
289 * @param msgLevel The message priority at which this message is to
290 * be logged.
291 */
292 public void log(String msg, int msgLevel) {
293 if (getProject() != null) {
294 getProject().log(this, msg, msgLevel);
295 } else {
296 super.log(msg, msgLevel);
297 }
298 }
299
300 /**
301 * Logs a message with the given priority. This delegates
302 * the actual logging to the project.
303 *
304 * @param t The exception to be logged. Should not be <code>null</code>.
305 * @param msgLevel The message priority at which this message is to
306 * be logged.
307 * @since 1.7
308 */
309 public void log(Throwable t, int msgLevel) {
310 if (t != null) {
311 log(t.getMessage(), t, msgLevel);
312 }
313 }
314
315 /**
316 * Logs a message with the given priority. This delegates
317 * the actual logging to the project.
318 *
319 * @param msg The message to be logged. Should not be <code>null</code>.
320 * @param t The exception to be logged. May be <code>null</code>.
321 * @param msgLevel The message priority at which this message is to
322 * be logged.
323 * @since 1.7
324 */
325 public void log(String msg, Throwable t, int msgLevel) {
326 if (getProject() != null) {
327 getProject().log(this, msg, t, msgLevel);
328 } else {
329 super.log(msg, msgLevel);
330 }
331 }
332
333 /**
334 * Performs this task if it's still valid, or gets a replacement
335 * version and performs that otherwise.
336 *
337 * Performing a task consists of firing a task started event,
338 * configuring the task, executing it, and then firing task finished
339 * event. If a runtime exception is thrown, the task finished event
340 * is still fired, but with the exception as the cause.
341 */
342 public final void perform() {
343 if (!invalid) {
344 getProject().fireTaskStarted(this);
345 Throwable reason = null;
346 try {
347 maybeConfigure();
348 DispatchUtils.execute(this);
349 } catch (BuildException ex) {
350 if (ex.getLocation() == Location.UNKNOWN_LOCATION) {
351 ex.setLocation(getLocation());
352 }
353 reason = ex;
354 throw ex;
355 } catch (Exception ex) {
356 reason = ex;
357 BuildException be = new BuildException(ex);
358 be.setLocation(getLocation());
359 throw be;
360 } catch (Error ex) {
361 reason = ex;
362 throw ex;
363 } finally {
364 getProject().fireTaskFinished(this, reason);
365 }
366 } else {
367 UnknownElement ue = getReplacement();
368 Task task = ue.getTask();
369 task.perform();
370 }
371 }
372
373 /**
374 * Marks this task as invalid. Any further use of this task
375 * will go through a replacement with the updated definition.
376 */
377 final void markInvalid() {
378 invalid = true;
379 }
380
381 /**
382 * Has this task been marked invalid?
383 *
384 * @return true if this task is no longer valid. A new task should be
385 * configured in this case.
386 *
387 * @since Ant 1.5
388 */
389 protected final boolean isInvalid() {
390 return invalid;
391 }
392
393 /**
394 * Replacement element used if this task is invalidated.
395 */
396 private UnknownElement replacement;
397
398 /**
399 * Creates an UnknownElement that can be used to replace this task.
400 * Once this has been created once, it is cached and returned by
401 * future calls.
402 *
403 * @return the UnknownElement instance for the new definition of this task.
404 */
405 private UnknownElement getReplacement() {
406 if (replacement == null) {
407 replacement = new UnknownElement(taskType);
408 replacement.setProject(getProject());
409 replacement.setTaskType(taskType);
410 replacement.setTaskName(taskName);
411 replacement.setLocation(location);
412 replacement.setOwningTarget(target);
413 replacement.setRuntimeConfigurableWrapper(wrapper);
414 wrapper.setProxy(replacement);
415 replaceChildren(wrapper, replacement);
416 target.replaceChild(this, replacement);
417 replacement.maybeConfigure();
418 }
419 return replacement;
420 }
421
422 /**
423 * Recursively adds an UnknownElement instance for each child
424 * element of replacement.
425 *
426 * @since Ant 1.5.1
427 */
428 private void replaceChildren(RuntimeConfigurable wrapper,
429 UnknownElement parentElement) {
430 Enumeration e = wrapper.getChildren();
431 while (e.hasMoreElements()) {
432 RuntimeConfigurable childWrapper =
433 (RuntimeConfigurable) e.nextElement();
434 UnknownElement childElement =
435 new UnknownElement(childWrapper.getElementTag());
436 parentElement.addChild(childElement);
437 childElement.setProject(getProject());
438 childElement.setRuntimeConfigurableWrapper(childWrapper);
439 childWrapper.setProxy(childElement);
440 replaceChildren(childWrapper, childElement);
441 }
442 }
443
444 /**
445 * Return the type of task.
446 *
447 * @return the type of task.
448 */
449 public String getTaskType() {
450 return taskType;
451 }
452
453 /**
454 * Return the runtime configurable structure for this task.
455 *
456 * @return the runtime structure for this task.
457 */
458 protected RuntimeConfigurable getWrapper() {
459 return wrapper;
460 }
461
462 /**
463 * Bind a task to another; use this when configuring a newly created
464 * task to do work on behalf of another.
465 * Project, OwningTarget, TaskName, Location and Description are all copied
466 *
467 * Important: this method does not call {@link Task#init()}.
468 * If you are creating a task to delegate work to, call {@link Task#init()}
469 * to initialize it.
470 *
471 * @param owner owning target
472 * @since Ant1.7
473 */
474 public final void bindToOwner(Task owner) {
475 setProject(owner.getProject());
476 setOwningTarget(owner.getOwningTarget());
477 setTaskName(owner.getTaskName());
478 setDescription(owner.getDescription());
479 setLocation(owner.getLocation());
480 setTaskType(owner.getTaskType());
481 }
482 }