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