You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

Java.java 24 kB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807
  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.taskdefs;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.io.PrintWriter;
  21. import java.io.StringWriter;
  22. import java.util.Vector;
  23. import org.apache.tools.ant.BuildException;
  24. import org.apache.tools.ant.ExitException;
  25. import org.apache.tools.ant.Project;
  26. import org.apache.tools.ant.Task;
  27. import org.apache.tools.ant.types.Commandline;
  28. import org.apache.tools.ant.types.CommandlineJava;
  29. import org.apache.tools.ant.types.Environment;
  30. import org.apache.tools.ant.types.Path;
  31. import org.apache.tools.ant.types.PropertySet;
  32. import org.apache.tools.ant.types.Reference;
  33. import org.apache.tools.ant.types.Assertions;
  34. import org.apache.tools.ant.types.Permissions;
  35. /**
  36. * Launcher for Java applications. Allows use of
  37. * the same JVM for the called application thus resulting in much
  38. * faster operation.
  39. *
  40. * @since Ant 1.1
  41. *
  42. * @ant.task category="java"
  43. */
  44. public class Java extends Task {
  45. private CommandlineJava cmdl = new CommandlineJava();
  46. private Environment env = new Environment();
  47. private boolean fork = false;
  48. private boolean newEnvironment = false;
  49. private File dir = null;
  50. private boolean failOnError = false;
  51. private Long timeout = null;
  52. private Redirector redirector = new Redirector(this);
  53. private String resultProperty;
  54. private Permissions perm = null;
  55. private boolean spawn = false;
  56. private boolean incompatibleWithSpawn = false;
  57. /**
  58. * Do the execution.
  59. * @throws BuildException if failOnError is set to true and the application
  60. * returns a non 0 result code
  61. */
  62. public void execute() throws BuildException {
  63. File savedDir = dir;
  64. Permissions savedPermissions = perm;
  65. int err = -1;
  66. try {
  67. err = executeJava();
  68. if (err != 0) {
  69. if (failOnError) {
  70. throw new BuildException("Java returned: " + err, getLocation());
  71. } else {
  72. log("Java Result: " + err, Project.MSG_ERR);
  73. }
  74. }
  75. maybeSetResultPropertyValue(err);
  76. } finally {
  77. dir = savedDir;
  78. perm = savedPermissions;
  79. }
  80. }
  81. /**
  82. * Do the execution and return a return code.
  83. *
  84. * @return the return code from the execute java class if it was
  85. * executed in a separate VM (fork = "yes").
  86. *
  87. * @throws BuildException if required parameters are missing
  88. */
  89. public int executeJava() throws BuildException {
  90. String classname = cmdl.getClassname();
  91. if (classname == null && cmdl.getJar() == null) {
  92. throw new BuildException("Classname must not be null.");
  93. }
  94. if (!fork && cmdl.getJar() != null) {
  95. throw new BuildException("Cannot execute a jar in non-forked mode."
  96. + " Please set fork='true'. ");
  97. }
  98. if (spawn && !fork) {
  99. throw new BuildException("Cannot spawn a java process in non-forked mode."
  100. + " Please set fork='true'. ");
  101. }
  102. if (spawn && incompatibleWithSpawn) {
  103. getProject().log("spawn does not allow attributes related to input, "
  104. + "output, error, result", Project.MSG_ERR);
  105. getProject().log("spawn does not also not allow timeout", Project.MSG_ERR);
  106. throw new BuildException("You have used an attribute which is "
  107. + "not compatible with spawn");
  108. }
  109. if (cmdl.getAssertions() != null && !fork) {
  110. log("Assertion statements are currently ignored in non-forked mode");
  111. }
  112. if (fork) {
  113. if (perm != null) {
  114. log("Permissions can not be set this way in forked mode.", Project.MSG_WARN);
  115. }
  116. log(cmdl.describeCommand(), Project.MSG_VERBOSE);
  117. } else {
  118. if (cmdl.getVmCommand().size() > 1) {
  119. log("JVM args ignored when same JVM is used.",
  120. Project.MSG_WARN);
  121. }
  122. if (dir != null) {
  123. log("Working directory ignored when same JVM is used.",
  124. Project.MSG_WARN);
  125. }
  126. if (newEnvironment || null != env.getVariables()) {
  127. log("Changes to environment variables are ignored when same "
  128. + "JVM is used.", Project.MSG_WARN);
  129. }
  130. if (cmdl.getBootclasspath() != null) {
  131. log("bootclasspath ignored when same JVM is used.",
  132. Project.MSG_WARN);
  133. }
  134. if (perm == null && failOnError == true) {
  135. perm = new Permissions(true);
  136. log("running " + this.cmdl.getClassname()
  137. + " with default permissions (exit forbidden)", Project.MSG_VERBOSE);
  138. }
  139. log("Running in same VM " + cmdl.describeJavaCommand(),
  140. Project.MSG_VERBOSE);
  141. }
  142. try {
  143. if (fork) {
  144. if (!spawn) {
  145. return fork(cmdl.getCommandline());
  146. } else {
  147. spawn(cmdl.getCommandline());
  148. return 0;
  149. }
  150. } else {
  151. try {
  152. run(cmdl);
  153. return 0;
  154. } catch (ExitException ex) {
  155. return ex.getStatus();
  156. }
  157. }
  158. } catch (BuildException e) {
  159. if (failOnError) {
  160. throw e;
  161. } else {
  162. log(e);
  163. return 0;
  164. }
  165. } catch (Throwable t) {
  166. if (failOnError) {
  167. throw new BuildException(t);
  168. } else {
  169. log(t);
  170. return 0;
  171. }
  172. }
  173. }
  174. /**
  175. * set whether or not you want the process to be spawned
  176. * default is not spawned
  177. * @param spawn if true you do not want ant to wait for the end of the process
  178. * @since ant 1.6
  179. */
  180. public void setSpawn(boolean spawn) {
  181. this.spawn = spawn;
  182. }
  183. /**
  184. * Set the classpath to be used when running the Java class
  185. *
  186. * @param s an Ant Path object containing the classpath.
  187. */
  188. public void setClasspath(Path s) {
  189. createClasspath().append(s);
  190. }
  191. /**
  192. * Adds a path to the classpath.
  193. *
  194. * @return created classpath
  195. */
  196. public Path createClasspath() {
  197. return cmdl.createClasspath(getProject()).createPath();
  198. }
  199. /**
  200. * Adds a path to the bootclasspath.
  201. * @since Ant 1.6
  202. *
  203. * @return created bootclasspath
  204. */
  205. public Path createBootclasspath() {
  206. return cmdl.createBootclasspath(getProject()).createPath();
  207. }
  208. /**
  209. * Sets the permissions for the application run inside the same JVM.
  210. * @since Ant 1.6
  211. * @return .
  212. */
  213. public Permissions createPermissions() {
  214. if (perm == null) {
  215. perm = new Permissions();
  216. }
  217. return perm;
  218. }
  219. /**
  220. * Classpath to use, by reference.
  221. *
  222. * @param r a reference to an existing classpath
  223. */
  224. public void setClasspathRef(Reference r) {
  225. createClasspath().setRefid(r);
  226. }
  227. /**
  228. * The location of the JAR file to execute.
  229. *
  230. * @param jarfile the jarfile that one wants to execute
  231. *
  232. * @throws BuildException if there is also a main class specified
  233. */
  234. public void setJar(File jarfile) throws BuildException {
  235. if (cmdl.getClassname() != null) {
  236. throw new BuildException("Cannot use 'jar' and 'classname' "
  237. + "attributes in same command.");
  238. }
  239. cmdl.setJar(jarfile.getAbsolutePath());
  240. }
  241. /**
  242. * Sets the Java class to execute.
  243. *
  244. * @param s the name of the main class
  245. *
  246. * @throws BuildException if the jar attribute has been set
  247. */
  248. public void setClassname(String s) throws BuildException {
  249. if (cmdl.getJar() != null) {
  250. throw new BuildException("Cannot use 'jar' and 'classname' "
  251. + "attributes in same command");
  252. }
  253. cmdl.setClassname(s);
  254. }
  255. /**
  256. * Deprecated: use nested arg instead.
  257. * Set the command line arguments for the class.
  258. *
  259. * @param s arguments
  260. *
  261. * @ant.attribute ignore="true"
  262. */
  263. public void setArgs(String s) {
  264. log("The args attribute is deprecated. "
  265. + "Please use nested arg elements.", Project.MSG_WARN);
  266. cmdl.createArgument().setLine(s);
  267. }
  268. /**
  269. * If set, system properties will be copied to the cloned VM - as
  270. * well as the bootclasspath unless you have explicitly specified
  271. * a bootclaspath.
  272. *
  273. * <p>Doesn't have any effect unless fork is true.</p>
  274. *
  275. * @since Ant 1.7
  276. */
  277. public void setCloneVm(boolean cloneVm) {
  278. cmdl.setCloneVm(cloneVm);
  279. }
  280. /**
  281. * Adds a command-line argument.
  282. *
  283. * @return created argument
  284. */
  285. public Commandline.Argument createArg() {
  286. return cmdl.createArgument();
  287. }
  288. /**
  289. * The name of a property in which the return code of the
  290. * command should be stored. Only of interest if failonerror=false.
  291. *
  292. * @param resultProperty name of property
  293. *
  294. * @since Ant 1.6
  295. */
  296. public void setResultProperty(String resultProperty) {
  297. this.resultProperty = resultProperty;
  298. }
  299. /**
  300. * helper method to set result property to the
  301. * passed in value if appropriate
  302. *
  303. * @param result the exit code
  304. */
  305. protected void maybeSetResultPropertyValue(int result) {
  306. String res = Integer.toString(result);
  307. if (resultProperty != null) {
  308. getProject().setNewProperty(resultProperty, res);
  309. }
  310. }
  311. /**
  312. * If true, execute in a new VM.
  313. *
  314. * @param s do you want to run Java in a new VM.
  315. */
  316. public void setFork(boolean s) {
  317. this.fork = s;
  318. }
  319. /**
  320. * Set the command line arguments for the JVM.
  321. *
  322. * @param s jvmargs
  323. */
  324. public void setJvmargs(String s) {
  325. log("The jvmargs attribute is deprecated. "
  326. + "Please use nested jvmarg elements.", Project.MSG_WARN);
  327. cmdl.createVmArgument().setLine(s);
  328. }
  329. /**
  330. * Adds a JVM argument.
  331. *
  332. * @return JVM argument created
  333. */
  334. public Commandline.Argument createJvmarg() {
  335. return cmdl.createVmArgument();
  336. }
  337. /**
  338. * Set the command used to start the VM (only if forking).
  339. *
  340. * @param s command to start the VM
  341. */
  342. public void setJvm(String s) {
  343. cmdl.setVm(s);
  344. }
  345. /**
  346. * Adds a system property.
  347. *
  348. * @param sysp system property
  349. */
  350. public void addSysproperty(Environment.Variable sysp) {
  351. cmdl.addSysproperty(sysp);
  352. }
  353. /**
  354. * Adds a set of properties as system properties.
  355. *
  356. * @param sysp set of properties to add
  357. *
  358. * @since Ant 1.6
  359. */
  360. public void addSyspropertyset(PropertySet sysp) {
  361. cmdl.addSyspropertyset(sysp);
  362. }
  363. /**
  364. * If true, then fail if the command exits with a
  365. * returncode other than 0
  366. *
  367. * @param fail if true fail the build when the command exits with a non
  368. * zero returncode
  369. */
  370. public void setFailonerror(boolean fail) {
  371. failOnError = fail;
  372. incompatibleWithSpawn |= fail;
  373. }
  374. /**
  375. * The working directory of the process
  376. *
  377. * @param d working directory
  378. *
  379. */
  380. public void setDir(File d) {
  381. this.dir = d;
  382. }
  383. /**
  384. * File the output of the process is redirected to.
  385. *
  386. * @param out name of the output file
  387. */
  388. public void setOutput(File out) {
  389. redirector.setOutput(out);
  390. incompatibleWithSpawn = true;
  391. }
  392. /**
  393. * Set the input to use for the task
  394. *
  395. * @param input name of the input file
  396. */
  397. public void setInput(File input) {
  398. redirector.setInput(input);
  399. incompatibleWithSpawn = true;
  400. }
  401. /**
  402. * Set the string to use as input
  403. *
  404. * @param inputString the string which is used as the input source
  405. */
  406. public void setInputString(String inputString) {
  407. redirector.setInputString(inputString);
  408. incompatibleWithSpawn = true;
  409. }
  410. /**
  411. * Controls whether error output of exec is logged. This is only useful
  412. * when output is being redirected and error output is desired in the
  413. * Ant log
  414. *
  415. * @param logError get in the ant log the messages coming from stderr
  416. * in the case that fork = true
  417. */
  418. public void setLogError(boolean logError) {
  419. redirector.setLogError(logError);
  420. incompatibleWithSpawn = true;
  421. }
  422. /**
  423. * File the error stream of the process is redirected to.
  424. *
  425. * @param error file getting the error stream
  426. *
  427. * @since ant 1.6
  428. */
  429. public void setError(File error) {
  430. redirector.setError(error);
  431. incompatibleWithSpawn = true;
  432. }
  433. /**
  434. * Property name whose value should be set to the output of
  435. * the process.
  436. *
  437. * @param outputProp property name
  438. *
  439. */
  440. public void setOutputproperty(String outputProp) {
  441. redirector.setOutputProperty(outputProp);
  442. incompatibleWithSpawn = true;
  443. }
  444. /**
  445. * Property name whose value should be set to the error of
  446. * the process.
  447. *
  448. * @param errorProperty property name
  449. *
  450. * @since ant 1.6
  451. */
  452. public void setErrorProperty(String errorProperty) {
  453. redirector.setErrorProperty(errorProperty);
  454. incompatibleWithSpawn = true;
  455. }
  456. /**
  457. * Corresponds to -mx or -Xmx depending on VM version.
  458. *
  459. * @param max max memory parameter
  460. */
  461. public void setMaxmemory(String max) {
  462. cmdl.setMaxmemory(max);
  463. }
  464. /**
  465. * Sets the JVM version.
  466. * @param value JVM version
  467. */
  468. public void setJVMVersion(String value) {
  469. cmdl.setVmversion(value);
  470. }
  471. /**
  472. * Adds an environment variable.
  473. *
  474. * <p>Will be ignored if we are not forking a new VM.
  475. *
  476. * @param var new environment variable
  477. *
  478. * @since Ant 1.5
  479. */
  480. public void addEnv(Environment.Variable var) {
  481. env.addVariable(var);
  482. }
  483. /**
  484. * If true, use a completely new environment.
  485. *
  486. * <p>Will be ignored if we are not forking a new VM.
  487. *
  488. * @param newenv if true, use a completely new environment.
  489. *
  490. * @since Ant 1.5
  491. */
  492. public void setNewenvironment(boolean newenv) {
  493. newEnvironment = newenv;
  494. }
  495. /**
  496. * If true, append output to existing file.
  497. *
  498. * @param append if true, append output to existing file
  499. *
  500. * @since Ant 1.5
  501. */
  502. public void setAppend(boolean append) {
  503. redirector.setAppend(append);
  504. incompatibleWithSpawn = true;
  505. }
  506. /**
  507. * Timeout in milliseconds after which the process will be killed.
  508. *
  509. * @param value time out in milliseconds
  510. *
  511. * @since Ant 1.5
  512. */
  513. public void setTimeout(Long value) {
  514. timeout = value;
  515. incompatibleWithSpawn = true;
  516. }
  517. /**
  518. * assertions to enable in this program (if fork=true)
  519. * @since Ant 1.6
  520. * @param asserts assertion set
  521. */
  522. public void addAssertions(Assertions asserts) {
  523. if(cmdl.getAssertions() != null) {
  524. throw new BuildException("Only one assertion declaration is allowed");
  525. }
  526. cmdl.setAssertions(asserts);
  527. }
  528. /**
  529. * Pass output sent to System.out to specified output file.
  530. *
  531. * @param output a string of output on its way to the handlers
  532. *
  533. * @since Ant 1.5
  534. */
  535. protected void handleOutput(String output) {
  536. if (redirector.getOutputStream() != null) {
  537. redirector.handleOutput(output);
  538. } else {
  539. super.handleOutput(output);
  540. }
  541. }
  542. /**
  543. * Handle an input request by this task
  544. *
  545. * @param buffer the buffer into which data is to be read.
  546. * @param offset the offset into the buffer at which data is stored.
  547. * @param length the amount of data to read
  548. *
  549. * @return the number of bytes read
  550. *
  551. * @exception IOException if the data cannot be read
  552. * @since Ant 1.6
  553. */
  554. public int handleInput(byte[] buffer, int offset, int length)
  555. throws IOException {
  556. if (redirector.getInputStream() != null) {
  557. return redirector.handleInput(buffer, offset, length);
  558. } else {
  559. return super.handleInput(buffer, offset, length);
  560. }
  561. }
  562. /**
  563. * Pass output sent to System.out to specified output file.
  564. *
  565. * @param output string of output on its way to its handlers
  566. *
  567. * @since Ant 1.5.2
  568. */
  569. protected void handleFlush(String output) {
  570. if (redirector.getOutputStream() != null) {
  571. redirector.handleFlush(output);
  572. } else {
  573. super.handleFlush(output);
  574. }
  575. }
  576. /**
  577. * Handle output sent to System.err
  578. *
  579. * @param output string of stderr
  580. *
  581. * @since Ant 1.5
  582. */
  583. protected void handleErrorOutput(String output) {
  584. if (redirector.getErrorStream() != null) {
  585. redirector.handleErrorOutput(output);
  586. } else {
  587. super.handleErrorOutput(output);
  588. }
  589. }
  590. /**
  591. * Handle output sent to System.err and flush the stream.
  592. *
  593. * @param output string of stderr
  594. *
  595. * @since Ant 1.5.2
  596. */
  597. protected void handleErrorFlush(String output) {
  598. if (redirector.getErrorStream() != null) {
  599. redirector.handleErrorFlush(output);
  600. } else {
  601. super.handleErrorOutput(output);
  602. }
  603. }
  604. /**
  605. * Executes the given classname with the given arguments as it
  606. * was a command line application.
  607. */
  608. private void run(CommandlineJava command) throws BuildException {
  609. try {
  610. ExecuteJava exe = new ExecuteJava();
  611. exe.setJavaCommand(command.getJavaCommand());
  612. exe.setClasspath(command.getClasspath());
  613. exe.setSystemProperties(command.getSystemProperties());
  614. exe.setPermissions(perm);
  615. exe.setTimeout(timeout);
  616. redirector.createStreams();
  617. exe.execute(getProject());
  618. redirector.complete();
  619. if (exe.killedProcess()) {
  620. throw new BuildException("Timeout: killed the sub-process");
  621. }
  622. } catch (IOException e) {
  623. throw new BuildException(e);
  624. }
  625. }
  626. /**
  627. * Executes the given classname with the given arguments in a separate VM.
  628. */
  629. private int fork(String[] command) throws BuildException {
  630. Execute exe
  631. = new Execute(redirector.createHandler(), createWatchdog());
  632. exe.setAntRun(getProject());
  633. if (dir == null) {
  634. dir = getProject().getBaseDir();
  635. } else if (!dir.exists() || !dir.isDirectory()) {
  636. throw new BuildException(dir.getAbsolutePath()
  637. + " is not a valid directory",
  638. getLocation());
  639. }
  640. exe.setWorkingDirectory(dir);
  641. String[] environment = env.getVariables();
  642. if (environment != null) {
  643. for (int i = 0; i < environment.length; i++) {
  644. log("Setting environment variable: " + environment[i],
  645. Project.MSG_VERBOSE);
  646. }
  647. }
  648. exe.setNewenvironment(newEnvironment);
  649. exe.setEnvironment(environment);
  650. exe.setCommandline(command);
  651. try {
  652. int rc = exe.execute();
  653. redirector.complete();
  654. if (exe.killedProcess()) {
  655. throw new BuildException("Timeout: killed the sub-process");
  656. }
  657. return rc;
  658. } catch (IOException e) {
  659. throw new BuildException(e, getLocation());
  660. }
  661. }
  662. /**
  663. * Executes the given classname with the given arguments in a separate VM.
  664. */
  665. private void spawn(String[] command) throws BuildException {
  666. Execute exe
  667. = new Execute();
  668. exe.setAntRun(getProject());
  669. if (dir == null) {
  670. dir = getProject().getBaseDir();
  671. } else if (!dir.exists() || !dir.isDirectory()) {
  672. throw new BuildException(dir.getAbsolutePath()
  673. + " is not a valid directory",
  674. getLocation());
  675. }
  676. exe.setWorkingDirectory(dir);
  677. String[] environment = env.getVariables();
  678. if (environment != null) {
  679. for (int i = 0; i < environment.length; i++) {
  680. log("Setting environment variable: " + environment[i],
  681. Project.MSG_VERBOSE);
  682. }
  683. }
  684. exe.setNewenvironment(newEnvironment);
  685. exe.setEnvironment(environment);
  686. exe.setCommandline(command);
  687. try {
  688. exe.spawn();
  689. } catch (IOException e) {
  690. throw new BuildException(e, getLocation());
  691. }
  692. }
  693. /**
  694. * Executes the given classname with the given arguments as it
  695. * was a command line application.
  696. *
  697. * @param classname the name of the class to run
  698. * @param args arguments for the class
  699. * @throws BuildException in case of IO Exception in the execution
  700. */
  701. protected void run(String classname, Vector args) throws BuildException {
  702. CommandlineJava cmdj = new CommandlineJava();
  703. cmdj.setClassname(classname);
  704. for (int i = 0; i < args.size(); i++) {
  705. cmdj.createArgument().setValue((String) args.elementAt(i));
  706. }
  707. run(cmdj);
  708. }
  709. /**
  710. * Clear out the arguments to this java task.
  711. */
  712. public void clearArgs() {
  713. cmdl.clearJavaArgs();
  714. }
  715. /**
  716. * Create the Watchdog to kill a runaway process.
  717. *
  718. * @return new watchdog
  719. *
  720. * @throws BuildException under unknown circumstances
  721. *
  722. * @since Ant 1.5
  723. */
  724. protected ExecuteWatchdog createWatchdog() throws BuildException {
  725. if (timeout == null) {
  726. return null;
  727. }
  728. return new ExecuteWatchdog(timeout.longValue());
  729. }
  730. /**
  731. * @since 1.6.2
  732. */
  733. private void log(Throwable t) {
  734. StringWriter sw = new StringWriter();
  735. PrintWriter w = new PrintWriter(sw);
  736. t.printStackTrace(w);
  737. w.close();
  738. log(sw.toString(), Project.MSG_ERR);
  739. }
  740. }