1. /*
  2. * Copyright 2001-2002,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.taskdefs.optional.ide;
  18. import java.beans.PropertyChangeListener;
  19. import java.beans.PropertyChangeSupport;
  20. import java.io.File;
  21. import java.util.Enumeration;
  22. import java.util.StringTokenizer;
  23. import java.util.Vector;
  24. import org.apache.tools.ant.BuildEvent;
  25. import org.apache.tools.ant.BuildException;
  26. import org.apache.tools.ant.BuildListener;
  27. import org.apache.tools.ant.Project;
  28. import org.apache.tools.ant.ProjectHelper;
  29. import org.apache.tools.ant.Target;
  30. /**
  31. * This class wraps the Ant project information needed to
  32. * start Ant from Visual Age.
  33. * It serves the following purposes:
  34. * - acts as model for AntMakeFrame
  35. * - converts itself to/from String (to store the information
  36. * as ToolData in the VA repository)
  37. * - wraps Project functions for the GUI (get target list,
  38. * execute target)
  39. * - manages a seperate thread for Ant project execution
  40. * this allows interrupting a running build from a GUI
  41. *
  42. */
  43. class VAJBuildInfo implements Runnable {
  44. /**
  45. * This exception is thrown when a build is interrupted
  46. */
  47. public static class BuildInterruptedException extends BuildException {
  48. public String toString() {
  49. return "BUILD INTERRUPTED";
  50. }
  51. }
  52. /**
  53. * BuildListener which checks for interruption and throws Exception
  54. * if build process is interrupted. This class is a wrapper around
  55. * a 'real' listener.
  56. */
  57. private class InterruptedChecker implements BuildListener {
  58. // the real listener
  59. BuildListener wrappedListener;
  60. /**
  61. * Can only be constructed as wrapper around a real listener
  62. * @param listener the real listener
  63. */
  64. public InterruptedChecker(BuildListener listener) {
  65. super();
  66. wrappedListener = listener;
  67. }
  68. /**
  69. * checks if the thread was interrupted. When an
  70. * interrupt occurred, throw an Exception to stop
  71. * the execution.
  72. */
  73. protected void checkInterrupted() {
  74. if (buildThread.isInterrupted()) {
  75. throw new BuildInterruptedException();
  76. }
  77. }
  78. /**
  79. * Fired after the last target has finished. This event
  80. * will still be thrown if an error occurred during the build.
  81. */
  82. public void buildFinished(BuildEvent event) {
  83. wrappedListener.buildFinished(event);
  84. checkInterrupted();
  85. }
  86. /**
  87. * Fired before any targets are started.
  88. */
  89. public void buildStarted(BuildEvent event) {
  90. wrappedListener.buildStarted(event);
  91. checkInterrupted();
  92. }
  93. /**
  94. * Fired whenever a message is logged.
  95. */
  96. public void messageLogged(BuildEvent event) {
  97. wrappedListener.messageLogged(event);
  98. checkInterrupted();
  99. }
  100. /**
  101. * Fired when a target has finished. This event will
  102. * still be thrown if an error occurred during the build.
  103. */
  104. public void targetFinished(BuildEvent event) {
  105. wrappedListener.targetFinished(event);
  106. checkInterrupted();
  107. }
  108. /**
  109. * Fired when a target is started.
  110. */
  111. public void targetStarted(BuildEvent event) {
  112. wrappedListener.targetStarted(event);
  113. checkInterrupted();
  114. }
  115. /**
  116. * Fired when a task has finished. This event will still
  117. * be throw if an error occurred during the build.
  118. */
  119. public void taskFinished(BuildEvent event) {
  120. wrappedListener.taskFinished(event);
  121. checkInterrupted();
  122. }
  123. /**
  124. * Fired when a task is started.
  125. */
  126. public void taskStarted(BuildEvent event) {
  127. wrappedListener.taskStarted(event);
  128. checkInterrupted();
  129. }
  130. }
  131. // name of the VA project this BuildInfo belongs to
  132. private String vajProjectName = "";
  133. // name of the Ant build file
  134. private String buildFileName = "";
  135. // main targets found in the build file
  136. private Vector projectTargets = new Vector();
  137. // target selected for execution
  138. private String target = "";
  139. // log level
  140. private int outputMessageLevel = Project.MSG_INFO;
  141. // Ant Project created from build file
  142. private transient Project project;
  143. // is true if Project initialization was successful
  144. private transient boolean projectInitialized = false;
  145. // Support for bound properties
  146. protected transient PropertyChangeSupport propertyChange;
  147. // thread for Ant build execution
  148. private Thread buildThread;
  149. // the listener used to log output.
  150. private BuildListener projectLogger;
  151. /**
  152. * The addPropertyChangeListener method was generated to support the
  153. * propertyChange field.
  154. */
  155. public synchronized void addPropertyChangeListener(PropertyChangeListener listener) {
  156. getPropertyChange().addPropertyChangeListener(listener);
  157. }
  158. /**
  159. * Returns the BuildInfo information as String. The BuildInfo can
  160. * be rebuilt from that String by calling parse().
  161. * @return String
  162. */
  163. public String asDataString() {
  164. String result = getOutputMessageLevel() + "|" + getBuildFileName()
  165. + "|" + getTarget();
  166. for (Enumeration e = getProjectTargets().elements();
  167. e.hasMoreElements();) {
  168. result = result + "|" + e.nextElement();
  169. }
  170. return result;
  171. }
  172. /**
  173. * Search for the insert position to keep names a sorted list of Strings
  174. * This method has been copied from org.apache.tools.ant.Main
  175. */
  176. private static int findTargetPosition(Vector names, String name) {
  177. int res = names.size();
  178. for (int i = 0; i < names.size() && res == names.size(); i++) {
  179. if (name.compareTo((String) names.elementAt(i)) < 0) {
  180. res = i;
  181. }
  182. }
  183. return res;
  184. }
  185. /**
  186. * The firePropertyChange method was generated to support the propertyChange field.
  187. */
  188. public void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
  189. getPropertyChange().firePropertyChange(propertyName, oldValue, newValue);
  190. }
  191. /**
  192. * Returns the build file name.
  193. * @return build file name.
  194. */
  195. public String getBuildFileName() {
  196. return buildFileName;
  197. }
  198. /**
  199. * Returns the log level
  200. * @return log level.
  201. */
  202. public int getOutputMessageLevel() {
  203. return outputMessageLevel;
  204. }
  205. /**
  206. * Returns the Ant project
  207. * @return org.apache.tools.ant.Project
  208. */
  209. private Project getProject() {
  210. if (project == null) {
  211. project = new Project();
  212. }
  213. return project;
  214. }
  215. /**
  216. * return a list of all targets in the current buildfile
  217. */
  218. public Vector getProjectTargets() {
  219. return projectTargets;
  220. }
  221. /**
  222. * Accessor for the propertyChange field.
  223. */
  224. protected PropertyChangeSupport getPropertyChange() {
  225. if (propertyChange == null) {
  226. propertyChange = new PropertyChangeSupport(this);
  227. }
  228. return propertyChange;
  229. }
  230. /**
  231. * returns the selected target.
  232. */
  233. public String getTarget() {
  234. return target;
  235. }
  236. /**
  237. * returns the VA project name
  238. */
  239. public String getVAJProjectName() {
  240. return vajProjectName;
  241. }
  242. /**
  243. * Initializes the Ant project. Assumes that the
  244. * project attribute is already set.
  245. */
  246. private void initProject() {
  247. try {
  248. project.init();
  249. File buildFile = new File(getBuildFileName());
  250. project.setUserProperty("ant.file", buildFile.getAbsolutePath());
  251. ProjectHelper.configureProject(project, buildFile);
  252. setProjectInitialized(true);
  253. } catch (RuntimeException exc) {
  254. setProjectInitialized(false);
  255. throw exc;
  256. } catch (Error err) {
  257. setProjectInitialized(false);
  258. throw err;
  259. }
  260. }
  261. /**
  262. * Returns true, if the Ant project is initialized.
  263. * (i.e., if the buildfile loaded).
  264. */
  265. public boolean isProjectInitialized() {
  266. return projectInitialized;
  267. }
  268. /**
  269. * Creates a BuildInfo object from a String
  270. * The String must be in the format
  271. * outputMessageLevel'|'buildFileName'|'defaultTarget'|'(project target'|')*
  272. *
  273. * @return org.apache.tools.ant.taskdefs.optional.vaj.BuildInfo
  274. * @param data String
  275. */
  276. public static VAJBuildInfo parse(String data) {
  277. VAJBuildInfo result = new VAJBuildInfo();
  278. try {
  279. StringTokenizer tok = new StringTokenizer(data, "|");
  280. result.setOutputMessageLevel(tok.nextToken());
  281. result.setBuildFileName(tok.nextToken());
  282. result.setTarget(tok.nextToken());
  283. while (tok.hasMoreTokens()) {
  284. result.projectTargets.addElement(tok.nextToken());
  285. }
  286. } catch (Throwable t) {
  287. // if parsing the info fails, just return
  288. // an empty VAJBuildInfo
  289. }
  290. return result;
  291. }
  292. /**
  293. * The removePropertyChangeListener method was generated
  294. * to support the propertyChange field.
  295. */
  296. public synchronized void removePropertyChangeListener(PropertyChangeListener listener) {
  297. getPropertyChange().removePropertyChangeListener(listener);
  298. }
  299. /**
  300. * Sets the build file name
  301. * @param buildFileName build file name
  302. */
  303. public void setBuildFileName(String newBuildFileName) {
  304. String oldValue = buildFileName;
  305. buildFileName = newBuildFileName;
  306. setProjectInitialized(false);
  307. firePropertyChange("buildFileName", oldValue, buildFileName);
  308. }
  309. /**
  310. * Sets the log level (value must be one of the constants in Project)
  311. * @param outputMessageLevel log level.
  312. */
  313. public void setOutputMessageLevel(int newOutputMessageLevel) {
  314. int oldValue = outputMessageLevel;
  315. outputMessageLevel = newOutputMessageLevel;
  316. firePropertyChange("outputMessageLevel",
  317. new Integer(oldValue), new Integer(outputMessageLevel));
  318. }
  319. /**
  320. * Sets the log level (value must be one of the constants in Project)
  321. * @param outputMessageLevel log level as String.
  322. */
  323. private void setOutputMessageLevel(String outputMessageLevel) {
  324. int level = Integer.parseInt(outputMessageLevel);
  325. setOutputMessageLevel(level);
  326. }
  327. /**
  328. * sets the initialized flag
  329. */
  330. private void setProjectInitialized(boolean initialized) {
  331. Boolean oldValue = new Boolean(projectInitialized);
  332. projectInitialized = initialized;
  333. firePropertyChange("projectInitialized", oldValue, new Boolean(projectInitialized));
  334. }
  335. /**
  336. * Sets the target to execute when executeBuild is called
  337. * @param newTarget build target
  338. */
  339. public void setTarget(String newTarget) {
  340. String oldValue = target;
  341. target = newTarget;
  342. firePropertyChange("target", oldValue, target);
  343. }
  344. /**
  345. * Sets the name of the Visual Age for Java project where
  346. * this BuildInfo belongs to
  347. * @param newProjectName VAJ project
  348. */
  349. public void setVAJProjectName(String newVAJProjectName) {
  350. String oldValue = vajProjectName;
  351. vajProjectName = newVAJProjectName;
  352. firePropertyChange("VAJProjectName", oldValue, vajProjectName);
  353. }
  354. /**
  355. * reloads the build file and updates the target list
  356. */
  357. public void updateTargetList() {
  358. project = new Project();
  359. initProject();
  360. projectTargets.removeAllElements();
  361. Enumeration ptargets = project.getTargets().elements();
  362. while (ptargets.hasMoreElements()) {
  363. Target currentTarget = (Target) ptargets.nextElement();
  364. if (currentTarget.getDescription() != null) {
  365. String targetName = currentTarget.getName();
  366. int pos = findTargetPosition(projectTargets, targetName);
  367. projectTargets.insertElementAt(targetName, pos);
  368. }
  369. }
  370. }
  371. /**
  372. * cancels a build.
  373. */
  374. public void cancelBuild() {
  375. buildThread.interrupt();
  376. }
  377. /**
  378. * Executes the target set by setTarget().
  379. * @param listener BuildListener for the output of the build
  380. */
  381. public void executeProject(BuildListener logger) {
  382. Throwable error;
  383. projectLogger = logger;
  384. try {
  385. buildThread = new Thread(this);
  386. buildThread.setPriority(Thread.MIN_PRIORITY);
  387. buildThread.start();
  388. } catch (RuntimeException exc) {
  389. error = exc;
  390. throw exc;
  391. } catch (Error err) {
  392. error = err;
  393. throw err;
  394. }
  395. }
  396. /**
  397. * Executes a build. This method is executed by
  398. * the Ant execution thread
  399. */
  400. public void run() {
  401. try {
  402. InterruptedChecker ic = new InterruptedChecker(projectLogger);
  403. BuildEvent e = new BuildEvent(getProject());
  404. try {
  405. ic.buildStarted(e);
  406. if (!isProjectInitialized()) {
  407. initProject();
  408. }
  409. project.addBuildListener(ic);
  410. project.executeTarget(target);
  411. ic.buildFinished(e);
  412. } catch (Throwable t) {
  413. e.setException(t);
  414. ic.buildFinished(e);
  415. } finally {
  416. project.removeBuildListener(ic);
  417. }
  418. } catch (Throwable t2) {
  419. System.out.println("unexpected exception!");
  420. t2.printStackTrace();
  421. }
  422. }
  423. }