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: package org.apache.tools.ant.taskdefs.optional.jsp;
019:
020: //apache/ant imports
021: import java.io.File;
022: import java.util.Date;
023: import java.util.StringTokenizer;
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.Java;
029: import org.apache.tools.ant.taskdefs.MatchingTask;
030: import org.apache.tools.ant.types.Path;
031:
032: /**
033: * Precompiles JSP's using WebLogic's JSP compiler (weblogic.jspc).
034: *
035: * Tested only on Weblogic 4.5.1 - NT4.0 and Solaris 5.7
036: *
037: * required attributes
038: * src : root of source tree for JSP, ie, the document root for your weblogic server
039: * dest : root of destination directory, what you have set as
040: * WorkingDir in the weblogic properties
041: * package : start package name under which your JSP's would be compiled
042: *
043: * other attributes
044: * classpath
045: *
046: * A classpath should be set which contains the weblogic classes as well as all
047: * application classes referenced by the JSP. The system classpath is also
048: * appended when the jspc is called, so you may choose to put everything in
049: * the classpath while calling Ant. However, since presumably the JSP's will
050: * reference classes being build by Ant, it would be better to explicitly add
051: * the classpath in the task
052: *
053: * The task checks timestamps on the JSP's and the generated classes, and compiles
054: * only those files that have changed.
055: *
056: * It follows the weblogic naming convention of putting classes in
057: * <b> _dirName/_fileName.class for dirname/fileName.jsp </b>
058: *
059: * Limitation: It compiles the files thru the Classic compiler only.
060: * Limitation: Since it is my experience that weblogic jspc throws out of
061: * memory error on being given too many files at one go, it is
062: * called multiple times with one jsp file each.
063: *
064: * <pre>
065: * example
066: * <target name="jspcompile" depends="compile">
067: * <wljspc src="c:\\weblogic\\myserver\\public_html"
068: * dest="c:\\weblogic\\myserver\\serverclasses" package="myapp.jsp">
069: * <classpath>
070: * <pathelement location="${weblogic.classpath}" />
071: * <pathelement path="${compile.dest}" />
072: * </classpath>
073: *
074: * </wljspc>
075: * </target>
076: * </pre>
077: *
078: */
079:
080: public class WLJspc extends MatchingTask {
081: //TODO Test on other versions of weblogic
082: //TODO add more attributes to the task, to take care of all jspc options
083: //TODO Test on Unix
084:
085: /** root of compiled files tree */
086: private File destinationDirectory;
087:
088: /** root of source files tree */
089: private File sourceDirectory;
090:
091: /** package under which resultant classes will reside */
092: private String destinationPackage;
093:
094: /** classpath used to compile the jsp files. */
095: private Path compileClasspath;
096:
097: //private String compilerPath; //fully qualified name for the compiler executable
098:
099: private String pathToPackage = "";
100: private Vector filesToDo = new Vector();
101:
102: /**
103: * Run the task.
104: * @throws BuildException if there is an error.
105: */
106: public void execute() throws BuildException {
107: if (!destinationDirectory.isDirectory()) {
108: throw new BuildException("destination directory "
109: + destinationDirectory.getPath() + " is not valid");
110: }
111:
112: if (!sourceDirectory.isDirectory()) {
113: throw new BuildException("src directory "
114: + sourceDirectory.getPath() + " is not valid");
115: }
116:
117: if (destinationPackage == null) {
118: throw new BuildException(
119: "package attribute must be present.", getLocation());
120: }
121:
122: pathToPackage = this .destinationPackage.replace('.',
123: File.separatorChar);
124: // get all the files in the sourceDirectory
125: DirectoryScanner ds = super
126: .getDirectoryScanner(sourceDirectory);
127:
128: //use the systemclasspath as well, to include the ant jar
129: if (compileClasspath == null) {
130: compileClasspath = new Path(getProject());
131: }
132:
133: compileClasspath = compileClasspath.concatSystemClasspath();
134: String[] files = ds.getIncludedFiles();
135:
136: //Weblogic.jspc calls System.exit() ... have to fork
137: // Therefore, takes loads of time
138: // Can pass directories at a time (*.jsp) but easily runs out of
139: // memory on hefty dirs (even on a Sun)
140: Java helperTask = new Java(this );
141: helperTask.setFork(true);
142: helperTask.setClassname("weblogic.jspc");
143: helperTask.setTaskName(getTaskName());
144: String[] args = new String[12];
145:
146: File jspFile = null;
147: String parents = "";
148: int j = 0;
149: //XXX this array stuff is a remnant of prev trials.. gotta remove.
150: args[j++] = "-d";
151: args[j++] = destinationDirectory.getAbsolutePath().trim();
152: args[j++] = "-docroot";
153: args[j++] = sourceDirectory.getAbsolutePath().trim();
154: args[j++] = "-keepgenerated"; //TODO: Parameterise ??
155: //Call compiler as class... dont want to fork again
156: //Use classic compiler -- can be parameterised?
157: args[j++] = "-compilerclass";
158: args[j++] = "sun.tools.javac.Main";
159: //Weblogic jspc does not seem to work unless u explicitly set this...
160: // Does not take the classpath from the env....
161: // Am i missing something about the Java task??
162: args[j++] = "-classpath";
163: args[j++] = compileClasspath.toString();
164:
165: this .scanDir(files);
166: log("Compiling " + filesToDo.size() + " JSP files");
167:
168: for (int i = 0; i < filesToDo.size(); i++) {
169: //XXX
170: // All this to get package according to weblogic standards
171: // Can be written better... this is too hacky!
172: // Careful.. similar code in scanDir , but slightly different!!
173: String filename = (String) filesToDo.elementAt(i);
174: jspFile = new File(filename);
175: args[j] = "-package";
176: parents = jspFile.getParent();
177: if ((parents != null) && (!("").equals(parents))) {
178: parents = this .replaceString(parents, File.separator,
179: "_.");
180: args[j + 1] = destinationPackage + "." + "_" + parents;
181: } else {
182: args[j + 1] = destinationPackage;
183: }
184:
185: args[j + 2] = sourceDirectory + File.separator + filename;
186: helperTask.clearArgs();
187:
188: for (int x = 0; x < j + 3; x++) {
189: helperTask.createArg().setValue(args[x]);
190: }
191:
192: helperTask.setClasspath(compileClasspath);
193: if (helperTask.executeJava() != 0) {
194: log(filename + " failed to compile", Project.MSG_WARN);
195: }
196: }
197: }
198:
199: /**
200: * Set the classpath to be used for this compilation.
201: * @param classpath the classpath to use.
202: */
203: public void setClasspath(Path classpath) {
204: if (compileClasspath == null) {
205: compileClasspath = classpath;
206: } else {
207: compileClasspath.append(classpath);
208: }
209: }
210:
211: /**
212: * Maybe creates a nested classpath element.
213: * @return a path to be configured.
214: */
215: public Path createClasspath() {
216: if (compileClasspath == null) {
217: compileClasspath = new Path(getProject());
218: }
219: return compileClasspath;
220: }
221:
222: /**
223: * Set the directory containing the source jsp's
224: *
225: *
226: * @param dirName the directory containg the source jsp's
227: */
228: public void setSrc(File dirName) {
229:
230: sourceDirectory = dirName;
231: }
232:
233: /**
234: * Set the directory containing the source jsp's
235: *
236: *
237: * @param dirName the directory containg the source jsp's
238: */
239: public void setDest(File dirName) {
240:
241: destinationDirectory = dirName;
242: }
243:
244: /**
245: * Set the package under which the compiled classes go
246: *
247: * @param packageName the package name for the clases
248: */
249: public void setPackage(String packageName) {
250:
251: destinationPackage = packageName;
252: }
253:
254: /**
255: * Scan the array of files and add the jsp
256: * files that need to be compiled to the filesToDo field.
257: * @param files the files to scan.
258: */
259: protected void scanDir(String[] files) {
260:
261: long now = (new Date()).getTime();
262: File jspFile = null;
263: String parents = null;
264: String pack = "";
265: for (int i = 0; i < files.length; i++) {
266: File srcFile = new File(this .sourceDirectory, files[i]);
267: //XXX
268: // All this to convert source to destination directory according
269: // to weblogic standards Can be written better... this is too hacky!
270: jspFile = new File(files[i]);
271: parents = jspFile.getParent();
272:
273: if ((parents != null) && (!("").equals(parents))) {
274: parents = this .replaceString(parents, File.separator,
275: "_/");
276: pack = pathToPackage + File.separator + "_" + parents;
277: } else {
278: pack = pathToPackage;
279: }
280:
281: String filePath = pack + File.separator + "_";
282: int startingIndex = files[i].lastIndexOf(File.separator) != -1 ? files[i]
283: .lastIndexOf(File.separator) + 1
284: : 0;
285: int endingIndex = files[i].indexOf(".jsp");
286: if (endingIndex == -1) {
287: log("Skipping " + files[i] + ". Not a JSP",
288: Project.MSG_VERBOSE);
289: continue;
290: }
291:
292: filePath += files[i].substring(startingIndex, endingIndex);
293: filePath += ".class";
294: File classFile = new File(this .destinationDirectory,
295: filePath);
296:
297: if (srcFile.lastModified() > now) {
298: log(
299: "Warning: file modified in the future: "
300: + files[i], Project.MSG_WARN);
301: }
302: if (srcFile.lastModified() > classFile.lastModified()) {
303: filesToDo.addElement(files[i]);
304: log("Recompiling File " + files[i], Project.MSG_VERBOSE);
305: }
306: }
307: }
308:
309: /**
310: * Replace occurances of a string with a replacement string.
311: * @param inpString the string to convert.
312: * @param escapeChars the string to replace.
313: * @param replaceChars the string to place.
314: * @return the converted string.
315: */
316: protected String replaceString(String inpString,
317: String escapeChars, String replaceChars) {
318: String localString = "";
319: int numTokens = 0;
320: StringTokenizer st = new StringTokenizer(inpString,
321: escapeChars, true);
322: numTokens = st.countTokens();
323: for (int i = 0; i < numTokens; i++) {
324: String test = st.nextToken();
325: test = (test.equals(escapeChars) ? replaceChars : test);
326: localString += test;
327: }
328: return localString;
329: }
330: }
|