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.jsp;
  18. import java.io.File;
  19. import java.util.Date;
  20. import java.util.Enumeration;
  21. import java.util.Vector;
  22. import org.apache.tools.ant.BuildException;
  23. import org.apache.tools.ant.DirectoryScanner;
  24. import org.apache.tools.ant.Project;
  25. import org.apache.tools.ant.taskdefs.MatchingTask;
  26. import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapter;
  27. import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapterFactory;
  28. import org.apache.tools.ant.types.Path;
  29. import org.apache.tools.ant.types.Reference;
  30. /**
  31. * Runs a JSP compiler.
  32. *
  33. * <p> This task takes the given jsp files and compiles them into java
  34. * files. It is then up to the user to compile the java files into classes.
  35. *
  36. * <p> The task requires the srcdir and destdir attributes to be
  37. * set. This Task is a MatchingTask, so the files to be compiled can be
  38. * specified using includes/excludes attributes or nested include/exclude
  39. * elements. Optional attributes are verbose (set the verbosity level passed
  40. * to jasper), package (name of the destination package for generated java
  41. * classes and classpath (the classpath to use when running the jsp
  42. * compiler).
  43. * <p> This task supports the nested elements classpath (A Path) and
  44. * classpathref (A Reference) which can be used in preference to the
  45. * attribute classpath, if the jsp compiler is not already in the ant
  46. * classpath.
  47. *
  48. * <p><h4>Usage</h4>
  49. * <pre>
  50. * <jspc srcdir="${basedir}/src/war"
  51. * destdir="${basedir}/gensrc"
  52. * package="com.i3sp.jsp"
  53. * verbose="9">
  54. * <include name="**\/*.jsp" />
  55. * </jspc>
  56. * </pre>
  57. *
  58. * @since 1.5
  59. */
  60. public class JspC extends MatchingTask {
  61. private Path classpath;
  62. private Path compilerClasspath;
  63. private Path src;
  64. private File destDir;
  65. private String packageName ;
  66. /** name of the compiler to use */
  67. private String compilerName = "jasper";
  68. /**
  69. * -ieplugin <clsid> Java Plugin classid for Internet Explorer
  70. */
  71. private String iepluginid ;
  72. private boolean mapped ;
  73. private int verbose = 0;
  74. protected Vector compileList = new Vector();
  75. Vector javaFiles = new Vector();
  76. /**
  77. * flag to control action on execution trouble
  78. */
  79. protected boolean failOnError = true;
  80. /**
  81. * -uriroot <dir> The root directory that uri files should be resolved
  82. * against,
  83. */
  84. private File uriroot;
  85. /**
  86. * -webinc <file> Creates partial servlet mappings for the -webapp option
  87. */
  88. private File webinc;
  89. /**
  90. * -webxml <file> Creates a complete web.xml when using the -webapp option.
  91. */
  92. private File webxml;
  93. /**
  94. * web apps
  95. */
  96. protected WebAppParameter webApp;
  97. private static final String FAIL_MSG
  98. = "Compile failed, messages should have been provided.";
  99. /**
  100. * Set the path for source JSP files.
  101. */
  102. public void setSrcDir(Path srcDir) {
  103. if (src == null) {
  104. src = srcDir;
  105. } else {
  106. src.append(srcDir);
  107. }
  108. }
  109. public Path getSrcDir() {
  110. return src;
  111. }
  112. /**
  113. * Set the destination directory into which the JSP source
  114. * files should be compiled.
  115. */
  116. public void setDestdir(File destDir) {
  117. this.destDir = destDir;
  118. }
  119. public File getDestdir() {
  120. return destDir;
  121. }
  122. /**
  123. * Set the name of the package the compiled jsp files should be in.
  124. */
  125. public void setPackage(String pkg) {
  126. this.packageName = pkg;
  127. }
  128. public String getPackage() {
  129. return packageName;
  130. }
  131. /**
  132. * Set the verbose level of the compiler
  133. */
  134. public void setVerbose(int i) {
  135. verbose = i;
  136. }
  137. public int getVerbose() {
  138. return verbose;
  139. }
  140. /**
  141. * Whether or not the build should halt if compilation fails.
  142. * Defaults to <code>true</code>.
  143. */
  144. public void setFailonerror(boolean fail) {
  145. failOnError = fail;
  146. }
  147. /**
  148. * Gets the failonerror flag.
  149. */
  150. public boolean getFailonerror() {
  151. return failOnError;
  152. }
  153. public String getIeplugin() {
  154. return iepluginid;
  155. }
  156. /**
  157. * Java Plugin CLASSID for Internet Explorer
  158. */
  159. public void setIeplugin(String iepluginid) {
  160. this.iepluginid = iepluginid;
  161. }
  162. /**
  163. * If true, generate separate write() calls for each HTML line
  164. * in the JSP.
  165. * @return mapping status
  166. */
  167. public boolean isMapped() {
  168. return mapped;
  169. }
  170. /**
  171. * If true, generate separate write() calls for each HTML line
  172. * in the JSP.
  173. */
  174. public void setMapped(boolean mapped) {
  175. this.mapped = mapped;
  176. }
  177. /**
  178. * The URI context of relative URI references in the JSP pages.
  179. * If it does not exist then it is derived from the location
  180. * of the file relative to the declared or derived value of uriroot.
  181. *
  182. * @param uribase The new Uribase value
  183. */
  184. public void setUribase(File uribase) {
  185. log("Uribase is currently an unused parameter", Project.MSG_WARN);
  186. }
  187. public File getUribase() {
  188. return uriroot;
  189. }
  190. /**
  191. * The root directory that uri files should be resolved
  192. * against. (Default is the directory jspc is invoked from)
  193. *
  194. * @param uriroot The new Uribase value
  195. */
  196. public void setUriroot(File uriroot) {
  197. this.uriroot = uriroot;
  198. }
  199. public File getUriroot() {
  200. return uriroot;
  201. }
  202. /**
  203. * Set the classpath to be used for this compilation.
  204. */
  205. public void setClasspath(Path cp) {
  206. if (classpath == null) {
  207. classpath = cp;
  208. } else {
  209. classpath.append(cp);
  210. }
  211. }
  212. /**
  213. * Adds a path to the classpath.
  214. */
  215. public Path createClasspath() {
  216. if (classpath == null) {
  217. classpath = new Path(getProject());
  218. }
  219. return classpath.createPath();
  220. }
  221. /**
  222. * Adds a reference to a classpath defined elsewhere
  223. */
  224. public void setClasspathRef(Reference r) {
  225. createClasspath().setRefid(r);
  226. }
  227. public Path getClasspath() {
  228. return classpath;
  229. }
  230. /**
  231. * Set the classpath to be used to find this compiler adapter
  232. */
  233. public void setCompilerclasspath(Path cp) {
  234. if (compilerClasspath == null) {
  235. compilerClasspath = cp;
  236. } else {
  237. compilerClasspath.append(cp);
  238. }
  239. }
  240. /**
  241. * get the classpath used to find the compiler adapter
  242. */
  243. public Path getCompilerclasspath() {
  244. return compilerClasspath;
  245. }
  246. /**
  247. * Support nested compiler classpath, used to locate compiler adapter
  248. */
  249. public Path createCompilerclasspath() {
  250. if (compilerClasspath == null) {
  251. compilerClasspath = new Path(getProject());
  252. }
  253. return compilerClasspath.createPath();
  254. }
  255. /**
  256. * Filename for web.xml.
  257. *
  258. * @param webxml The new Webxml value
  259. */
  260. public void setWebxml(File webxml) {
  261. this.webxml = webxml;
  262. }
  263. /**
  264. * Filename for web.xml.
  265. * @return The filename for web.xml.
  266. */
  267. public File getWebxml() {
  268. return this.webxml;
  269. }
  270. /**
  271. * output filename for the fraction of web.xml that lists
  272. * servlets.
  273. * @param webinc The new Webinc value
  274. */
  275. public void setWebinc(File webinc) {
  276. this.webinc = webinc;
  277. }
  278. public File getWebinc() {
  279. return this.webinc;
  280. }
  281. /**
  282. * Adds a single webapp.
  283. *
  284. * @param webappParam add a web app parameter
  285. */
  286. public void addWebApp(WebAppParameter webappParam)
  287. throws BuildException {
  288. //demand create vector of filesets
  289. if (webApp == null) {
  290. webApp = webappParam;
  291. } else {
  292. throw new BuildException("Only one webapp can be specified");
  293. }
  294. }
  295. public WebAppParameter getWebApp() {
  296. return webApp;
  297. }
  298. /**
  299. * Class name of a JSP compiler adapter.
  300. */
  301. public void setCompiler(String compiler) {
  302. this.compilerName = compiler;
  303. }
  304. /**
  305. * get the list of files to compile
  306. */
  307. public Vector getCompileList() {
  308. return compileList;
  309. }
  310. /**
  311. * execute by building up a list of files that
  312. * have changed and hand them off to a jsp compiler
  313. */
  314. public void execute()
  315. throws BuildException {
  316. // make sure that we've got a destdir
  317. if (destDir == null) {
  318. throw new BuildException("destdir attribute must be set!",
  319. getLocation());
  320. }
  321. if (!destDir.isDirectory()) {
  322. throw new BuildException("destination directory \"" + destDir
  323. + "\" does not exist or is not a directory", getLocation());
  324. }
  325. File dest = getActualDestDir();
  326. //bind to a compiler
  327. JspCompilerAdapter compiler =
  328. JspCompilerAdapterFactory.getCompiler(compilerName, this,
  329. getProject().createClassLoader(compilerClasspath));
  330. //if we are a webapp, hand off to the compiler, which had better handle it
  331. if (webApp != null) {
  332. doCompilation(compiler);
  333. return;
  334. }
  335. // make sure that we've got a srcdir
  336. if (src == null) {
  337. throw new BuildException("srcdir attribute must be set!",
  338. getLocation());
  339. }
  340. String [] list = src.list();
  341. if (list.length == 0) {
  342. throw new BuildException("srcdir attribute must be set!",
  343. getLocation());
  344. }
  345. // if the compiler does its own dependency stuff, we just call it right now
  346. if (compiler.implementsOwnDependencyChecking()) {
  347. doCompilation(compiler);
  348. return;
  349. }
  350. //the remainder of this method is only for compilers that need their dependency work done
  351. JspMangler mangler = compiler.createMangler();
  352. // scan source directories and dest directory to build up both copy
  353. // lists and compile lists
  354. resetFileLists();
  355. int filecount = 0;
  356. for (int i = 0; i < list.length; i++) {
  357. File srcDir = getProject().resolveFile(list[i]);
  358. if (!srcDir.exists()) {
  359. throw new BuildException("srcdir \"" + srcDir.getPath()
  360. + "\" does not exist!", getLocation());
  361. }
  362. DirectoryScanner ds = this.getDirectoryScanner(srcDir);
  363. String[] files = ds.getIncludedFiles();
  364. filecount = files.length;
  365. scanDir(srcDir, dest, mangler, files);
  366. }
  367. // compile the source files
  368. log("compiling " + compileList.size() + " files", Project.MSG_VERBOSE);
  369. if (compileList.size() > 0) {
  370. log("Compiling " + compileList.size() + " source file"
  371. + (compileList.size() == 1 ? "" : "s")
  372. + " to "
  373. + dest);
  374. doCompilation(compiler);
  375. } else {
  376. if (filecount == 0) {
  377. log("there were no files to compile", Project.MSG_INFO);
  378. } else {
  379. log("all files are up to date", Project.MSG_VERBOSE);
  380. }
  381. }
  382. }
  383. /**
  384. * calculate where the files will end up:
  385. * this is destDir or it id destDir + the package name
  386. */
  387. private File getActualDestDir() {
  388. File dest = null;
  389. if (packageName == null) {
  390. dest = destDir;
  391. } else {
  392. String path = destDir.getPath() + File.separatorChar
  393. + packageName.replace('.', File.separatorChar);
  394. dest = new File(path);
  395. }
  396. return dest;
  397. }
  398. /**
  399. * do the compile
  400. */
  401. private void doCompilation(JspCompilerAdapter compiler)
  402. throws BuildException {
  403. // now we need to populate the compiler adapter
  404. compiler.setJspc(this);
  405. // finally, lets execute the compiler!!
  406. if (!compiler.execute()) {
  407. if (failOnError) {
  408. throw new BuildException(FAIL_MSG, getLocation());
  409. } else {
  410. log(FAIL_MSG, Project.MSG_ERR);
  411. }
  412. }
  413. }
  414. /**
  415. * Clear the list of files to be compiled and copied..
  416. */
  417. protected void resetFileLists() {
  418. compileList.removeAllElements();
  419. }
  420. /**
  421. * Scans the directory looking for source files to be compiled.
  422. * The results are returned in the class variable compileList
  423. */
  424. protected void scanDir(File srcDir, File dest, JspMangler mangler, String files[]) {
  425. long now = (new Date()).getTime();
  426. for (int i = 0; i < files.length; i++) {
  427. String filename = files[i];
  428. File srcFile = new File(srcDir, filename);
  429. File javaFile = mapToJavaFile(mangler, srcFile, srcDir, dest);
  430. if (javaFile == null) {
  431. continue;
  432. }
  433. if (srcFile.lastModified() > now) {
  434. log("Warning: file modified in the future: " + filename,
  435. Project.MSG_WARN);
  436. }
  437. boolean shouldCompile = false;
  438. shouldCompile = isCompileNeeded(srcFile, javaFile);
  439. if (shouldCompile) {
  440. compileList.addElement(srcFile.getAbsolutePath());
  441. javaFiles.addElement(javaFile);
  442. }
  443. }
  444. }
  445. /**
  446. * Test whether or not compilation is needed. A return value of
  447. * <code>true<code> means yes, <code>false</code> means
  448. * our tests do not indicate this, but as the TLDs are
  449. * not used for dependency checking this is not guaranteed.
  450. * The current tests are
  451. * <ol>
  452. * <li>no dest file
  453. * <li>dest file out of date w.r.t source
  454. * <li>dest file zero bytes long
  455. * </ol>
  456. * @param srcFile JSP source file
  457. * @param javaFile JSP dest file
  458. * @return true if a compile is definately needed.
  459. *
  460. */
  461. private boolean isCompileNeeded(File srcFile, File javaFile) {
  462. boolean shouldCompile = false;
  463. if (!javaFile.exists()) {
  464. shouldCompile = true;
  465. log("Compiling " + srcFile.getPath()
  466. + " because java file " + javaFile.getPath()
  467. + " does not exist", Project.MSG_VERBOSE);
  468. } else {
  469. if (srcFile.lastModified() > javaFile.lastModified()) {
  470. shouldCompile = true;
  471. log("Compiling " + srcFile.getPath()
  472. + " because it is out of date with respect to "
  473. + javaFile.getPath(),
  474. Project.MSG_VERBOSE);
  475. } else {
  476. if (javaFile.length() == 0) {
  477. shouldCompile = true;
  478. log("Compiling " + srcFile.getPath()
  479. + " because java file " + javaFile.getPath()
  480. + " is empty", Project.MSG_VERBOSE);
  481. }
  482. }
  483. }
  484. return shouldCompile;
  485. }
  486. /**
  487. * get a filename from our jsp file
  488. * @todo support packages and subdirs
  489. */
  490. protected File mapToJavaFile(JspMangler mangler, File srcFile, File srcDir, File dest) {
  491. if (!srcFile.getName().endsWith(".jsp")) {
  492. return null;
  493. }
  494. String javaFileName = mangler.mapJspToJavaName(srcFile);
  495. // String srcFileDir=srcFile.getParent();
  496. return new File(dest, javaFileName);
  497. }
  498. /**
  499. * delete any java output files that are empty
  500. * this is to get around a little defect in jasper: when it
  501. * fails, it leaves incomplete files around.
  502. */
  503. public void deleteEmptyJavaFiles() {
  504. if (javaFiles != null) {
  505. Enumeration e = javaFiles.elements();
  506. while (e.hasMoreElements()) {
  507. File file = (File) e.nextElement();
  508. if (file.exists() && file.length() == 0) {
  509. log("deleting empty output file " + file);
  510. file.delete();
  511. }
  512. }
  513. }
  514. }
  515. /**
  516. * static inner class used as a parameter element
  517. */
  518. public static class WebAppParameter {
  519. /**
  520. * the sole option
  521. */
  522. private File directory;
  523. /**
  524. * query current directory
  525. */
  526. public File getDirectory() {
  527. return directory;
  528. }
  529. /**
  530. * set directory; alternate syntax
  531. */
  532. public void setBaseDir(File directory) {
  533. this.directory = directory;
  534. }
  535. //end inner class
  536. }
  537. //end class
  538. }