0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017:
0018: package org.apache.jasper;
0019:
0020: import java.io.BufferedReader;
0021: import java.io.CharArrayWriter;
0022: import java.io.File;
0023: import java.io.FileInputStream;
0024: import java.io.FileNotFoundException;
0025: import java.io.FileOutputStream;
0026: import java.io.FileReader;
0027: import java.io.FileWriter;
0028: import java.io.IOException;
0029: import java.io.PrintWriter;
0030: import java.io.Writer;
0031: import java.net.MalformedURLException;
0032: import java.net.URL;
0033: import java.net.URLClassLoader;
0034: import java.util.ArrayList;
0035: import java.util.Iterator;
0036: import java.util.List;
0037: import java.util.Map;
0038: import java.util.HashMap;
0039: import java.util.Stack;
0040: import java.util.StringTokenizer;
0041: import java.util.Vector;
0042:
0043: import org.apache.jasper.compiler.Compiler;
0044: import org.apache.jasper.compiler.JspConfig;
0045: import org.apache.jasper.compiler.JspRuntimeContext;
0046: import org.apache.jasper.compiler.Localizer;
0047: import org.apache.jasper.compiler.TagPluginManager;
0048: import org.apache.jasper.compiler.TldLocationsCache;
0049: import org.apache.jasper.servlet.JspCServletContext;
0050: import org.apache.juli.logging.Log;
0051: import org.apache.juli.logging.LogFactory;
0052:
0053: /**
0054: * Shell for the jspc compiler. Handles all options associated with the
0055: * command line and creates compilation contexts which it then compiles
0056: * according to the specified options.
0057: *
0058: * This version can process files from a _single_ webapp at once, i.e.
0059: * a single docbase can be specified.
0060: *
0061: * It can be used as an Ant task using:
0062: * <pre>
0063: * <taskdef classname="org.apache.jasper.JspC" name="jasper2" >
0064: * <classpath>
0065: * <pathelement location="${java.home}/../lib/tools.jar"/>
0066: * <fileset dir="${ENV.CATALINA_HOME}/server/lib">
0067: * <include name="*.jar"/>
0068: * </fileset>
0069: * <fileset dir="${ENV.CATALINA_HOME}/common/lib">
0070: * <include name="*.jar"/>
0071: * </fileset>
0072: * <path refid="myjars"/>
0073: * </classpath>
0074: * </taskdef>
0075: *
0076: * <jasper2 verbose="0"
0077: * package="my.package"
0078: * uriroot="${webapps.dir}/${webapp.name}"
0079: * webXmlFragment="${build.dir}/generated_web.xml"
0080: * outputDir="${webapp.dir}/${webapp.name}/WEB-INF/src/my/package" />
0081: * </pre>
0082: *
0083: * @author Danno Ferrin
0084: * @author Pierre Delisle
0085: * @author Costin Manolache
0086: * @author Yoav Shapira
0087: */
0088: public class JspC implements Options {
0089:
0090: public static final String DEFAULT_IE_CLASS_ID = "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
0091:
0092: // Logger
0093: protected static Log log = LogFactory.getLog(JspC.class);
0094:
0095: protected static final String SWITCH_VERBOSE = "-v";
0096: protected static final String SWITCH_HELP = "-help";
0097: protected static final String SWITCH_OUTPUT_DIR = "-d";
0098: protected static final String SWITCH_PACKAGE_NAME = "-p";
0099: protected static final String SWITCH_CACHE = "-cache";
0100: protected static final String SWITCH_CLASS_NAME = "-c";
0101: protected static final String SWITCH_FULL_STOP = "--";
0102: protected static final String SWITCH_COMPILE = "-compile";
0103: protected static final String SWITCH_SOURCE = "-source";
0104: protected static final String SWITCH_TARGET = "-target";
0105: protected static final String SWITCH_URI_BASE = "-uribase";
0106: protected static final String SWITCH_URI_ROOT = "-uriroot";
0107: protected static final String SWITCH_FILE_WEBAPP = "-webapp";
0108: protected static final String SWITCH_WEBAPP_INC = "-webinc";
0109: protected static final String SWITCH_WEBAPP_XML = "-webxml";
0110: protected static final String SWITCH_MAPPED = "-mapped";
0111: protected static final String SWITCH_XPOWERED_BY = "-xpoweredBy";
0112: protected static final String SWITCH_TRIM_SPACES = "-trimSpaces";
0113: protected static final String SWITCH_CLASSPATH = "-classpath";
0114: protected static final String SWITCH_DIE = "-die";
0115: protected static final String SWITCH_POOLING = "-poolingEnabled";
0116: protected static final String SWITCH_ENCODING = "-javaEncoding";
0117: protected static final String SWITCH_SMAP = "-smap";
0118: protected static final String SWITCH_DUMP_SMAP = "-dumpsmap";
0119:
0120: protected static final String SHOW_SUCCESS = "-s";
0121: protected static final String LIST_ERRORS = "-l";
0122: protected static final int INC_WEBXML = 10;
0123: protected static final int ALL_WEBXML = 20;
0124: protected static final int DEFAULT_DIE_LEVEL = 1;
0125: protected static final int NO_DIE_LEVEL = 0;
0126:
0127: protected static final String[] insertBefore = { "</web-app>",
0128: "<servlet-mapping>", "<session-config>", "<mime-mapping>",
0129: "<welcome-file-list>", "<error-page>", "<taglib>",
0130: "<resource-env-ref>", "<resource-ref>",
0131: "<security-constraint>", "<login-config>",
0132: "<security-role>", "<env-entry>", "<ejb-ref>",
0133: "<ejb-local-ref>" };
0134:
0135: protected static int die;
0136: protected String classPath = null;
0137: protected URLClassLoader loader = null;
0138: protected boolean trimSpaces = false;
0139: protected boolean genStringAsCharArray = false;
0140: protected boolean xpoweredBy;
0141: protected boolean mappedFile = false;
0142: protected boolean poolingEnabled = true;
0143: protected File scratchDir;
0144: protected String ieClassId = DEFAULT_IE_CLASS_ID;
0145: protected String targetPackage;
0146: protected String targetClassName;
0147: protected String uriBase;
0148: protected String uriRoot;
0149: // protected Project project;
0150: protected int dieLevel;
0151: protected boolean helpNeeded = false;
0152: protected boolean compile = false;
0153: protected boolean smapSuppressed = true;
0154: protected boolean smapDumped = false;
0155: protected boolean caching = true;
0156: protected Map cache = new HashMap();
0157:
0158: protected String compiler = null;
0159:
0160: protected String compilerTargetVM = "1.4";
0161: protected String compilerSourceVM = "1.4";
0162:
0163: protected boolean classDebugInfo = true;
0164:
0165: /**
0166: * Throw an exception if there's a compilation error, or swallow it.
0167: * Default is true to preserve old behavior.
0168: */
0169: protected boolean failOnError = true;
0170:
0171: /**
0172: * The file extensions to be handled as JSP files.
0173: * Default list is .jsp and .jspx.
0174: */
0175: protected List extensions;
0176:
0177: /**
0178: * The pages.
0179: */
0180: protected List pages = new Vector();
0181:
0182: /**
0183: * Needs better documentation, this data member does.
0184: * True by default.
0185: */
0186: protected boolean errorOnUseBeanInvalidClassAttribute = true;
0187:
0188: /**
0189: * The java file encoding. Default
0190: * is UTF-8. Added per bugzilla 19622.
0191: */
0192: protected String javaEncoding = "UTF-8";
0193:
0194: // Generation of web.xml fragments
0195: protected String webxmlFile;
0196: protected int webxmlLevel;
0197: protected boolean addWebXmlMappings = false;
0198:
0199: protected Writer mapout;
0200: protected CharArrayWriter servletout;
0201: protected CharArrayWriter mappingout;
0202:
0203: /**
0204: * The servlet context.
0205: */
0206: protected JspCServletContext context;
0207:
0208: /**
0209: * The runtime context.
0210: * Maintain a dummy JspRuntimeContext for compiling tag files.
0211: */
0212: protected JspRuntimeContext rctxt;
0213:
0214: /**
0215: * Cache for the TLD locations
0216: */
0217: protected TldLocationsCache tldLocationsCache = null;
0218:
0219: protected JspConfig jspConfig = null;
0220: protected TagPluginManager tagPluginManager = null;
0221:
0222: protected boolean verbose = false;
0223: protected boolean listErrors = false;
0224: protected boolean showSuccess = false;
0225: protected int argPos;
0226: protected boolean fullstop = false;
0227: protected String args[];
0228:
0229: public static void main(String arg[]) {
0230: if (arg.length == 0) {
0231: System.out.println(Localizer.getMessage("jspc.usage"));
0232: } else {
0233: try {
0234: JspC jspc = new JspC();
0235: jspc.setArgs(arg);
0236: if (jspc.helpNeeded) {
0237: System.out.println(Localizer
0238: .getMessage("jspc.usage"));
0239: } else {
0240: jspc.execute();
0241: }
0242: } catch (JasperException je) {
0243: System.err.println(je);
0244: if (die != NO_DIE_LEVEL) {
0245: System.exit(die);
0246: }
0247: }
0248: }
0249: }
0250:
0251: public void setArgs(String[] arg) throws JasperException {
0252: args = arg;
0253: String tok;
0254:
0255: dieLevel = NO_DIE_LEVEL;
0256: die = dieLevel;
0257:
0258: while ((tok = nextArg()) != null) {
0259: if (tok.equals(SWITCH_VERBOSE)) {
0260: verbose = true;
0261: showSuccess = true;
0262: listErrors = true;
0263: } else if (tok.equals(SWITCH_OUTPUT_DIR)) {
0264: tok = nextArg();
0265: setOutputDir(tok);
0266: } else if (tok.equals(SWITCH_PACKAGE_NAME)) {
0267: targetPackage = nextArg();
0268: } else if (tok.equals(SWITCH_COMPILE)) {
0269: compile = true;
0270: } else if (tok.equals(SWITCH_CLASS_NAME)) {
0271: targetClassName = nextArg();
0272: } else if (tok.equals(SWITCH_URI_BASE)) {
0273: uriBase = nextArg();
0274: } else if (tok.equals(SWITCH_URI_ROOT)) {
0275: setUriroot(nextArg());
0276: } else if (tok.equals(SWITCH_FILE_WEBAPP)) {
0277: setUriroot(nextArg());
0278: } else if (tok.equals(SHOW_SUCCESS)) {
0279: showSuccess = true;
0280: } else if (tok.equals(LIST_ERRORS)) {
0281: listErrors = true;
0282: } else if (tok.equals(SWITCH_WEBAPP_INC)) {
0283: webxmlFile = nextArg();
0284: if (webxmlFile != null) {
0285: webxmlLevel = INC_WEBXML;
0286: }
0287: } else if (tok.equals(SWITCH_WEBAPP_XML)) {
0288: webxmlFile = nextArg();
0289: if (webxmlFile != null) {
0290: webxmlLevel = ALL_WEBXML;
0291: }
0292: } else if (tok.equals(SWITCH_MAPPED)) {
0293: mappedFile = true;
0294: } else if (tok.equals(SWITCH_XPOWERED_BY)) {
0295: xpoweredBy = true;
0296: } else if (tok.equals(SWITCH_TRIM_SPACES)) {
0297: setTrimSpaces(true);
0298: } else if (tok.equals(SWITCH_CACHE)) {
0299: tok = nextArg();
0300: if ("false".equals(tok)) {
0301: caching = false;
0302: } else {
0303: caching = true;
0304: }
0305: } else if (tok.equals(SWITCH_CLASSPATH)) {
0306: setClassPath(nextArg());
0307: } else if (tok.startsWith(SWITCH_DIE)) {
0308: try {
0309: dieLevel = Integer.parseInt(tok
0310: .substring(SWITCH_DIE.length()));
0311: } catch (NumberFormatException nfe) {
0312: dieLevel = DEFAULT_DIE_LEVEL;
0313: }
0314: die = dieLevel;
0315: } else if (tok.equals(SWITCH_HELP)) {
0316: helpNeeded = true;
0317: } else if (tok.equals(SWITCH_POOLING)) {
0318: tok = nextArg();
0319: if ("false".equals(tok)) {
0320: poolingEnabled = false;
0321: } else {
0322: poolingEnabled = true;
0323: }
0324: } else if (tok.equals(SWITCH_ENCODING)) {
0325: setJavaEncoding(nextArg());
0326: } else if (tok.equals(SWITCH_SOURCE)) {
0327: setCompilerSourceVM(nextArg());
0328: } else if (tok.equals(SWITCH_TARGET)) {
0329: setCompilerTargetVM(nextArg());
0330: } else if (tok.equals(SWITCH_SMAP)) {
0331: smapSuppressed = false;
0332: } else if (tok.equals(SWITCH_DUMP_SMAP)) {
0333: smapDumped = true;
0334: } else {
0335: if (tok.startsWith("-")) {
0336: throw new JasperException("Unrecognized option: "
0337: + tok + ". Use -help for help.");
0338: }
0339: if (!fullstop) {
0340: argPos--;
0341: }
0342: // Start treating the rest as JSP Pages
0343: break;
0344: }
0345: }
0346:
0347: // Add all extra arguments to the list of files
0348: while (true) {
0349: String file = nextFile();
0350: if (file == null) {
0351: break;
0352: }
0353: pages.add(file);
0354: }
0355: }
0356:
0357: public boolean getKeepGenerated() {
0358: // isn't this why we are running jspc?
0359: return true;
0360: }
0361:
0362: public boolean getTrimSpaces() {
0363: return trimSpaces;
0364: }
0365:
0366: public void setTrimSpaces(boolean ts) {
0367: this .trimSpaces = ts;
0368: }
0369:
0370: public boolean isPoolingEnabled() {
0371: return poolingEnabled;
0372: }
0373:
0374: public void setPoolingEnabled(boolean poolingEnabled) {
0375: this .poolingEnabled = poolingEnabled;
0376: }
0377:
0378: public boolean isXpoweredBy() {
0379: return xpoweredBy;
0380: }
0381:
0382: public void setXpoweredBy(boolean xpoweredBy) {
0383: this .xpoweredBy = xpoweredBy;
0384: }
0385:
0386: public boolean getDisplaySourceFragment() {
0387: return true;
0388: }
0389:
0390: public boolean getErrorOnUseBeanInvalidClassAttribute() {
0391: return errorOnUseBeanInvalidClassAttribute;
0392: }
0393:
0394: public void setErrorOnUseBeanInvalidClassAttribute(boolean b) {
0395: errorOnUseBeanInvalidClassAttribute = b;
0396: }
0397:
0398: public int getTagPoolSize() {
0399: return Constants.MAX_POOL_SIZE;
0400: }
0401:
0402: /**
0403: * Are we supporting HTML mapped servlets?
0404: */
0405: public boolean getMappedFile() {
0406: return mappedFile;
0407: }
0408:
0409: // Off-line compiler, no need for security manager
0410: public Object getProtectionDomain() {
0411: return null;
0412: }
0413:
0414: public boolean getSendErrorToClient() {
0415: // implied send to System.err
0416: return true;
0417: }
0418:
0419: public void setClassDebugInfo(boolean b) {
0420: classDebugInfo = b;
0421: }
0422:
0423: public boolean getClassDebugInfo() {
0424: // compile with debug info
0425: return classDebugInfo;
0426: }
0427:
0428: /**
0429: * @see Options#isCaching()
0430: */
0431: public boolean isCaching() {
0432: return caching;
0433: }
0434:
0435: /**
0436: * @see Options#isCaching()
0437: */
0438: public void setCaching(boolean caching) {
0439: this .caching = caching;
0440: }
0441:
0442: /**
0443: * @see Options#getCache()
0444: */
0445: public Map getCache() {
0446: return cache;
0447: }
0448:
0449: /**
0450: * Background compilation check intervals in seconds
0451: */
0452: public int getCheckInterval() {
0453: return 0;
0454: }
0455:
0456: /**
0457: * Modification test interval.
0458: */
0459: public int getModificationTestInterval() {
0460: return 0;
0461: }
0462:
0463: /**
0464: * Is Jasper being used in development mode?
0465: */
0466: public boolean getDevelopment() {
0467: return false;
0468: }
0469:
0470: /**
0471: * Is the generation of SMAP info for JSR45 debuggin suppressed?
0472: */
0473: public boolean isSmapSuppressed() {
0474: return smapSuppressed;
0475: }
0476:
0477: /**
0478: * Set smapSuppressed flag.
0479: */
0480: public void setSmapSuppressed(boolean smapSuppressed) {
0481: this .smapSuppressed = smapSuppressed;
0482: }
0483:
0484: /**
0485: * Should SMAP info for JSR45 debugging be dumped to a file?
0486: */
0487: public boolean isSmapDumped() {
0488: return smapDumped;
0489: }
0490:
0491: /**
0492: * Set smapSuppressed flag.
0493: */
0494: public void setSmapDumped(boolean smapDumped) {
0495: this .smapDumped = smapDumped;
0496: }
0497:
0498: /**
0499: * Determines whether text strings are to be generated as char arrays,
0500: * which improves performance in some cases.
0501: *
0502: * @param genStringAsCharArray true if text strings are to be generated as
0503: * char arrays, false otherwise
0504: */
0505: public void setGenStringAsCharArray(boolean genStringAsCharArray) {
0506: this .genStringAsCharArray = genStringAsCharArray;
0507: }
0508:
0509: /**
0510: * Indicates whether text strings are to be generated as char arrays.
0511: *
0512: * @return true if text strings are to be generated as char arrays, false
0513: * otherwise
0514: */
0515: public boolean genStringAsCharArray() {
0516: return genStringAsCharArray;
0517: }
0518:
0519: /**
0520: * Sets the class-id value to be sent to Internet Explorer when using
0521: * <jsp:plugin> tags.
0522: *
0523: * @param ieClassId Class-id value
0524: */
0525: public void setIeClassId(String ieClassId) {
0526: this .ieClassId = ieClassId;
0527: }
0528:
0529: /**
0530: * Gets the class-id value that is sent to Internet Explorer when using
0531: * <jsp:plugin> tags.
0532: *
0533: * @return Class-id value
0534: */
0535: public String getIeClassId() {
0536: return ieClassId;
0537: }
0538:
0539: public File getScratchDir() {
0540: return scratchDir;
0541: }
0542:
0543: public Class getJspCompilerPlugin() {
0544: // we don't compile, so this is meanlingless
0545: return null;
0546: }
0547:
0548: public String getJspCompilerPath() {
0549: // we don't compile, so this is meanlingless
0550: return null;
0551: }
0552:
0553: /**
0554: * Compiler to use.
0555: */
0556: public String getCompiler() {
0557: return compiler;
0558: }
0559:
0560: public void setCompiler(String c) {
0561: compiler = c;
0562: }
0563:
0564: /**
0565: * Compiler class name to use.
0566: */
0567: public String getCompilerClassName() {
0568: return null;
0569: }
0570:
0571: /**
0572: * @see Options#getCompilerTargetVM
0573: */
0574: public String getCompilerTargetVM() {
0575: return compilerTargetVM;
0576: }
0577:
0578: public void setCompilerTargetVM(String vm) {
0579: compilerTargetVM = vm;
0580: }
0581:
0582: /**
0583: * @see Options#getCompilerSourceVM()
0584: */
0585: public String getCompilerSourceVM() {
0586: return compilerSourceVM;
0587: }
0588:
0589: /**
0590: * @see Options#getCompilerSourceVM()
0591: */
0592: public void setCompilerSourceVM(String vm) {
0593: compilerSourceVM = vm;
0594: }
0595:
0596: public TldLocationsCache getTldLocationsCache() {
0597: return tldLocationsCache;
0598: }
0599:
0600: /**
0601: * Returns the encoding to use for
0602: * java files. The default is UTF-8.
0603: *
0604: * @return String The encoding
0605: */
0606: public String getJavaEncoding() {
0607: return javaEncoding;
0608: }
0609:
0610: /**
0611: * Sets the encoding to use for
0612: * java files.
0613: *
0614: * @param encodingName The name, e.g. "UTF-8"
0615: */
0616: public void setJavaEncoding(String encodingName) {
0617: javaEncoding = encodingName;
0618: }
0619:
0620: public boolean getFork() {
0621: return false;
0622: }
0623:
0624: public String getClassPath() {
0625: if (classPath != null)
0626: return classPath;
0627: return System.getProperty("java.class.path");
0628: }
0629:
0630: public void setClassPath(String s) {
0631: classPath = s;
0632: }
0633:
0634: /**
0635: * Returns the list of file extensions
0636: * that are treated as JSP files.
0637: *
0638: * @return The list of extensions
0639: */
0640: public List getExtensions() {
0641: return extensions;
0642: }
0643:
0644: /**
0645: * Adds the given file extension to the
0646: * list of extensions handled as JSP files.
0647: *
0648: * @param extension The extension to add, e.g. "myjsp"
0649: */
0650: protected void addExtension(final String extension) {
0651: if (extension != null) {
0652: if (extensions == null) {
0653: extensions = new Vector();
0654: }
0655:
0656: extensions.add(extension);
0657: }
0658: }
0659:
0660: /**
0661: * Base dir for the webapp. Used to generate class names and resolve
0662: * includes
0663: */
0664: public void setUriroot(String s) {
0665: if (s == null) {
0666: uriRoot = s;
0667: return;
0668: }
0669: try {
0670: uriRoot = resolveFile(s).getCanonicalPath();
0671: } catch (Exception ex) {
0672: uriRoot = s;
0673: }
0674: }
0675:
0676: /**
0677: * Parses comma-separated list of JSP files to be processed. If the argument
0678: * is null, nothing is done.
0679: *
0680: * <p>Each file is interpreted relative to uriroot, unless it is absolute,
0681: * in which case it must start with uriroot.</p>
0682: *
0683: * @param jspFiles Comma-separated list of JSP files to be processed
0684: */
0685: public void setJspFiles(final String jspFiles) {
0686: if (jspFiles == null) {
0687: return;
0688: }
0689:
0690: StringTokenizer tok = new StringTokenizer(jspFiles, ",");
0691: while (tok.hasMoreTokens()) {
0692: pages.add(tok.nextToken());
0693: }
0694: }
0695:
0696: /**
0697: * Sets the compile flag.
0698: *
0699: * @param b Flag value
0700: */
0701: public void setCompile(final boolean b) {
0702: compile = b;
0703: }
0704:
0705: /**
0706: * Sets the verbosity level. The actual number doesn't
0707: * matter: if it's greater than zero, the verbose flag will
0708: * be true.
0709: *
0710: * @param level Positive means verbose
0711: */
0712: public void setVerbose(final int level) {
0713: if (level > 0) {
0714: verbose = true;
0715: showSuccess = true;
0716: listErrors = true;
0717: }
0718: }
0719:
0720: public void setValidateXml(boolean b) {
0721: org.apache.jasper.xmlparser.ParserUtils.validating = b;
0722: }
0723:
0724: public void setListErrors(boolean b) {
0725: listErrors = b;
0726: }
0727:
0728: public void setOutputDir(String s) {
0729: if (s != null) {
0730: scratchDir = resolveFile(s).getAbsoluteFile();
0731: } else {
0732: scratchDir = null;
0733: }
0734: }
0735:
0736: public void setPackage(String p) {
0737: targetPackage = p;
0738: }
0739:
0740: /**
0741: * Class name of the generated file ( without package ).
0742: * Can only be used if a single file is converted.
0743: * XXX Do we need this feature ?
0744: */
0745: public void setClassName(String p) {
0746: targetClassName = p;
0747: }
0748:
0749: /**
0750: * File where we generate a web.xml fragment with the class definitions.
0751: */
0752: public void setWebXmlFragment(String s) {
0753: webxmlFile = resolveFile(s).getAbsolutePath();
0754: webxmlLevel = INC_WEBXML;
0755: }
0756:
0757: /**
0758: * File where we generate a complete web.xml with the class definitions.
0759: */
0760: public void setWebXml(String s) {
0761: webxmlFile = resolveFile(s).getAbsolutePath();
0762: webxmlLevel = ALL_WEBXML;
0763: }
0764:
0765: public void setAddWebXmlMappings(boolean b) {
0766: addWebXmlMappings = b;
0767: }
0768:
0769: /**
0770: * Set the option that throws an exception in case of a compilation error.
0771: */
0772: public void setFailOnError(final boolean b) {
0773: failOnError = b;
0774: }
0775:
0776: public boolean getFailOnError() {
0777: return failOnError;
0778: }
0779:
0780: /**
0781: * Obtain JSP configuration informantion specified in web.xml.
0782: */
0783: public JspConfig getJspConfig() {
0784: return jspConfig;
0785: }
0786:
0787: public TagPluginManager getTagPluginManager() {
0788: return tagPluginManager;
0789: }
0790:
0791: public void generateWebMapping(String file,
0792: JspCompilationContext clctxt) throws IOException {
0793: String className = clctxt.getServletClassName();
0794: String packageName = clctxt.getServletPackageName();
0795:
0796: String this ServletName;
0797: if ("".equals(packageName)) {
0798: this ServletName = className;
0799: } else {
0800: this ServletName = packageName + '.' + className;
0801: }
0802:
0803: if (servletout != null) {
0804: servletout.write("\n <servlet>\n <servlet-name>");
0805: servletout.write(this ServletName);
0806: servletout
0807: .write("</servlet-name>\n <servlet-class>");
0808: servletout.write(this ServletName);
0809: servletout.write("</servlet-class>\n </servlet>\n");
0810: }
0811: if (mappingout != null) {
0812: mappingout
0813: .write("\n <servlet-mapping>\n <servlet-name>");
0814: mappingout.write(this ServletName);
0815: mappingout.write("</servlet-name>\n <url-pattern>");
0816: mappingout.write(file.replace('\\', '/'));
0817: mappingout
0818: .write("</url-pattern>\n </servlet-mapping>\n");
0819:
0820: }
0821: }
0822:
0823: /**
0824: * Include the generated web.xml inside the webapp's web.xml.
0825: */
0826: protected void mergeIntoWebXml() throws IOException {
0827:
0828: File webappBase = new File(uriRoot);
0829: File webXml = new File(webappBase, "WEB-INF/web.xml");
0830: File webXml2 = new File(webappBase, "WEB-INF/web2.xml");
0831: String insertStartMarker = Localizer
0832: .getMessage("jspc.webinc.insertStart");
0833: String insertEndMarker = Localizer
0834: .getMessage("jspc.webinc.insertEnd");
0835:
0836: BufferedReader reader = new BufferedReader(new FileReader(
0837: webXml));
0838: BufferedReader fragmentReader = new BufferedReader(
0839: new FileReader(webxmlFile));
0840: PrintWriter writer = new PrintWriter(new FileWriter(webXml2));
0841:
0842: // Insert the <servlet> and <servlet-mapping> declarations
0843: int pos = -1;
0844: String line = null;
0845: while (true) {
0846: line = reader.readLine();
0847: if (line == null) {
0848: break;
0849: }
0850: // Skip anything previously generated by JSPC
0851: if (line.indexOf(insertStartMarker) >= 0) {
0852: while (true) {
0853: line = reader.readLine();
0854: if (line == null) {
0855: return;
0856: }
0857: if (line.indexOf(insertEndMarker) >= 0) {
0858: line = reader.readLine();
0859: line = reader.readLine();
0860: if (line == null) {
0861: return;
0862: }
0863: break;
0864: }
0865: }
0866: }
0867: for (int i = 0; i < insertBefore.length; i++) {
0868: pos = line.indexOf(insertBefore[i]);
0869: if (pos >= 0)
0870: break;
0871: }
0872: if (pos >= 0) {
0873: writer.print(line.substring(0, pos));
0874: break;
0875: } else {
0876: writer.println(line);
0877: }
0878: }
0879:
0880: writer.println(insertStartMarker);
0881: while (true) {
0882: String line2 = fragmentReader.readLine();
0883: if (line2 == null) {
0884: writer.println();
0885: break;
0886: }
0887: writer.println(line2);
0888: }
0889: writer.println(insertEndMarker);
0890: writer.println();
0891:
0892: for (int i = 0; i < pos; i++) {
0893: writer.print(" ");
0894: }
0895: writer.println(line.substring(pos));
0896:
0897: while (true) {
0898: line = reader.readLine();
0899: if (line == null) {
0900: break;
0901: }
0902: writer.println(line);
0903: }
0904: writer.close();
0905:
0906: reader.close();
0907: fragmentReader.close();
0908:
0909: FileInputStream fis = new FileInputStream(webXml2);
0910: FileOutputStream fos = new FileOutputStream(webXml);
0911:
0912: byte buf[] = new byte[512];
0913: while (true) {
0914: int n = fis.read(buf);
0915: if (n < 0) {
0916: break;
0917: }
0918: fos.write(buf, 0, n);
0919: }
0920:
0921: fis.close();
0922: fos.close();
0923:
0924: webXml2.delete();
0925: (new File(webxmlFile)).delete();
0926:
0927: }
0928:
0929: protected void processFile(String file) throws JasperException {
0930: ClassLoader originalClassLoader = null;
0931:
0932: try {
0933: // set up a scratch/output dir if none is provided
0934: if (scratchDir == null) {
0935: String temp = System.getProperty("java.io.tmpdir");
0936: if (temp == null) {
0937: temp = "";
0938: }
0939: scratchDir = new File(new File(temp).getAbsolutePath());
0940: }
0941:
0942: String jspUri = file.replace('\\', '/');
0943: JspCompilationContext clctxt = new JspCompilationContext(
0944: jspUri, false, this , context, null, rctxt);
0945:
0946: /* Override the defaults */
0947: if ((targetClassName != null)
0948: && (targetClassName.length() > 0)) {
0949: clctxt.setServletClassName(targetClassName);
0950: targetClassName = null;
0951: }
0952: if (targetPackage != null) {
0953: clctxt.setServletPackageName(targetPackage);
0954: }
0955:
0956: originalClassLoader = Thread.currentThread()
0957: .getContextClassLoader();
0958: if (loader == null) {
0959: initClassLoader(clctxt);
0960: }
0961: Thread.currentThread().setContextClassLoader(loader);
0962:
0963: clctxt.setClassLoader(loader);
0964: clctxt.setClassPath(classPath);
0965:
0966: Compiler clc = clctxt.createCompiler();
0967:
0968: // If compile is set, generate both .java and .class, if
0969: // .jsp file is newer than .class file;
0970: // Otherwise only generate .java, if .jsp file is newer than
0971: // the .java file
0972: if (clc.isOutDated(compile)) {
0973: clc.compile(compile, true);
0974: }
0975:
0976: // Generate mapping
0977: generateWebMapping(file, clctxt);
0978: if (showSuccess) {
0979: log.info("Built File: " + file);
0980: }
0981:
0982: } catch (JasperException je) {
0983: Throwable rootCause = je;
0984: while (rootCause instanceof JasperException
0985: && ((JasperException) rootCause).getRootCause() != null) {
0986: rootCause = ((JasperException) rootCause)
0987: .getRootCause();
0988: }
0989: if (rootCause != je) {
0990: log
0991: .error(Localizer.getMessage(
0992: "jspc.error.generalException", file),
0993: rootCause);
0994: }
0995:
0996: // Bugzilla 35114.
0997: if (getFailOnError()) {
0998: throw je;
0999: } else {
1000: log.error(je.getMessage());
1001: }
1002:
1003: } catch (Exception e) {
1004: if ((e instanceof FileNotFoundException)
1005: && log.isWarnEnabled()) {
1006: log.warn(Localizer.getMessage(
1007: "jspc.error.fileDoesNotExist", e.getMessage()));
1008: }
1009: throw new JasperException(e);
1010: } finally {
1011: if (originalClassLoader != null) {
1012: Thread.currentThread().setContextClassLoader(
1013: originalClassLoader);
1014: }
1015: }
1016: }
1017:
1018: /**
1019: * Locate all jsp files in the webapp. Used if no explicit
1020: * jsps are specified.
1021: */
1022: public void scanFiles(File base) throws JasperException {
1023: Stack<String> dirs = new Stack<String>();
1024: dirs.push(base.toString());
1025:
1026: // Make sure default extensions are always included
1027: if ((getExtensions() == null) || (getExtensions().size() < 2)) {
1028: addExtension("jsp");
1029: addExtension("jspx");
1030: }
1031:
1032: while (!dirs.isEmpty()) {
1033: String s = dirs.pop();
1034: File f = new File(s);
1035: if (f.exists() && f.isDirectory()) {
1036: String[] files = f.list();
1037: String ext;
1038: for (int i = 0; (files != null) && i < files.length; i++) {
1039: File f2 = new File(s, files[i]);
1040: if (f2.isDirectory()) {
1041: dirs.push(f2.getPath());
1042: } else {
1043: String path = f2.getPath();
1044: String uri = path.substring(uriRoot.length());
1045: ext = files[i].substring(files[i]
1046: .lastIndexOf('.') + 1);
1047: if (getExtensions().contains(ext)
1048: || jspConfig.isJspPage(uri)) {
1049: pages.add(path);
1050: }
1051: }
1052: }
1053: }
1054: }
1055: }
1056:
1057: /**
1058: * Executes the compilation.
1059: *
1060: * @throws JasperException If an error occurs
1061: */
1062: public void execute() throws JasperException {
1063: if (log.isDebugEnabled()) {
1064: log.debug("execute() starting for " + pages.size()
1065: + " pages.");
1066: }
1067:
1068: try {
1069: if (uriRoot == null) {
1070: if (pages.size() == 0) {
1071: throw new JasperException(Localizer
1072: .getMessage("jsp.error.jspc.missingTarget"));
1073: }
1074: String firstJsp = (String) pages.get(0);
1075: File firstJspF = new File(firstJsp);
1076: if (!firstJspF.exists()) {
1077: throw new JasperException(Localizer.getMessage(
1078: "jspc.error.fileDoesNotExist", firstJsp));
1079: }
1080: locateUriRoot(firstJspF);
1081: }
1082:
1083: if (uriRoot == null) {
1084: throw new JasperException(Localizer
1085: .getMessage("jsp.error.jspc.no_uriroot"));
1086: }
1087:
1088: if (context == null) {
1089: initServletContext();
1090: }
1091:
1092: // No explicit pages, we'll process all .jsp in the webapp
1093: if (pages.size() == 0) {
1094: scanFiles(new File(uriRoot));
1095: }
1096:
1097: File uriRootF = new File(uriRoot);
1098: if (!uriRootF.exists() || !uriRootF.isDirectory()) {
1099: throw new JasperException(Localizer
1100: .getMessage("jsp.error.jspc.uriroot_not_dir"));
1101: }
1102:
1103: initWebXml();
1104:
1105: Iterator iter = pages.iterator();
1106: while (iter.hasNext()) {
1107: String nextjsp = iter.next().toString();
1108: File fjsp = new File(nextjsp);
1109: if (!fjsp.isAbsolute()) {
1110: fjsp = new File(uriRootF, nextjsp);
1111: }
1112: if (!fjsp.exists()) {
1113: if (log.isWarnEnabled()) {
1114: log.warn(Localizer.getMessage(
1115: "jspc.error.fileDoesNotExist", fjsp
1116: .toString()));
1117: }
1118: continue;
1119: }
1120: String s = fjsp.getAbsolutePath();
1121: if (s.startsWith(uriRoot)) {
1122: nextjsp = s.substring(uriRoot.length());
1123: }
1124: if (nextjsp.startsWith("." + File.separatorChar)) {
1125: nextjsp = nextjsp.substring(2);
1126: }
1127: processFile(nextjsp);
1128: }
1129:
1130: completeWebXml();
1131:
1132: if (addWebXmlMappings) {
1133: mergeIntoWebXml();
1134: }
1135:
1136: } catch (IOException ioe) {
1137: throw new JasperException(ioe);
1138:
1139: } catch (JasperException je) {
1140: Throwable rootCause = je;
1141: while (rootCause instanceof JasperException
1142: && ((JasperException) rootCause).getRootCause() != null) {
1143: rootCause = ((JasperException) rootCause)
1144: .getRootCause();
1145: }
1146: if (rootCause != je) {
1147: rootCause.printStackTrace();
1148: }
1149: throw je;
1150: } finally {
1151: if (loader != null) {
1152: LogFactory.release(loader);
1153: }
1154: }
1155: }
1156:
1157: // ==================== protected utility methods ====================
1158:
1159: protected String nextArg() {
1160: if ((argPos >= args.length)
1161: || (fullstop = SWITCH_FULL_STOP.equals(args[argPos]))) {
1162: return null;
1163: } else {
1164: return args[argPos++];
1165: }
1166: }
1167:
1168: protected String nextFile() {
1169: if (fullstop)
1170: argPos++;
1171: if (argPos >= args.length) {
1172: return null;
1173: } else {
1174: return args[argPos++];
1175: }
1176: }
1177:
1178: protected void initWebXml() {
1179: try {
1180: if (webxmlLevel >= INC_WEBXML) {
1181: File fmapings = new File(webxmlFile);
1182: mapout = new FileWriter(fmapings);
1183: servletout = new CharArrayWriter();
1184: mappingout = new CharArrayWriter();
1185: } else {
1186: mapout = null;
1187: servletout = null;
1188: mappingout = null;
1189: }
1190: if (webxmlLevel >= ALL_WEBXML) {
1191: mapout
1192: .write(Localizer
1193: .getMessage("jspc.webxml.header"));
1194: mapout.flush();
1195: } else if ((webxmlLevel >= INC_WEBXML)
1196: && !addWebXmlMappings) {
1197: mapout
1198: .write(Localizer
1199: .getMessage("jspc.webinc.header"));
1200: mapout.flush();
1201: }
1202: } catch (IOException ioe) {
1203: mapout = null;
1204: servletout = null;
1205: mappingout = null;
1206: }
1207: }
1208:
1209: protected void completeWebXml() {
1210: if (mapout != null) {
1211: try {
1212: servletout.writeTo(mapout);
1213: mappingout.writeTo(mapout);
1214: if (webxmlLevel >= ALL_WEBXML) {
1215: mapout.write(Localizer
1216: .getMessage("jspc.webxml.footer"));
1217: } else if ((webxmlLevel >= INC_WEBXML)
1218: && !addWebXmlMappings) {
1219: mapout.write(Localizer
1220: .getMessage("jspc.webinc.footer"));
1221: }
1222: mapout.close();
1223: } catch (IOException ioe) {
1224: // noting to do if it fails since we are done with it
1225: }
1226: }
1227: }
1228:
1229: protected void initServletContext() {
1230: try {
1231: context = new JspCServletContext(
1232: new PrintWriter(System.out), new URL("file:"
1233: + uriRoot.replace('\\', '/') + '/'));
1234: tldLocationsCache = new TldLocationsCache(context, true);
1235: } catch (MalformedURLException me) {
1236: System.out.println("**" + me);
1237: }
1238: rctxt = new JspRuntimeContext(context, this );
1239: jspConfig = new JspConfig(context);
1240: tagPluginManager = new TagPluginManager(context);
1241: }
1242:
1243: /**
1244: * Initializes the classloader as/if needed for the given
1245: * compilation context.
1246: *
1247: * @param clctxt The compilation context
1248: * @throws IOException If an error occurs
1249: */
1250: protected void initClassLoader(JspCompilationContext clctxt)
1251: throws IOException {
1252:
1253: classPath = getClassPath();
1254:
1255: ClassLoader jspcLoader = getClass().getClassLoader();
1256:
1257: // TODO add 7Bee class loader
1258:
1259: // Turn the classPath into URLs
1260: ArrayList<URL> urls = new ArrayList<URL>();
1261: StringTokenizer tokenizer = new StringTokenizer(classPath,
1262: File.pathSeparator);
1263: while (tokenizer.hasMoreTokens()) {
1264: String path = tokenizer.nextToken();
1265: try {
1266: File libFile = new File(path);
1267: urls.add(libFile.toURL());
1268: } catch (IOException ioe) {
1269: // Failing a toCanonicalPath on a file that
1270: // exists() should be a JVM regression test,
1271: // therefore we have permission to freak uot
1272: throw new RuntimeException(ioe.toString());
1273: }
1274: }
1275:
1276: File webappBase = new File(uriRoot);
1277: if (webappBase.exists()) {
1278: File classes = new File(webappBase, "/WEB-INF/classes");
1279: try {
1280: if (classes.exists()) {
1281: classPath = classPath + File.pathSeparator
1282: + classes.getCanonicalPath();
1283: urls.add(classes.getCanonicalFile().toURL());
1284: }
1285: } catch (IOException ioe) {
1286: // failing a toCanonicalPath on a file that
1287: // exists() should be a JVM regression test,
1288: // therefore we have permission to freak out
1289: throw new RuntimeException(ioe.toString());
1290: }
1291: File lib = new File(webappBase, "/WEB-INF/lib");
1292: if (lib.exists() && lib.isDirectory()) {
1293: String[] libs = lib.list();
1294: for (int i = 0; i < libs.length; i++) {
1295: if (libs[i].length() < 5)
1296: continue;
1297: String ext = libs[i]
1298: .substring(libs[i].length() - 4);
1299: if (!".jar".equalsIgnoreCase(ext)) {
1300: if (".tld".equalsIgnoreCase(ext)) {
1301: log
1302: .warn("TLD files should not be placed in "
1303: + "/WEB-INF/lib");
1304: }
1305: continue;
1306: }
1307: try {
1308: File libFile = new File(lib, libs[i]);
1309: classPath = classPath + File.pathSeparator
1310: + libFile.getAbsolutePath();
1311: urls.add(libFile.getAbsoluteFile().toURL());
1312: } catch (IOException ioe) {
1313: // failing a toCanonicalPath on a file that
1314: // exists() should be a JVM regression test,
1315: // therefore we have permission to freak out
1316: throw new RuntimeException(ioe.toString());
1317: }
1318: }
1319: }
1320: }
1321:
1322: // What is this ??
1323: urls.add(new File(clctxt.getRealPath("/")).getCanonicalFile()
1324: .toURL());
1325:
1326: URL urlsA[] = new URL[urls.size()];
1327: urls.toArray(urlsA);
1328: loader = new URLClassLoader(urlsA, this .getClass()
1329: .getClassLoader());
1330:
1331: }
1332:
1333: /**
1334: * Find the WEB-INF dir by looking up in the directory tree.
1335: * This is used if no explicit docbase is set, but only files.
1336: * XXX Maybe we should require the docbase.
1337: */
1338: protected void locateUriRoot(File f) {
1339: String tUriBase = uriBase;
1340: if (tUriBase == null) {
1341: tUriBase = "/";
1342: }
1343: try {
1344: if (f.exists()) {
1345: f = new File(f.getAbsolutePath());
1346: while (f != null) {
1347: File g = new File(f, "WEB-INF");
1348: if (g.exists() && g.isDirectory()) {
1349: uriRoot = f.getCanonicalPath();
1350: uriBase = tUriBase;
1351: if (log.isInfoEnabled()) {
1352: log.info(Localizer.getMessage(
1353: "jspc.implicit.uriRoot", uriRoot));
1354: }
1355: break;
1356: }
1357: if (f.exists() && f.isDirectory()) {
1358: tUriBase = "/" + f.getName() + "/" + tUriBase;
1359: }
1360:
1361: String fParent = f.getParent();
1362: if (fParent == null) {
1363: break;
1364: } else {
1365: f = new File(fParent);
1366: }
1367:
1368: // If there is no acceptible candidate, uriRoot will
1369: // remain null to indicate to the CompilerContext to
1370: // use the current working/user dir.
1371: }
1372:
1373: if (uriRoot != null) {
1374: File froot = new File(uriRoot);
1375: uriRoot = froot.getCanonicalPath();
1376: }
1377: }
1378: } catch (IOException ioe) {
1379: // since this is an optional default and a null value
1380: // for uriRoot has a non-error meaning, we can just
1381: // pass straight through
1382: }
1383: }
1384:
1385: /**
1386: * Resolves the relative or absolute pathname correctly
1387: * in both Ant and command-line situations. If Ant launched
1388: * us, we should use the basedir of the current project
1389: * to resolve relative paths.
1390: *
1391: * See Bugzilla 35571.
1392: *
1393: * @param s The file
1394: * @return The file resolved
1395: */
1396: protected File resolveFile(final String s) {
1397: return new File(s);
1398: }
1399: }
|