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 org.apache.tools.ant.dispatch.DispatchUtils;
022:
023: import java.util.Enumeration;
024: import java.io.IOException;
025:
026: /**
027: * Base class for all tasks.
028: *
029: * Use Project.createTask to create a new task instance rather than
030: * using this class directly for construction.
031: *
032: * @see Project#createTask
033: */
034: public abstract class Task extends ProjectComponent {
035: // CheckStyle:VisibilityModifier OFF - bc
036: /**
037: * Target this task belongs to, if any.
038: * @deprecated since 1.6.x.
039: * You should not be accessing this variable directly.
040: * Please use the {@link #getOwningTarget()} method.
041: */
042: protected Target target;
043:
044: /**
045: * Name of this task to be used for logging purposes.
046: * This defaults to the same as the type, but may be
047: * overridden by the user. For instance, the name "java"
048: * isn't terribly descriptive for a task used within
049: * another task - the outer task code can probably
050: * provide a better one.
051: * @deprecated since 1.6.x.
052: * You should not be accessing this variable directly.
053: * Please use the {@link #getTaskName()} method.
054: */
055: protected String taskName;
056:
057: /**
058: * Type of this task.
059: *
060: * @deprecated since 1.6.x.
061: * You should not be accessing this variable directly.
062: * Please use the {@link #getTaskType()} method.
063: */
064: protected String taskType;
065:
066: /**
067: * Wrapper for this object, used to configure it at runtime.
068: *
069: * @deprecated since 1.6.x.
070: * You should not be accessing this variable directly.
071: * Please use the {@link #getWrapper()} method.
072: */
073: protected RuntimeConfigurable wrapper;
074:
075: // CheckStyle:VisibilityModifier ON
076:
077: /**
078: * Whether or not this task is invalid. A task becomes invalid
079: * if a conflicting class is specified as the implementation for
080: * its type.
081: */
082: private boolean invalid;
083:
084: /** Sole constructor. */
085: public Task() {
086: }
087:
088: /**
089: * Sets the target container of this task.
090: *
091: * @param target Target in whose scope this task belongs.
092: * May be <code>null</code>, indicating a top-level task.
093: */
094: public void setOwningTarget(Target target) {
095: this .target = target;
096: }
097:
098: /**
099: * 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(
185: RuntimeConfigurable wrapper) {
186: this .wrapper = wrapper;
187: }
188:
189: // XXX: (Jon Skeet) The comment "if it hasn't been done already" may
190: // not be strictly true. wrapper.maybeConfigure() won't configure the same
191: // attributes/text more than once, but it may well add the children again,
192: // unless I've missed something.
193: /**
194: * Configures this task - if it hasn't been done already.
195: * If the task has been invalidated, it is replaced with an
196: * UnknownElement task which uses the new definition in the project.
197: *
198: * @exception BuildException if the task cannot be configured.
199: */
200: public void maybeConfigure() throws BuildException {
201: if (!invalid) {
202: if (wrapper != null) {
203: wrapper.maybeConfigure(getProject());
204: }
205: } else {
206: getReplacement();
207: }
208: }
209:
210: /**
211: * Force the task to be reconfigured from its RuntimeConfigurable.
212: */
213: public void reconfigure() {
214: if (wrapper != null) {
215: wrapper.reconfigure(getProject());
216: }
217: }
218:
219: /**
220: * Handles output by logging it with the INFO priority.
221: *
222: * @param output The output to log. Should not be <code>null</code>.
223: */
224: protected void handleOutput(String output) {
225: log(output, Project.MSG_INFO);
226: }
227:
228: /**
229: * Handles output by logging it with the INFO priority.
230: *
231: * @param output The output to log. Should not be <code>null</code>.
232: *
233: * @since Ant 1.5.2
234: */
235: protected void handleFlush(String output) {
236: handleOutput(output);
237: }
238:
239: /**
240: * Handle an input request by this task.
241: *
242: * @param buffer the buffer into which data is to be read.
243: * @param offset the offset into the buffer at which data is stored.
244: * @param length the amount of data to read.
245: *
246: * @return the number of bytes read.
247: *
248: * @exception IOException if the data cannot be read.
249: * @since Ant 1.6
250: */
251: protected int handleInput(byte[] buffer, int offset, int length)
252: throws IOException {
253: return getProject().defaultInput(buffer, offset, length);
254: }
255:
256: /**
257: * Handles an error output by logging it with the WARN priority.
258: *
259: * @param output The error output to log. Should not be <code>null</code>.
260: */
261: protected void handleErrorOutput(String output) {
262: log(output, Project.MSG_WARN);
263: }
264:
265: /**
266: * Handles an error line by logging it with the WARN priority.
267: *
268: * @param output The error output to log. Should not be <code>null</code>.
269: *
270: * @since Ant 1.5.2
271: */
272: protected void handleErrorFlush(String output) {
273: handleErrorOutput(output);
274: }
275:
276: /**
277: * Logs a message with the default (INFO) priority.
278: *
279: * @param msg The message to be logged. Should not be <code>null</code>.
280: */
281: public void log(String msg) {
282: log(msg, Project.MSG_INFO);
283: }
284:
285: /**
286: * Logs a message with the given priority. This delegates
287: * the actual logging to the project.
288: *
289: * @param msg The message to be logged. Should not be <code>null</code>.
290: * @param msgLevel The message priority at which this message is to
291: * be logged.
292: */
293: public void log(String msg, int msgLevel) {
294: if (getProject() != null) {
295: getProject().log(this , msg, msgLevel);
296: } else {
297: super .log(msg, msgLevel);
298: }
299: }
300:
301: /**
302: * Logs a message with the given priority. This delegates
303: * the actual logging to the project.
304: *
305: * @param t The exception to be logged. Should not be <code>null</code>.
306: * @param msgLevel The message priority at which this message is to
307: * be logged.
308: * @since 1.7
309: */
310: public void log(Throwable t, int msgLevel) {
311: if (t != null) {
312: log(t.getMessage(), t, msgLevel);
313: }
314: }
315:
316: /**
317: * Logs a message with the given priority. This delegates
318: * the actual logging to the project.
319: *
320: * @param msg The message to be logged. Should not be <code>null</code>.
321: * @param t The exception to be logged. May be <code>null</code>.
322: * @param msgLevel The message priority at which this message is to
323: * be logged.
324: * @since 1.7
325: */
326: public void log(String msg, Throwable t, int msgLevel) {
327: if (getProject() != null) {
328: getProject().log(this , msg, t, msgLevel);
329: } else {
330: super .log(msg, msgLevel);
331: }
332: }
333:
334: /**
335: * Performs this task if it's still valid, or gets a replacement
336: * version and performs that otherwise.
337: *
338: * Performing a task consists of firing a task started event,
339: * configuring the task, executing it, and then firing task finished
340: * event. If a runtime exception is thrown, the task finished event
341: * is still fired, but with the exception as the cause.
342: */
343: public final void perform() {
344: if (!invalid) {
345: getProject().fireTaskStarted(this );
346: Throwable reason = null;
347: try {
348: maybeConfigure();
349: DispatchUtils.execute(this );
350: } catch (BuildException ex) {
351: if (ex.getLocation() == Location.UNKNOWN_LOCATION) {
352: ex.setLocation(getLocation());
353: }
354: reason = ex;
355: throw ex;
356: } catch (Exception ex) {
357: reason = ex;
358: BuildException be = new BuildException(ex);
359: be.setLocation(getLocation());
360: throw be;
361: } catch (Error ex) {
362: reason = ex;
363: throw ex;
364: } finally {
365: getProject().fireTaskFinished(this , reason);
366: }
367: } else {
368: UnknownElement ue = getReplacement();
369: Task task = ue.getTask();
370: task.perform();
371: }
372: }
373:
374: /**
375: * Marks this task as invalid. Any further use of this task
376: * will go through a replacement with the updated definition.
377: */
378: final void markInvalid() {
379: invalid = true;
380: }
381:
382: /**
383: * Has this task been marked invalid?
384: *
385: * @return true if this task is no longer valid. A new task should be
386: * configured in this case.
387: *
388: * @since Ant 1.5
389: */
390: protected final boolean isInvalid() {
391: return invalid;
392: }
393:
394: /**
395: * Replacement element used if this task is invalidated.
396: */
397: private UnknownElement replacement;
398:
399: /**
400: * Creates an UnknownElement that can be used to replace this task.
401: * Once this has been created once, it is cached and returned by
402: * future calls.
403: *
404: * @return the UnknownElement instance for the new definition of this task.
405: */
406: private UnknownElement getReplacement() {
407: if (replacement == null) {
408: replacement = new UnknownElement(taskType);
409: replacement.setProject(getProject());
410: replacement.setTaskType(taskType);
411: replacement.setTaskName(taskName);
412: replacement.setLocation(location);
413: replacement.setOwningTarget(target);
414: replacement.setRuntimeConfigurableWrapper(wrapper);
415: wrapper.setProxy(replacement);
416: replaceChildren(wrapper, replacement);
417: target.replaceChild(this , replacement);
418: replacement.maybeConfigure();
419: }
420: return replacement;
421: }
422:
423: /**
424: * Recursively adds an UnknownElement instance for each child
425: * element of replacement.
426: *
427: * @since Ant 1.5.1
428: */
429: private void replaceChildren(RuntimeConfigurable wrapper,
430: UnknownElement parentElement) {
431: Enumeration e = wrapper.getChildren();
432: while (e.hasMoreElements()) {
433: RuntimeConfigurable childWrapper = (RuntimeConfigurable) e
434: .nextElement();
435: UnknownElement childElement = new UnknownElement(
436: childWrapper.getElementTag());
437: parentElement.addChild(childElement);
438: childElement.setProject(getProject());
439: childElement.setRuntimeConfigurableWrapper(childWrapper);
440: childWrapper.setProxy(childElement);
441: replaceChildren(childWrapper, childElement);
442: }
443: }
444:
445: /**
446: * Return the type of task.
447: *
448: * @return the type of task.
449: */
450: public String getTaskType() {
451: return taskType;
452: }
453:
454: /**
455: * Return the runtime configurable structure for this task.
456: *
457: * @return the runtime structure for this task.
458: */
459: protected RuntimeConfigurable getWrapper() {
460: return wrapper;
461: }
462:
463: /**
464: * Bind a task to another; use this when configuring a newly created
465: * task to do work on behalf of another.
466: * Project, OwningTarget, TaskName, Location and Description are all copied
467: *
468: * Important: this method does not call {@link Task#init()}.
469: * If you are creating a task to delegate work to, call {@link Task#init()}
470: * to initialize it.
471: *
472: * @param owner owning target
473: * @since Ant1.7
474: */
475: public final void bindToOwner(Task owner) {
476: setProject(owner.getProject());
477: setOwningTarget(owner.getOwningTarget());
478: setTaskName(owner.getTaskName());
479: setDescription(owner.getDescription());
480: setLocation(owner.getLocation());
481: setTaskType(owner.getTaskType());
482: }
483: }
|