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