001: /*
002: * The Apache Software License, Version 1.1
003: *
004: * Copyright (c) 1999 The Apache Software Foundation. All rights
005: * reserved.
006: *
007: * Redistribution and use in source and binary forms, with or without
008: * modification, are permitted provided that the following conditions
009: * are met:
010: *
011: * 1. Redistributions of source code must retain the above copyright
012: * notice, this list of conditions and the following disclaimer.
013: *
014: * 2. Redistributions in binary form must reproduce the above copyright
015: * notice, this list of conditions and the following disclaimer in
016: * the documentation and/or other materials provided with the
017: * distribution.
018: *
019: * 3. The end-user documentation included with the redistribution, if
020: * any, must include the following acknowlegement:
021: * "This product includes software developed by the
022: * Apache Software Foundation (http://www.apache.org/)."
023: * Alternately, this acknowlegement may appear in the software itself,
024: * if and wherever such third-party acknowlegements normally appear.
025: *
026: * 4. The names "The Jakarta Project", "Tomcat", and "Apache Software
027: * Foundation" must not be used to endorse or promote products derived
028: * from this software without prior written permission. For written
029: * permission, please contact apache@apache.org.
030: *
031: * 5. Products derived from this software may not be called "Apache"
032: * nor may "Apache" appear in their names without prior written
033: * permission of the Apache Group.
034: *
035: * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
036: * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
037: * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
038: * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
039: * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
040: * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
041: * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
042: * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
043: * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
044: * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
045: * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
046: * SUCH DAMAGE.
047: * ====================================================================
048: *
049: * This software consists of voluntary contributions made by many
050: * individuals on behalf of the Apache Software Foundation. For more
051: * information on the Apache Software Foundation, please see
052: * <http://www.apache.org/>.
053: *
054: */
055: package com.sun.portal.providers.jsp.jasper3.jasper.compiler;
056:
057: import java.util.Hashtable;
058: import java.io.FileNotFoundException;
059: import java.io.File;
060: import java.io.PrintWriter;
061: import java.io.ByteArrayOutputStream;
062: import java.io.FileOutputStream;
063: import java.io.OutputStreamWriter;
064:
065: import com.sun.portal.providers.jsp.jasper3.jasper.JspCompilationContext;
066: import com.sun.portal.providers.jsp.jasper3.jasper.Constants;
067: import com.sun.portal.providers.jsp.jasper3.jasper.JasperException;
068: import com.sun.portal.providers.jsp.jasper3.jasper.compiler.ParseException;
069:
070: import com.sun.portal.providers.jsp.jasper3.tomcat.logging.Logger;
071:
072: /**
073: * If you want to customize JSP compilation aspects, this class is
074: * something you should take a look at.
075: *
076: * Hope is that people can just extend Compiler and override things
077: * like isOutDated() but inherit things like compile(). This might
078: * change.
079: *
080: * @author Anil K. Vijendran
081: * @author Mandar Raje
082: */
083: public class Compiler {
084: protected JavaCompiler javac;
085: protected Mangler mangler;
086: protected JspCompilationContext ctxt;
087:
088: public Compiler(JspCompilationContext ctxt) {
089: this .ctxt = ctxt;
090: }
091:
092: // Checks to see if the output director exists, creat it if necessary
093: private boolean makeOutputDirectory(String javaFileName)
094: throws FileNotFoundException, Exception {
095: boolean res = false;
096: int index = javaFileName.lastIndexOf(File.separatorChar);
097:
098: if (index != -1) {
099: String outputDir = javaFileName.substring(0, index);
100:
101: File f = new File(outputDir);
102: if ((res = f.exists()) == false)
103: res = f.mkdirs();
104: }
105: return res;
106: }
107:
108: /**
109: * Compile the jsp file from the current engine context
110: *
111: * @return true if the class file was outdated the jsp file
112: * was recompiled.
113: */
114: public boolean compile() throws FileNotFoundException,
115: JasperException, Exception {
116: String pkgName = mangler.getPackageName();
117: ctxt.setServletPackageName(pkgName);
118: Constants.message("jsp.message.package_name_is",
119: new Object[] { (pkgName == null) ? "[default package]"
120: : pkgName }, Logger.DEBUG);
121:
122: String classFileName = mangler.getClassFileName();
123: Constants.message("jsp.message.class_file_name_is",
124: new Object[] { classFileName }, Logger.DEBUG);
125:
126: String javaFileName = mangler.getJavaFileName();
127: ctxt.setServletJavaFileName(javaFileName);
128: Constants.message("jsp.message.java_file_name_is",
129: new Object[] { javaFileName }, Logger.DEBUG);
130:
131: String className = mangler.getClassName();
132: ctxt.setServletClassName(className);
133: Constants.message("jsp.message.class_name_is",
134: new Object[] { className }, Logger.DEBUG);
135:
136: if (!isOutDated())
137: return false;
138:
139: // Need the encoding specified in the JSP 'page' directive for
140: // - reading the JSP page
141: // - writing the JSP servlet source
142: // - compiling the generated servlets (pass -encoding to javac).
143: // XXX - There are really three encodings of interest.
144:
145: String jspEncoding = "ISO-8859-1"; // default per JSP spec
146:
147: // We try UTF8 by default. If it fails, we use the java encoding
148: // specified for JspServlet init parameter "javaEncoding".
149: String javaEncoding = "UTF8";
150:
151: // This seems to be a reasonable point to scan the JSP file
152: // for a 'contentType' directive. If it found then the set
153: // the value of 'jspEncoding to reflect the value specified.
154: // Note: if (true) is convenience programming. It can be
155: // taken out once we have a more efficient method.
156:
157: if (true) {
158: JspReader tmpReader = JspReader.createJspReader(ctxt
159: .getJspFile(), ctxt, jspEncoding);
160: String newEncode = changeEncodingIfNecessary(tmpReader);
161: if (newEncode != null)
162: jspEncoding = newEncode;
163: }
164:
165: JspReader reader = JspReader.createJspReader(ctxt.getJspFile(),
166: ctxt, jspEncoding);
167:
168: // See if the directory where the .java and .class files to be generated
169: // exists; if not create it.
170: makeOutputDirectory(javaFileName);
171:
172: OutputStreamWriter osw;
173: try {
174: osw = new OutputStreamWriter(new FileOutputStream(
175: javaFileName), javaEncoding);
176: } catch (java.io.UnsupportedEncodingException ex) {
177: // Try to get the java encoding from the "javaEncoding"
178: // init parameter for JspServlet.
179: javaEncoding = ctxt.getOptions().getJavaEncoding();
180: if (javaEncoding != null) {
181: try {
182: osw = new OutputStreamWriter(new FileOutputStream(
183: javaFileName), javaEncoding);
184: } catch (java.io.UnsupportedEncodingException ex2) {
185: // no luck :-(
186: throw new JasperException(Constants.getString(
187: "jsp.error.invalid.javaEncoding",
188: new Object[] { "UTF8", javaEncoding, }));
189: }
190: } else {
191: throw new JasperException(Constants.getString(
192: "jsp.error.needAlternateJavaEncoding",
193: new Object[] { "UTF8" }));
194: }
195: }
196: ServletWriter writer = new ServletWriter(new PrintWriter(osw));
197:
198: ctxt.setReader(reader);
199: ctxt.setWriter(writer);
200:
201: ParseEventListener listener = new JspParseEventListener(ctxt);
202:
203: Parser p = new Parser(reader, listener);
204: listener.beginPageProcessing();
205: p.parse();
206: listener.endPageProcessing();
207: writer.close();
208:
209: String classpath = ctxt.getClassPath();
210:
211: // I'm nuking
212: // System.getProperty("jsp.class.path", ".")
213: // business. If anyone badly needs this we can talk. -akv
214:
215: String sep = System.getProperty("path.separator");
216:
217: // A@ rearrange the order of the included classpaths to
218: // force JSP engine to use our classpath before the
219: //system classpath
220: String[] argv = new String[] {
221: "-encoding",
222: javaEncoding,
223: "-classpath",
224: classpath + sep + System.getProperty("java.class.path")
225: + sep + ctxt.getOutputDir(), "-d",
226: ctxt.getOutputDir(), javaFileName };
227: // end of A@
228:
229: StringBuffer b = new StringBuffer();
230: for (int i = 0; i < argv.length; i++) {
231: b.append(argv[i]);
232: b.append(" ");
233: }
234:
235: Constants.message("jsp.message.compiling_with",
236: new Object[] { b.toString() }, Logger.DEBUG);
237:
238: /**
239: * 256 chosen randomly. The default is 32 if you don't pass
240: * anything to the constructor which will be less.
241: */
242: ByteArrayOutputStream out = new ByteArrayOutputStream(256);
243:
244: // if no compiler was set we can kick out now
245:
246: if (javac == null) {
247: return true;
248: }
249:
250: /**
251: * Configure the compiler object
252: */
253: javac.setEncoding(javaEncoding);
254:
255: // A@ rearrange the order of the included classpaths to
256: // force JSP engine to use our classpath before the
257: //system classpath
258: javac.setClasspath(classpath + sep
259: + System.getProperty("java.class.path") + sep
260: + ctxt.getOutputDir());
261: // end of A@
262:
263: javac.setOutputDir(ctxt.getOutputDir());
264: javac.setMsgOutput(out);
265: javac.setClassDebugInfo(ctxt.getOptions().getClassDebugInfo());
266:
267: /**
268: * Execute the compiler
269: */
270: boolean status = javac.compile(javaFileName);
271:
272: if (!ctxt.keepGenerated()) {
273: File javaFile = new File(javaFileName);
274: javaFile.delete();
275: }
276:
277: if (status == false) {
278: String msg = out.toString();
279: throw new JasperException(Constants
280: .getString("jsp.error.unable.compile")
281: + msg);
282: }
283:
284: String classFile = ctxt.getOutputDir() + File.separatorChar;
285: if (pkgName != null && !pkgName.equals(""))
286: classFile = classFile
287: + pkgName.replace('.', File.separatorChar)
288: + File.separatorChar;
289: classFile = classFile + className + ".class";
290:
291: if (!classFile.equals(classFileName)) {
292: File classFileObject = new File(classFile);
293: File myClassFileObject = new File(classFileName);
294: if (myClassFileObject.exists())
295: myClassFileObject.delete();
296: if (classFileObject.renameTo(myClassFileObject) == false)
297: throw new JasperException(Constants.getString(
298: "jsp.error.unable.rename", new Object[] {
299: classFileObject, myClassFileObject }));
300: }
301:
302: return true;
303: }
304:
305: public void computeServletClassName() {
306: // Hack to avoid readign the class file every time -
307: // getClassName() is an _expensive_ operation, and it's needed only
308: // if isOutDated() return true.
309: String className = mangler.getClassName();
310: ctxt.setServletClassName(className);
311: Constants.message("jsp.message.class_name_is",
312: new Object[] { className }, Logger.DEBUG);
313: }
314:
315: /**
316: * This is a protected method intended to be overridden by
317: * subclasses of Compiler. This is used by the compile method
318: * to do all the compilation.
319: */
320: public boolean isOutDated() {
321: return true;
322: }
323:
324: /**
325: * Set java compiler info
326: */
327: public void setJavaCompiler(JavaCompiler javac) {
328: this .javac = javac;
329: }
330:
331: /**
332: * Set Mangler which will be used as part of compile().
333: */
334: public void setMangler(Mangler mangler) {
335: this .mangler = mangler;
336: }
337:
338: /**
339: * Change the encoding for the reader if specified.
340: */
341: public String changeEncodingIfNecessary(JspReader tmpReader)
342: throws ParseException {
343:
344: // A lot of code replicated from Parser.java
345: // Main aim is to "get-it-to-work".
346: while (tmpReader.skipUntil("<%@") != null) {
347:
348: tmpReader.skipSpaces();
349:
350: // check if it is a page directive.
351: if (tmpReader.matches("page")) {
352:
353: tmpReader.advance(4);
354: tmpReader.skipSpaces();
355:
356: try {
357: Hashtable attrs = tmpReader.parseTagAttributes();
358: String ct = (String) attrs.get("contentType");
359: if (ct != null) {
360: int loc = ct.indexOf("charset=");
361: if (loc > 0) {
362: String encoding = ct.substring(loc + 8);
363: return encoding;
364: }
365: }
366: } catch (ParseException ex) {
367: // Ignore the exception here, it will be caught later.
368: return null;
369: }
370: }
371: }
372: return null;
373: }
374:
375: /**
376: * Remove generated files
377: */
378: public void removeGeneratedFiles() {
379: try {
380: String classFileName = mangler.getClassFileName();
381: if (classFileName != null) {
382: File classFile = new File(classFileName);
383: classFile.delete();
384: }
385: String javaFileName = mangler.getJavaFileName();
386: if (javaFileName != null) {
387: File javaFile = new File(javaFileName);
388: javaFile.delete();
389: }
390: } catch (Exception e) {
391: }
392: }
393: }
|