1. /*
  2. * Copyright 2001-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.sitraka;
  18. import java.io.File;
  19. import java.io.FileWriter;
  20. import java.io.IOException;
  21. import java.io.OutputStream;
  22. import java.io.PrintWriter;
  23. import java.io.StringWriter;
  24. import java.util.Vector;
  25. import org.apache.tools.ant.BuildException;
  26. import org.apache.tools.ant.Project;
  27. import org.apache.tools.ant.Task;
  28. import org.apache.tools.ant.taskdefs.Execute;
  29. import org.apache.tools.ant.taskdefs.LogStreamHandler;
  30. import org.apache.tools.ant.types.Commandline;
  31. import org.apache.tools.ant.types.CommandlineJava;
  32. import org.apache.tools.ant.types.EnumeratedAttribute;
  33. import org.apache.tools.ant.types.FileSet;
  34. import org.apache.tools.ant.types.Path;
  35. import org.apache.tools.ant.util.JavaEnvUtils;
  36. /**
  37. * Runs Sitraka JProbe Coverage.
  38. *
  39. * Options are pretty numerous, you'd better check the manual for a full
  40. * descriptions of options. (not that simple since they differ from the online
  41. * help, from the usage command line and from the examples...)
  42. * <p>
  43. * For additional information, visit <a href="http://www.sitraka.com">www.sitraka.com</a>
  44. *
  45. *
  46. * @ant.task name="jpcoverage" category="metrics"
  47. */
  48. public class Coverage extends CovBase {
  49. protected Commandline cmdl = new Commandline();
  50. protected CommandlineJava cmdlJava = new CommandlineJava();
  51. protected String function = "coverage";
  52. protected String seedName;
  53. protected File inputFile;
  54. protected File javaExe;
  55. protected String vm;
  56. protected boolean applet = false;
  57. /** this is a somewhat annoying thing, set it to never */
  58. protected String exitPrompt = "never";
  59. protected Filters filters = new Filters();
  60. protected Triggers triggers;
  61. protected String finalSnapshot = "coverage";
  62. protected String recordFromStart = "coverage";
  63. protected File snapshotDir;
  64. protected File workingDir;
  65. protected boolean trackNatives = false;
  66. protected Socket socket;
  67. protected int warnLevel = 0;
  68. protected Vector filesets = new Vector();
  69. //--------- setters used via reflection --
  70. /** seed name for snapshot file. Can be null, default to snap */
  71. public void setSeedname(String value) {
  72. seedName = value;
  73. }
  74. /**
  75. * @ant.attribute ignore="true"
  76. */
  77. public void setInputfile(File value) {
  78. inputFile = value;
  79. }
  80. /**
  81. * Path to the java executable.
  82. */
  83. public void setJavaexe(File value) {
  84. javaExe = value;
  85. }
  86. public static class Javavm extends EnumeratedAttribute {
  87. public String[] getValues() {
  88. return new String[]{"java2", "jdk118", "jdk117"};
  89. }
  90. }
  91. /**
  92. * Indicates which virtual machine to run: "jdk117", "jdk118" or "java2".
  93. * Can be null, default to "java2". */
  94. public void setVm(Javavm value) {
  95. vm = value.getValue();
  96. }
  97. /**
  98. * If true, run an applet.
  99. */
  100. public void setApplet(boolean value) {
  101. applet = value;
  102. }
  103. /**
  104. * Toggles display of the console prompt: always, error, never
  105. */
  106. public void setExitprompt(String value) {
  107. exitPrompt = value;
  108. }
  109. /**
  110. * Defines class/method filters based on pattern matching.
  111. * The syntax is filters is similar to a fileset.
  112. */
  113. public Filters createFilters() {
  114. return filters;
  115. }
  116. /**
  117. * Defines events to use for interacting with the
  118. * collection of data performed during coverage.
  119. *
  120. * For example you may run a whole application but only decide
  121. * to collect data once it reaches a certain method and once it
  122. * exits another one.
  123. */
  124. public Triggers createTriggers() {
  125. if (triggers == null) {
  126. triggers = new Triggers();
  127. }
  128. return triggers;
  129. }
  130. /**
  131. * Define a host and port to connect to if you want to do
  132. * remote viewing.
  133. */
  134. public Socket createSocket() {
  135. if (socket == null) {
  136. socket = new Socket();
  137. }
  138. return socket;
  139. }
  140. public static class Finalsnapshot extends EnumeratedAttribute {
  141. public String[] getValues() {
  142. return new String[]{"coverage", "none", "all"};
  143. }
  144. }
  145. /**
  146. * Type of snapshot to send at program termination: none, coverage, all.
  147. * Can be null, default to none
  148. */
  149. public void setFinalsnapshot(String value) {
  150. finalSnapshot = value;
  151. }
  152. public static class Recordfromstart extends EnumeratedAttribute {
  153. public String[] getValues() {
  154. return new String[]{"coverage", "none", "all"};
  155. }
  156. }
  157. /**
  158. * "all", "coverage", or "none".
  159. */
  160. public void setRecordfromstart(Recordfromstart value) {
  161. recordFromStart = value.getValue();
  162. }
  163. /**
  164. * Set warning level (0-3, where 0 is the least amount of warnings).
  165. */
  166. public void setWarnlevel(Integer value) {
  167. warnLevel = value.intValue();
  168. }
  169. /**
  170. * The path to the directory where snapshot files are stored.
  171. * Choose a directory that is reachable by both the remote
  172. * and local computers, and enter the same path on the command-line
  173. * and in the viewer.
  174. */
  175. public void setSnapshotdir(File value) {
  176. snapshotDir = value;
  177. }
  178. /**
  179. * The physical path to the working directory for the VM.
  180. */
  181. public void setWorkingdir(File value) {
  182. workingDir = value;
  183. }
  184. /**
  185. * If true, track native methods.
  186. */
  187. public void setTracknatives(boolean value) {
  188. trackNatives = value;
  189. }
  190. //
  191. /**
  192. * Adds a JVM argument.
  193. */
  194. public Commandline.Argument createJvmarg() {
  195. return cmdlJava.createVmArgument();
  196. }
  197. /**
  198. * Adds a command argument.
  199. */
  200. public Commandline.Argument createArg() {
  201. return cmdlJava.createArgument();
  202. }
  203. /**
  204. * classpath to run the files.
  205. */
  206. public Path createClasspath() {
  207. return cmdlJava.createClasspath(getProject()).createPath();
  208. }
  209. /**
  210. * classname to run as standalone or runner for filesets.
  211. */
  212. public void setClassname(String value) {
  213. cmdlJava.setClassname(value);
  214. }
  215. /**
  216. * the classnames to execute.
  217. */
  218. public void addFileset(FileSet fs) {
  219. filesets.addElement(fs);
  220. }
  221. //---------------- the tedious job begins here
  222. public Coverage() {
  223. }
  224. /** execute the jplauncher by providing a parameter file */
  225. public void execute() throws BuildException {
  226. File paramfile = null;
  227. // if an input file is used, all other options are ignored...
  228. if (inputFile == null) {
  229. checkOptions();
  230. paramfile = createParamFile();
  231. } else {
  232. paramfile = inputFile;
  233. }
  234. try {
  235. // we need to run Coverage from his directory due to dll/jar issues
  236. cmdl.setExecutable(findExecutable("jplauncher"));
  237. cmdl.createArgument().setValue("-jp_input=" + paramfile.getAbsolutePath());
  238. // use the custom handler for stdin issues
  239. LogStreamHandler handler = new CoverageStreamHandler(this);
  240. Execute exec = new Execute(handler);
  241. log(cmdl.describeCommand(), Project.MSG_VERBOSE);
  242. exec.setCommandline(cmdl.getCommandline());
  243. int exitValue = exec.execute();
  244. if (Execute.isFailure(exitValue)) {
  245. throw new BuildException("JProbe Coverage failed (" + exitValue + ")");
  246. }
  247. } catch (IOException e) {
  248. throw new BuildException("Failed to execute JProbe Coverage.", e);
  249. } finally {
  250. //@todo should be removed once switched to JDK1.2
  251. if (inputFile == null && paramfile != null) {
  252. paramfile.delete();
  253. }
  254. }
  255. }
  256. /** wheck what is necessary to check, Coverage will do the job for us */
  257. protected void checkOptions() throws BuildException {
  258. // check coverage home
  259. if (getHome() == null || !getHome().isDirectory()) {
  260. throw new BuildException("Invalid home directory. Must point to JProbe home directory");
  261. }
  262. File jar = findCoverageJar();
  263. if (!jar.exists()) {
  264. throw new BuildException("Cannot find Coverage directory: " + getHome());
  265. }
  266. // make sure snapshot dir exists and is resolved
  267. if (snapshotDir == null) {
  268. snapshotDir = new File(".");
  269. }
  270. snapshotDir = getProject().resolveFile(snapshotDir.getPath());
  271. if (!snapshotDir.isDirectory() || !snapshotDir.exists()) {
  272. throw new BuildException("Snapshot directory does not exists :" + snapshotDir);
  273. }
  274. if (workingDir == null) {
  275. workingDir = new File(".");
  276. }
  277. workingDir = getProject().resolveFile(workingDir.getPath());
  278. // check for info, do your best to select the java executable.
  279. // JProbe 3.0 fails if there is no javaexe option. So
  280. if (javaExe == null && (vm == null || "java2".equals(vm))) {
  281. if (!JavaEnvUtils.isJavaVersion(JavaEnvUtils.JAVA_1_1)) {
  282. if (vm == null) {
  283. vm = "java2";
  284. }
  285. javaExe = new File(JavaEnvUtils.getJreExecutable("java"));
  286. }
  287. }
  288. }
  289. /**
  290. * return the command line parameters. Parameters can either be passed
  291. * to the command line and stored to a file (then use the -jp_input=<filename>)
  292. * if they are too numerous.
  293. */
  294. protected String[] getParameters() {
  295. Vector params = new Vector();
  296. params.addElement("-jp_function=" + function);
  297. if (vm != null) {
  298. params.addElement("-jp_vm=" + vm);
  299. }
  300. if (javaExe != null) {
  301. params.addElement("-jp_java_exe=" + getProject().resolveFile(javaExe.getPath()));
  302. }
  303. params.addElement("-jp_working_dir=" + workingDir.getPath());
  304. params.addElement("-jp_snapshot_dir=" + snapshotDir.getPath());
  305. params.addElement("-jp_record_from_start=" + recordFromStart);
  306. params.addElement("-jp_warn=" + warnLevel);
  307. if (seedName != null) {
  308. params.addElement("-jp_output_file=" + seedName);
  309. }
  310. params.addElement("-jp_filter=" + filters.toString());
  311. if (triggers != null) {
  312. params.addElement("-jp_trigger=" + triggers.toString());
  313. }
  314. if (finalSnapshot != null) {
  315. params.addElement("-jp_final_snapshot=" + finalSnapshot);
  316. }
  317. params.addElement("-jp_exit_prompt=" + exitPrompt);
  318. //params.addElement("-jp_append=" + append);
  319. params.addElement("-jp_track_natives=" + trackNatives);
  320. //.... now the jvm
  321. // arguments
  322. String[] vmargs = cmdlJava.getVmCommand().getArguments();
  323. for (int i = 0; i < vmargs.length; i++) {
  324. params.addElement(vmargs[i]);
  325. }
  326. // classpath
  327. Path classpath = cmdlJava.getClasspath();
  328. if (classpath != null && classpath.size() > 0) {
  329. params.addElement("-classpath " + classpath.toString());
  330. }
  331. // classname (runner or standalone)
  332. if (cmdlJava.getClassname() != null) {
  333. params.addElement(cmdlJava.getClassname());
  334. }
  335. // arguments for classname
  336. String[] args = cmdlJava.getJavaCommand().getArguments();
  337. for (int i = 0; i < args.length; i++) {
  338. params.addElement(args[i]);
  339. }
  340. String[] array = new String[params.size()];
  341. params.copyInto(array);
  342. return array;
  343. }
  344. /**
  345. * create the parameter file from the given options. The file is
  346. * created with a random name in the current directory.
  347. * @return the file object where are written the configuration to run
  348. * JProbe Coverage
  349. * @throws BuildException thrown if something bad happens while writing
  350. * the arguments to the file.
  351. */
  352. protected File createParamFile() throws BuildException {
  353. //@todo change this when switching to JDK 1.2 and use File.createTmpFile()
  354. File file = createTempFile("jpcov");
  355. file.deleteOnExit();
  356. log("Creating parameter file: " + file, Project.MSG_VERBOSE);
  357. // options need to be one per line in the parameter file
  358. // so write them all in a single string
  359. StringWriter sw = new StringWriter();
  360. PrintWriter pw = new PrintWriter(sw);
  361. String[] params = getParameters();
  362. for (int i = 0; i < params.length; i++) {
  363. pw.println(params[i]);
  364. }
  365. pw.flush();
  366. log("JProbe Coverage parameters:\n" + sw.toString(), Project.MSG_VERBOSE);
  367. // now write them to the file
  368. FileWriter fw = null;
  369. try {
  370. fw = new FileWriter(file);
  371. fw.write(sw.toString());
  372. fw.flush();
  373. } catch (IOException e) {
  374. throw new BuildException("Could not write parameter file " + file, e);
  375. } finally {
  376. if (fw != null) {
  377. try {
  378. fw.close();
  379. } catch (IOException ignored) {
  380. }
  381. }
  382. }
  383. return file;
  384. }
  385. /** specific pumper to avoid those nasty stdin issues */
  386. static class CoverageStreamHandler extends LogStreamHandler {
  387. CoverageStreamHandler(Task task) {
  388. super(task, Project.MSG_INFO, Project.MSG_WARN);
  389. }
  390. /**
  391. * there are some issues concerning all JProbe executable
  392. * In our case a 'Press ENTER to close this window..." will
  393. * be displayed in the current window waiting for enter.
  394. * So I'm closing the stream right away to avoid problems.
  395. */
  396. public void setProcessInputStream(OutputStream os) {
  397. try {
  398. os.close();
  399. } catch (IOException ignored) {
  400. }
  401. }
  402. }
  403. }