001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs.optional.jsp;
020:
021: import java.io.File;
022: import java.util.Date;
023: import java.util.Enumeration;
024: import java.util.Vector;
025: import org.apache.tools.ant.BuildException;
026: import org.apache.tools.ant.DirectoryScanner;
027: import org.apache.tools.ant.Project;
028: import org.apache.tools.ant.taskdefs.MatchingTask;
029: import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapter;
030: import org.apache.tools.ant.taskdefs.optional.jsp.compilers.JspCompilerAdapterFactory;
031: import org.apache.tools.ant.types.Path;
032: import org.apache.tools.ant.types.Reference;
033:
034: /**
035: * Runs a JSP compiler.
036: *
037: * <p> This task takes the given jsp files and compiles them into java
038: * files. It is then up to the user to compile the java files into classes.
039: *
040: * <p> The task requires the srcdir and destdir attributes to be
041: * set. This Task is a MatchingTask, so the files to be compiled can be
042: * specified using includes/excludes attributes or nested include/exclude
043: * elements. Optional attributes are verbose (set the verbosity level passed
044: * to jasper), package (name of the destination package for generated java
045: * classes and classpath (the classpath to use when running the jsp
046: * compiler).
047: * <p> This task supports the nested elements classpath (A Path) and
048: * classpathref (A Reference) which can be used in preference to the
049: * attribute classpath, if the jsp compiler is not already in the ant
050: * classpath.
051: *
052: * <p><h4>Usage</h4>
053: * <pre>
054: * <jspc srcdir="${basedir}/src/war"
055: * destdir="${basedir}/gensrc"
056: * package="com.i3sp.jsp"
057: * verbose="9">
058: * <include name="**\/*.jsp" />
059: * </jspc>
060: * </pre>
061: *
062: * <p> Large Amount of cutting and pasting from the Javac task...
063: * @since 1.5
064: */
065: public class JspC extends MatchingTask {
066: private Path classpath;
067: private Path compilerClasspath;
068: private Path src;
069: private File destDir;
070: private String packageName;
071: /** name of the compiler to use */
072: private String compilerName = "jasper";
073:
074: /**
075: * -ieplugin <clsid> Java Plugin classid for Internet Explorer
076: */
077: private String iepluginid;
078: private boolean mapped;
079: private int verbose = 0;
080: // CheckStyle:VisibilityModifier OFF - bc
081: protected Vector compileList = new Vector();
082: Vector javaFiles = new Vector();
083:
084: /**
085: * flag to control action on execution trouble
086: */
087: protected boolean failOnError = true;
088:
089: /**
090: * -uriroot <dir> The root directory that uri files should be resolved
091: * against,
092: */
093: private File uriroot;
094:
095: /**
096: * -webinc <file> Creates partial servlet mappings for the -webapp option
097: */
098: private File webinc;
099:
100: /**
101: * -webxml <file> Creates a complete web.xml when using the -webapp option.
102: */
103:
104: private File webxml;
105:
106: /**
107: * web apps
108: */
109: protected WebAppParameter webApp;
110:
111: private static final String FAIL_MSG = "Compile failed, messages should have been provided.";
112:
113: // CheckStyle:VisibilityModifier ON
114:
115: /**
116: * Set the path for source JSP files.
117: * @param srcDir the source path.
118: */
119: public void setSrcDir(Path srcDir) {
120: if (src == null) {
121: src = srcDir;
122: } else {
123: src.append(srcDir);
124: }
125: }
126:
127: /**
128: * Get the source dir.
129: * @return the source path.
130: */
131: public Path getSrcDir() {
132: return src;
133: }
134:
135: /**
136: * Set the destination directory into which the JSP source
137: * files should be compiled.
138: * @param destDir the destination directory.
139: */
140: public void setDestdir(File destDir) {
141: this .destDir = destDir;
142: }
143:
144: /**
145: * Get the destination directory.
146: * @return the directory.
147: */
148: public File getDestdir() {
149: return destDir;
150: }
151:
152: /**
153: * Set the name of the package the compiled jsp files should be in.
154: * @param pkg the name of the package.
155: */
156: public void setPackage(String pkg) {
157: this .packageName = pkg;
158: }
159:
160: /**
161: * Get the name of the package.
162: * @return the package.
163: */
164: public String getPackage() {
165: return packageName;
166: }
167:
168: /**
169: * Set the verbose level of the compiler
170: * @param i the verbose level to use.
171: */
172: public void setVerbose(int i) {
173: verbose = i;
174: }
175:
176: /**
177: * Get the verbose level.
178: * @return the level.
179: */
180: public int getVerbose() {
181: return verbose;
182: }
183:
184: /**
185: * Whether or not the build should halt if compilation fails.
186: * Defaults to <code>true</code>.
187: * @param fail a <code>boolean</code> value.
188: */
189: public void setFailonerror(boolean fail) {
190: failOnError = fail;
191: }
192:
193: /**
194: * Gets the failonerror flag.
195: * @return the flag.
196: */
197: public boolean getFailonerror() {
198: return failOnError;
199: }
200:
201: /**
202: * Get the IE CLASSID value.
203: * @return the value.
204: */
205: public String getIeplugin() {
206: return iepluginid;
207: }
208:
209: /**
210: * Java Plugin CLASSID for Internet Explorer
211: * @param iepluginid the id to use.
212: */
213: public void setIeplugin(String iepluginid) {
214: this .iepluginid = iepluginid;
215: }
216:
217: /**
218: * If true, generate separate write() calls for each HTML line
219: * in the JSP.
220: * @return mapping status
221: */
222: public boolean isMapped() {
223: return mapped;
224: }
225:
226: /**
227: * If true, generate separate write() calls for each HTML line
228: * in the JSP.
229: * @param mapped a <code>boolean</code> value.
230: */
231: public void setMapped(boolean mapped) {
232: this .mapped = mapped;
233: }
234:
235: /**
236: * The URI context of relative URI references in the JSP pages.
237: * If it does not exist then it is derived from the location
238: * of the file relative to the declared or derived value of uriroot.
239: *
240: * @param uribase The new Uribase value
241: */
242: public void setUribase(File uribase) {
243: log("Uribase is currently an unused parameter",
244: Project.MSG_WARN);
245: }
246:
247: /**
248: * Get the uri base value.
249: * @return the value.
250: */
251: public File getUribase() {
252: return uriroot;
253: }
254:
255: /**
256: * The root directory that uri files should be resolved
257: * against. (Default is the directory jspc is invoked from)
258: *
259: * @param uriroot The new Uribase value
260: */
261: public void setUriroot(File uriroot) {
262: this .uriroot = uriroot;
263: }
264:
265: /**
266: * Get the uri root value.
267: * @return the value.
268: */
269: public File getUriroot() {
270: return uriroot;
271: }
272:
273: /**
274: * Set the classpath to be used for this compilation.
275: * @param cp the path to be used.
276: */
277: public void setClasspath(Path cp) {
278: if (classpath == null) {
279: classpath = cp;
280: } else {
281: classpath.append(cp);
282: }
283: }
284:
285: /**
286: * Adds a path to the classpath.
287: * @return a path to be configured.
288: */
289: public Path createClasspath() {
290: if (classpath == null) {
291: classpath = new Path(getProject());
292: }
293: return classpath.createPath();
294: }
295:
296: /**
297: * Adds a reference to a classpath defined elsewhere
298: * @param r a reference to a classpath.
299: */
300: public void setClasspathRef(Reference r) {
301: createClasspath().setRefid(r);
302: }
303:
304: /**
305: * Get the classpath.
306: * @return the classpath.
307: */
308: public Path getClasspath() {
309: return classpath;
310: }
311:
312: /**
313: * Set the classpath to be used to find this compiler adapter
314: * @param cp the compiler classpath.
315: */
316: public void setCompilerclasspath(Path cp) {
317: if (compilerClasspath == null) {
318: compilerClasspath = cp;
319: } else {
320: compilerClasspath.append(cp);
321: }
322: }
323:
324: /**
325: * get the classpath used to find the compiler adapter
326: * @return the compiler classpath.
327: */
328: public Path getCompilerclasspath() {
329: return compilerClasspath;
330: }
331:
332: /**
333: * Support nested compiler classpath, used to locate compiler adapter
334: * @return a path to be configured.
335: */
336: public Path createCompilerclasspath() {
337: if (compilerClasspath == null) {
338: compilerClasspath = new Path(getProject());
339: }
340: return compilerClasspath.createPath();
341: }
342:
343: /**
344: * Filename for web.xml.
345: *
346: * @param webxml The new Webxml value
347: */
348: public void setWebxml(File webxml) {
349: this .webxml = webxml;
350: }
351:
352: /**
353: * Filename for web.xml.
354: * @return The filename for web.xml.
355: */
356: public File getWebxml() {
357: return this .webxml;
358: }
359:
360: /**
361: * output filename for the fraction of web.xml that lists
362: * servlets.
363: * @param webinc The new Webinc value
364: */
365: public void setWebinc(File webinc) {
366: this .webinc = webinc;
367: }
368:
369: /**
370: * Get the webinc attribute.
371: * @return the webinc attribute.
372: */
373: public File getWebinc() {
374: return this .webinc;
375: }
376:
377: /**
378: * Adds a single webapp.
379: *
380: * @param webappParam add a web app parameter
381: * @throws BuildException if more than one webapp is specified.
382: */
383: public void addWebApp(WebAppParameter webappParam)
384: throws BuildException {
385: //demand create vector of filesets
386: if (webApp == null) {
387: webApp = webappParam;
388: } else {
389: throw new BuildException("Only one webapp can be specified");
390: }
391: }
392:
393: /**
394: * Get the web app.
395: * @return the web app attribute.
396: */
397: public WebAppParameter getWebApp() {
398: return webApp;
399: }
400:
401: /**
402: * Class name of a JSP compiler adapter.
403: * @param compiler the compiler class name.
404: */
405: public void setCompiler(String compiler) {
406: this .compilerName = compiler;
407: }
408:
409: /**
410: * get the list of files to compile
411: * @return the list of files.
412: */
413: public Vector getCompileList() {
414: return compileList;
415: }
416:
417: /**
418: * execute by building up a list of files that
419: * have changed and hand them off to a jsp compiler
420: * @throws BuildException on error.
421: */
422: public void execute() throws BuildException {
423:
424: // make sure that we've got a destdir
425: if (destDir == null) {
426: throw new BuildException("destdir attribute must be set!",
427: getLocation());
428: }
429:
430: if (!destDir.isDirectory()) {
431: throw new BuildException("destination directory \""
432: + destDir
433: + "\" does not exist or is not a directory",
434: getLocation());
435: }
436:
437: File dest = getActualDestDir();
438:
439: //bind to a compiler
440: JspCompilerAdapter compiler = JspCompilerAdapterFactory
441: .getCompiler(compilerName, this , getProject()
442: .createClassLoader(compilerClasspath));
443:
444: //if we are a webapp, hand off to the compiler, which had better handle it
445: if (webApp != null) {
446: doCompilation(compiler);
447: return;
448: }
449:
450: // make sure that we've got a srcdir
451: if (src == null) {
452: throw new BuildException("srcdir attribute must be set!",
453: getLocation());
454: }
455: String[] list = src.list();
456: if (list.length == 0) {
457: throw new BuildException("srcdir attribute must be set!",
458: getLocation());
459: }
460:
461: // if the compiler does its own dependency stuff, we just call it right now
462: if (compiler.implements OwnDependencyChecking()) {
463: doCompilation(compiler);
464: return;
465: }
466:
467: //the remainder of this method is only for compilers that need their dependency work done
468: JspMangler mangler = compiler.createMangler();
469:
470: // scan source directories and dest directory to build up both copy
471: // lists and compile lists
472: resetFileLists();
473: int filecount = 0;
474: for (int i = 0; i < list.length; i++) {
475: File srcDir = getProject().resolveFile(list[i]);
476: if (!srcDir.exists()) {
477: throw new BuildException("srcdir \"" + srcDir.getPath()
478: + "\" does not exist!", getLocation());
479: }
480: DirectoryScanner ds = this .getDirectoryScanner(srcDir);
481: String[] files = ds.getIncludedFiles();
482: filecount = files.length;
483: scanDir(srcDir, dest, mangler, files);
484: }
485:
486: // compile the source files
487:
488: log("compiling " + compileList.size() + " files",
489: Project.MSG_VERBOSE);
490:
491: if (compileList.size() > 0) {
492:
493: log("Compiling " + compileList.size() + " source file"
494: + (compileList.size() == 1 ? "" : "s") + " to "
495: + dest);
496: doCompilation(compiler);
497:
498: } else {
499: if (filecount == 0) {
500: log("there were no files to compile", Project.MSG_INFO);
501: } else {
502: log("all files are up to date", Project.MSG_VERBOSE);
503: }
504: }
505: }
506:
507: /**
508: * calculate where the files will end up:
509: * this is destDir or it id destDir + the package name
510: */
511: private File getActualDestDir() {
512: File dest = null;
513: if (packageName == null) {
514: dest = destDir;
515: } else {
516: String path = destDir.getPath() + File.separatorChar
517: + packageName.replace('.', File.separatorChar);
518: dest = new File(path);
519: }
520: return dest;
521: }
522:
523: /**
524: * do the compile
525: */
526: private void doCompilation(JspCompilerAdapter compiler)
527: throws BuildException {
528: // now we need to populate the compiler adapter
529: compiler.setJspc(this );
530:
531: // finally, lets execute the compiler!!
532: if (!compiler.execute()) {
533: if (failOnError) {
534: throw new BuildException(FAIL_MSG, getLocation());
535: } else {
536: log(FAIL_MSG, Project.MSG_ERR);
537: }
538: }
539: }
540:
541: /**
542: * Clear the list of files to be compiled and copied..
543: */
544: protected void resetFileLists() {
545: compileList.removeAllElements();
546: }
547:
548: /**
549: * Scans the directory looking for source files to be compiled.
550: * The results are returned in the class variable compileList
551: * @param srcDir the source directory.
552: * @param dest the destination directory.
553: * @param mangler the jsp filename mangler.
554: * @param files the file names to mangle.
555: */
556: protected void scanDir(File srcDir, File dest, JspMangler mangler,
557: String[] files) {
558:
559: long now = (new Date()).getTime();
560:
561: for (int i = 0; i < files.length; i++) {
562: String filename = files[i];
563: File srcFile = new File(srcDir, filename);
564: File javaFile = mapToJavaFile(mangler, srcFile, srcDir,
565: dest);
566: if (javaFile == null) {
567: continue;
568: }
569:
570: if (srcFile.lastModified() > now) {
571: log(
572: "Warning: file modified in the future: "
573: + filename, Project.MSG_WARN);
574: }
575: boolean shouldCompile = false;
576: shouldCompile = isCompileNeeded(srcFile, javaFile);
577: if (shouldCompile) {
578: compileList.addElement(srcFile.getAbsolutePath());
579: javaFiles.addElement(javaFile);
580: }
581: }
582: }
583:
584: /**
585: * Test whether or not compilation is needed. A return value of
586: * <code>true<code> means yes, <code>false</code> means
587: * our tests do not indicate this, but as the TLDs are
588: * not used for dependency checking this is not guaranteed.
589: * The current tests are
590: * <ol>
591: * <li>no dest file
592: * <li>dest file out of date w.r.t source
593: * <li>dest file zero bytes long
594: * </ol>
595: * @param srcFile JSP source file
596: * @param javaFile JSP dest file
597: * @return true if a compile is definately needed.
598: *
599: */
600: private boolean isCompileNeeded(File srcFile, File javaFile) {
601: boolean shouldCompile = false;
602: if (!javaFile.exists()) {
603: shouldCompile = true;
604: log("Compiling " + srcFile.getPath()
605: + " because java file " + javaFile.getPath()
606: + " does not exist", Project.MSG_VERBOSE);
607: } else {
608: if (srcFile.lastModified() > javaFile.lastModified()) {
609: shouldCompile = true;
610: log("Compiling " + srcFile.getPath()
611: + " because it is out of date with respect to "
612: + javaFile.getPath(), Project.MSG_VERBOSE);
613: } else {
614: if (javaFile.length() == 0) {
615: shouldCompile = true;
616: log("Compiling " + srcFile.getPath()
617: + " because java file "
618: + javaFile.getPath() + " is empty",
619: Project.MSG_VERBOSE);
620: }
621: }
622: }
623: return shouldCompile;
624: }
625:
626: /**
627: * get a filename from our jsp file.
628: * @param mangler the jsp filename managler.
629: * @param srcFile the source file.
630: * @param srcDir the source directory.
631: * @param dest the destination directory.
632: * @return the filename.
633: * @todo support packages and subdirs
634: */
635: protected File mapToJavaFile(JspMangler mangler, File srcFile,
636: File srcDir, File dest) {
637: if (!srcFile.getName().endsWith(".jsp")) {
638: return null;
639: }
640: String javaFileName = mangler.mapJspToJavaName(srcFile);
641: // String srcFileDir=srcFile.getParent();
642: return new File(dest, javaFileName);
643: }
644:
645: /**
646: * delete any java output files that are empty
647: * this is to get around a little defect in jasper: when it
648: * fails, it leaves incomplete files around.
649: */
650: public void deleteEmptyJavaFiles() {
651: if (javaFiles != null) {
652: Enumeration e = javaFiles.elements();
653: while (e.hasMoreElements()) {
654: File file = (File) e.nextElement();
655: if (file.exists() && file.length() == 0) {
656: log("deleting empty output file " + file);
657: file.delete();
658: }
659: }
660: }
661: }
662:
663: /**
664: * static inner class used as a parameter element
665: */
666: public static class WebAppParameter {
667:
668: /**
669: * the sole option
670: */
671: private File directory;
672:
673: /**
674: * query current directory
675: * @return the directory.
676: */
677: public File getDirectory() {
678: return directory;
679: }
680:
681: /**
682: * set directory; alternate syntax
683: * @param directory the base dir.
684: */
685: public void setBaseDir(File directory) {
686: this .directory = directory;
687: }
688: //end inner class
689: }
690:
691: //end class
692: }
|