View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  package org.apache.struts2.jasper;
19  
20  import com.opensymphony.xwork2.util.finder.ClassLoaderInterface;
21  import com.opensymphony.xwork2.util.logging.Logger;
22  import com.opensymphony.xwork2.util.logging.LoggerFactory;
23  import org.apache.commons.logging.LogFactory;
24  import org.apache.struts2.jasper.compiler.Compiler;
25  import org.apache.struts2.jasper.compiler.*;
26  import org.apache.struts2.jasper.servlet.JspCServletContext;
27  
28  import java.io.*;
29  import java.net.URL;
30  import java.net.URLClassLoader;
31  import java.util.*;
32  
33  /***
34   * Shell for the jspc compiler.  Handles all options associated with the
35   * command line and creates compilation contexts which it then compiles
36   * according to the specified options.
37   * <p/>
38   * This version can process files from a _single_ webapp at once, i.e.
39   * a single docbase can be specified.
40   * <p/>
41   * It can be used as an Ant task using:
42   * <pre>
43   *   &lt;taskdef classname="org.apache.struts.jasperJspC" name="jasper2" &gt;
44   *      &lt;classpath&gt;
45   *          &lt;pathelement location="${java.home}/../lib/tools.jar"/&gt;
46   *          &lt;fileset dir="${ENV.CATALINA_HOME}/server/lib"&gt;
47   *              &lt;include name="*.jar"/&gt;
48   *          &lt;/fileset&gt;
49   *          &lt;fileset dir="${ENV.CATALINA_HOME}/common/lib"&gt;
50   *              &lt;include name="*.jar"/&gt;
51   *          &lt;/fileset&gt;
52   *          &lt;path refid="myjars"/&gt;
53   *       &lt;/classpath&gt;
54   *  &lt;/taskdef&gt;
55   * <p/>
56   *  &lt;jasper2 verbose="0"
57   *           package="my.package"
58   *           uriroot="${webapps.dir}/${webapp.name}"
59   *           webXmlFragment="${build.dir}/generated_web.xml"
60   *           outputDir="${webapp.dir}/${webapp.name}/WEB-INF/src/my/package" /&gt;
61   * </pre>
62   *
63   * @author Danno Ferrin
64   * @author Pierre Delisle
65   * @author Costin Manolache
66   * @author Yoav Shapira
67   */
68  public class JspC implements Options {
69      /***
70       * The default Microsoft Internet Explorer class ID.
71       */
72      public static final String DEFAULT_IE_CLASS_ID =
73              "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
74  
75      /***
76       * Logger (set by constructor.)
77       */
78      private Logger log;
79  
80      private static final String SWITCH_VERBOSE = "-v";
81      private static final String SWITCH_HELP = "-help";
82      private static final String SWITCH_QUIET = "-q";
83      private static final String SWITCH_OUTPUT_DIR = "-d";
84      private static final String SWITCH_IE_CLASS_ID = "-ieplugin";
85      private static final String SWITCH_PACKAGE_NAME = "-p";
86      private static final String SWITCH_CACHE = "-cache";
87      private static final String SWITCH_CLASS_NAME = "-c";
88      private static final String SWITCH_FULL_STOP = "--";
89      private static final String SWITCH_COMPILE = "-compile";
90      private static final String SWITCH_SOURCE = "-source";
91      private static final String SWITCH_TARGET = "-target";
92      private static final String SWITCH_URI_BASE = "-uribase";
93      private static final String SWITCH_URI_ROOT = "-uriroot";
94      private static final String SWITCH_FILE_WEBAPP = "-webapp";
95      private static final String SWITCH_WEBAPP_INC = "-webinc";
96      private static final String SWITCH_WEBAPP_XML = "-webxml";
97      private static final String SWITCH_MAPPED = "-mapped";
98      private static final String SWITCH_XPOWERED_BY = "-xpoweredBy";
99      private static final String SWITCH_TRIM_SPACES = "-trimSpaces";
100     private static final String SWITCH_CLASSPATH = "-classpath";
101     private static final String SWITCH_DIE = "-die";
102     private static final String SWITCH_POOLING = "-poolingEnabled";
103     private static final String SWITCH_ENCODING = "-javaEncoding";
104     private static final String SWITCH_SMAP = "-smap";
105     private static final String SWITCH_DUMP_SMAP = "-dumpsmap";
106 
107     private static final String SHOW_SUCCESS = "-s";
108     private static final String LIST_ERRORS = "-l";
109     private static final int NO_WEBXML = 0;
110     private static final int INC_WEBXML = 10;
111     private static final int ALL_WEBXML = 20;
112     private static final int DEFAULT_DIE_LEVEL = 1;
113     private static final int NO_DIE_LEVEL = 0;
114 
115     private static final String[] insertBefore =
116             {"</web-app>", "<servlet-mapping>", "<session-config>",
117                     "<mime-mapping>", "<welcome-file-list>", "<error-page>", "<taglib>",
118                     "<resource-env-ref>", "<resource-ref>", "<security-constraint>",
119                     "<login-config>", "<security-role>", "<env-entry>", "<ejb-ref>",
120                     "<ejb-local-ref>"};
121 
122     private static int die;
123     private String classPath = null;
124     private URLClassLoader loader = null;
125     private boolean trimSpaces = false;
126     private boolean genStringAsCharArray = false;
127     private boolean xpoweredBy;
128     private boolean mappedFile = false;
129     private boolean poolingEnabled = true;
130     private File scratchDir;
131     private String ieClassId = DEFAULT_IE_CLASS_ID;
132     private String targetPackage;
133     private String targetClassName;
134     private String uriBase;
135     //private String uriRoot;
136     private int dieLevel;
137     private boolean helpNeeded = false;
138     private boolean compile = false;
139     private boolean smapSuppressed = true;
140     private boolean smapDumped = false;
141     private boolean caching = true;
142     private Map cache = new HashMap();
143 
144     private String compiler = null;
145 
146     private ClassLoaderInterface classLoaderInterface;
147 
148     private String compilerTargetVM = "1.4";
149     private String compilerSourceVM = "1.4";
150 
151     private boolean classDebugInfo = true;
152 
153     /***
154      * Throw an exception if there's a compilation error, or swallow it.
155      * Default is true to preserve old behavior.
156      */
157     private boolean failOnError = true;
158 
159     /***
160      * The file extensions to be handled as JSP files.
161      * Default list is .jsp and .jspx.
162      */
163     private List extensions;
164 
165     /***
166      * The pages.
167      */
168     private List pages = new Vector();
169 
170     /***
171      * Needs better documentation, this data member does.
172      * True by default.
173      */
174     private boolean errorOnUseBeanInvalidClassAttribute = true;
175 
176     /***
177      * The java file encoding.  Default
178      * is UTF-8.  Added per bugzilla 19622.
179      */
180     private String javaEncoding = "UTF-8";
181 
182     // Generation of web.xml fragments
183     private String webxmlFile;
184     private int webxmlLevel;
185 
186     private Writer mapout;
187     private CharArrayWriter servletout;
188     private CharArrayWriter mappingout;
189 
190     /***
191      * The servlet context.
192      */
193     private JspCServletContext context;
194 
195     /***
196      * The runtime context.
197      * Maintain a dummy JspRuntimeContext for compiling tag files.
198      */
199     private JspRuntimeContext rctxt;
200 
201     /***
202      * Cache for the TLD locations
203      */
204     private TldLocationsCache tldLocationsCache = null;
205 
206     private JspConfig jspConfig = null;
207     private TagPluginManager tagPluginManager = null;
208 
209     private boolean verbose = false;
210     private boolean listErrors = false;
211     private boolean showSuccess = false;
212     private int argPos;
213     private boolean fullstop = false;
214     private String args[];
215     private String sourceCode;
216 
217     public static void main(String arg[]) {
218         if (arg.length == 0) {
219             System.out.println(Localizer.getMessage("jspc.usage"));
220         } else {
221             try {
222                 JspC jspc = new JspC();
223                 jspc.setArgs(arg);
224                 if (jspc.helpNeeded) {
225                     System.out.println(Localizer.getMessage("jspc.usage"));
226                 } else {
227                     jspc.execute();
228                 }
229             } catch (JasperException je) {
230                 System.err.println(je);
231                 //System.err.println(je.getMessage());
232                 if (die != NO_DIE_LEVEL) {
233                     System.exit(die);
234                 }
235             }
236         }
237     }
238 
239     /***
240      * Constructor.
241      */
242     public JspC() {
243         log = LoggerFactory.getLogger(getClass());
244     }
245 
246     public String getSourceCode() {
247         return sourceCode;
248     }
249 
250     public void setClassLoaderInterface(ClassLoaderInterface classLoaderInterface) {
251         this.classLoaderInterface = classLoaderInterface;
252     }
253 
254     public void setArgs(String[] arg) throws JasperException {
255         args = arg;
256         String tok;
257 
258         dieLevel = NO_DIE_LEVEL;
259         die = dieLevel;
260 
261         while ((tok = nextArg()) != null) {
262             if (tok.equals(SWITCH_VERBOSE)) {
263                 verbose = true;
264                 showSuccess = true;
265                 listErrors = true;
266             } else if (tok.equals(SWITCH_OUTPUT_DIR)) {
267                 tok = nextArg();
268                 setOutputDir(tok);
269             } else if (tok.equals(SWITCH_PACKAGE_NAME)) {
270                 targetPackage = nextArg();
271             } else if (tok.equals(SWITCH_COMPILE)) {
272                 compile = true;
273             } else if (tok.equals(SWITCH_CLASS_NAME)) {
274                 targetClassName = nextArg();
275             } else if (tok.equals(SWITCH_URI_BASE)) {
276                 uriBase = nextArg();
277             } else if (tok.equals(SHOW_SUCCESS)) {
278                 showSuccess = true;
279             } else if (tok.equals(LIST_ERRORS)) {
280                 listErrors = true;
281             } else if (tok.equals(SWITCH_WEBAPP_INC)) {
282                 webxmlFile = nextArg();
283                 if (webxmlFile != null) {
284                     webxmlLevel = INC_WEBXML;
285                 }
286             } else if (tok.equals(SWITCH_WEBAPP_XML)) {
287                 webxmlFile = nextArg();
288                 if (webxmlFile != null) {
289                     webxmlLevel = ALL_WEBXML;
290                 }
291             } else if (tok.equals(SWITCH_MAPPED)) {
292                 mappedFile = true;
293             } else if (tok.equals(SWITCH_XPOWERED_BY)) {
294                 xpoweredBy = true;
295             } else if (tok.equals(SWITCH_TRIM_SPACES)) {
296                 setTrimSpaces(true);
297             } else if (tok.equals(SWITCH_CACHE)) {
298                 tok = nextArg();
299                 if ("false".equals(tok)) {
300                     caching = false;
301                 } else {
302                     caching = true;
303                 }
304             } else if (tok.equals(SWITCH_CLASSPATH)) {
305                 setClassPath(nextArg());
306             } else if (tok.startsWith(SWITCH_DIE)) {
307                 try {
308                     dieLevel = Integer.parseInt(
309                             tok.substring(SWITCH_DIE.length()));
310                 } catch (NumberFormatException nfe) {
311                     dieLevel = DEFAULT_DIE_LEVEL;
312                 }
313                 die = dieLevel;
314             } else if (tok.equals(SWITCH_HELP)) {
315                 helpNeeded = true;
316             } else if (tok.equals(SWITCH_POOLING)) {
317                 tok = nextArg();
318                 if ("false".equals(tok)) {
319                     poolingEnabled = false;
320                 } else {
321                     poolingEnabled = true;
322                 }
323             } else if (tok.equals(SWITCH_ENCODING)) {
324                 setJavaEncoding(nextArg());
325             } else if (tok.equals(SWITCH_SOURCE)) {
326                 setCompilerSourceVM(nextArg());
327             } else if (tok.equals(SWITCH_TARGET)) {
328                 setCompilerTargetVM(nextArg());
329             } else if (tok.equals(SWITCH_SMAP)) {
330                 smapSuppressed = false;
331             } else if (tok.equals(SWITCH_DUMP_SMAP)) {
332                 smapDumped = true;
333             } else {
334                 if (tok.startsWith("-")) {
335                     throw new JasperException("Unrecognized option: " + tok +
336                             ".  Use -help for help.");
337                 }
338                 if (!fullstop) {
339                     argPos--;
340                 }
341                 // Start treating the rest as JSP Pages
342                 break;
343             }
344         }
345 
346         // Add all extra arguments to the list of files
347         while (true) {
348             String file = nextFile();
349             if (file == null) {
350                 break;
351             }
352             pages.add(file);
353         }
354     }
355 
356     public boolean getKeepGenerated() {
357         // isn't this why we are running jspc?
358         return true;
359     }
360 
361     public boolean getTrimSpaces() {
362         return trimSpaces;
363     }
364 
365     public void setTrimSpaces(boolean ts) {
366         this.trimSpaces = ts;
367     }
368 
369     public boolean isPoolingEnabled() {
370         return poolingEnabled;
371     }
372 
373     public void setPoolingEnabled(boolean poolingEnabled) {
374         this.poolingEnabled = poolingEnabled;
375     }
376 
377     public boolean isXpoweredBy() {
378         return xpoweredBy;
379     }
380 
381     public void setXpoweredBy(boolean xpoweredBy) {
382         this.xpoweredBy = xpoweredBy;
383     }
384 
385     public boolean getErrorOnUseBeanInvalidClassAttribute() {
386         return errorOnUseBeanInvalidClassAttribute;
387     }
388 
389     public void setErrorOnUseBeanInvalidClassAttribute(boolean b) {
390         errorOnUseBeanInvalidClassAttribute = b;
391     }
392 
393     public int getTagPoolSize() {
394         return Constants.MAX_POOL_SIZE;
395     }
396 
397     /***
398      * Are we supporting HTML mapped servlets?
399      */
400     public boolean getMappedFile() {
401         return mappedFile;
402     }
403 
404     // Off-line compiler, no need for security manager
405     public Object getProtectionDomain() {
406         return null;
407     }
408 
409     public boolean getSendErrorToClient() {
410         // implied send to System.err
411         return true;
412     }
413 
414     public void setClassDebugInfo(boolean b) {
415         classDebugInfo = b;
416     }
417 
418     public boolean getClassDebugInfo() {
419         // compile with debug info
420         return classDebugInfo;
421     }
422 
423     /***
424      * @see Options#isCaching()
425      */
426     public boolean isCaching() {
427         return caching;
428     }
429 
430     /***
431      * @see Options#isCaching()
432      */
433     public void setCaching(boolean caching) {
434         this.caching = caching;
435     }
436 
437     /***
438      * @see Options#getCache()
439      */
440     public Map getCache() {
441         return cache;
442     }
443 
444     /***
445      * Background compilation check intervals in seconds
446      */
447     public int getCheckInterval() {
448         return 0;
449     }
450 
451     /***
452      * Modification test interval.
453      */
454     public int getModificationTestInterval() {
455         return 0;
456     }
457 
458     /***
459      * Is Jasper being used in development mode?
460      */
461     public boolean getDevelopment() {
462         return false;
463     }
464 
465     /***
466      * Is the generation of SMAP info for JSR45 debuggin suppressed?
467      */
468     public boolean isSmapSuppressed() {
469         return smapSuppressed;
470     }
471 
472     /***
473      * Set smapSuppressed flag.
474      */
475     public void setSmapSuppressed(boolean smapSuppressed) {
476         this.smapSuppressed = smapSuppressed;
477     }
478 
479 
480     /***
481      * Should SMAP info for JSR45 debugging be dumped to a file?
482      */
483     public boolean isSmapDumped() {
484         return smapDumped;
485     }
486 
487     /***
488      * Set smapSuppressed flag.
489      */
490     public void setSmapDumped(boolean smapDumped) {
491         this.smapDumped = smapDumped;
492     }
493 
494 
495     /***
496      * Determines whether text strings are to be generated as char arrays,
497      * which improves performance in some cases.
498      *
499      * @param genStringAsCharArray true if text strings are to be generated as
500      *                             char arrays, false otherwise
501      */
502     public void setGenStringAsCharArray(boolean genStringAsCharArray) {
503         this.genStringAsCharArray = genStringAsCharArray;
504     }
505 
506     /***
507      * Indicates whether text strings are to be generated as char arrays.
508      *
509      * @return true if text strings are to be generated as char arrays, false
510      *         otherwise
511      */
512     public boolean genStringAsCharArray() {
513         return genStringAsCharArray;
514     }
515 
516     /***
517      * Sets the class-id value to be sent to Internet Explorer when using
518      * <jsp:plugin> tags.
519      *
520      * @param ieClassId Class-id value
521      */
522     public void setIeClassId(String ieClassId) {
523         this.ieClassId = ieClassId;
524     }
525 
526     /***
527      * Gets the class-id value that is sent to Internet Explorer when using
528      * <jsp:plugin> tags.
529      *
530      * @return Class-id value
531      */
532     public String getIeClassId() {
533         return ieClassId;
534     }
535 
536     public File getScratchDir() {
537         return scratchDir;
538     }
539 
540     public Class getJspCompilerPlugin() {
541         // we don't compile, so this is meanlingless
542         return null;
543     }
544 
545     public String getJspCompilerPath() {
546         // we don't compile, so this is meanlingless
547         return null;
548     }
549 
550     /***
551      * Compiler to use.
552      */
553     public String getCompiler() {
554         return compiler;
555     }
556 
557     public void setCompiler(String c) {
558         compiler = c;
559     }
560 
561     /***
562      * @see Options#getCompilerTargetVM
563      */
564     public String getCompilerTargetVM() {
565         return compilerTargetVM;
566     }
567 
568     public void setCompilerTargetVM(String vm) {
569         compilerTargetVM = vm;
570     }
571 
572     /***
573      * @see Options#getCompilerSourceVM()
574      */
575     public String getCompilerSourceVM() {
576         return compilerSourceVM;
577     }
578 
579     /***
580      * @see Options#getCompilerSourceVM()
581      */
582     public void setCompilerSourceVM(String vm) {
583         compilerSourceVM = vm;
584     }
585 
586     public TldLocationsCache getTldLocationsCache() {
587         return tldLocationsCache;
588     }
589 
590     /***
591      * Returns the encoding to use for
592      * java files.  The default is UTF-8.
593      *
594      * @return String The encoding
595      */
596     public String getJavaEncoding() {
597         return javaEncoding;
598     }
599 
600     /***
601      * Sets the encoding to use for
602      * java files.
603      *
604      * @param encodingName The name, e.g. "UTF-8"
605      */
606     public void setJavaEncoding(String encodingName) {
607         javaEncoding = encodingName;
608     }
609 
610     public boolean getFork() {
611         return false;
612     }
613 
614     public String getClassPath() {
615         if (classPath != null)
616             return classPath;
617         return System.getProperty("java.class.path");
618     }
619 
620     public void setClassPath(String s) {
621         classPath = s;
622     }
623 
624     /***
625      * Returns the list of file extensions
626      * that are treated as JSP files.
627      *
628      * @return The list of extensions
629      */
630     public List getExtensions() {
631         return extensions;
632     }
633 
634     /***
635      * Adds the given file extension to the
636      * list of extensions handled as JSP files.
637      *
638      * @param extension The extension to add, e.g. "myjsp"
639      */
640     protected void addExtension(final String extension) {
641         if (extension != null) {
642             if (extensions == null) {
643                 extensions = new Vector();
644             }
645 
646             extensions.add(extension);
647         }
648     }
649 
650     /***
651      * Parses comma-separated list of JSP files to be processed.  If the argument
652      * is null, nothing is done.
653      * <p/>
654      * <p>Each file is interpreted relative to uriroot, unless it is absolute,
655      * in which case it must start with uriroot.</p>
656      *
657      * @param jspFiles Comma-separated list of JSP files to be processed
658      */
659     public void setJspFiles(final String jspFiles) {
660         if (jspFiles == null) {
661             return;
662         }
663 
664         StringTokenizer tok = new StringTokenizer(jspFiles, ",");
665         while (tok.hasMoreTokens()) {
666             pages.add(tok.nextToken());
667         }
668     }
669 
670     /***
671      * Sets the compile flag.
672      *
673      * @param b Flag value
674      */
675     public void setCompile(final boolean b) {
676         compile = b;
677     }
678 
679     /***
680      * Sets the verbosity level.  The actual number doesn't
681      * matter: if it's greater than zero, the verbose flag will
682      * be true.
683      *
684      * @param level Positive means verbose
685      */
686     public void setVerbose(final int level) {
687         if (level > 0) {
688             verbose = true;
689             showSuccess = true;
690             listErrors = true;
691         }
692     }
693 
694     public void setValidateXml(boolean b) {
695         org.apache.struts2.jasper.xmlparser.ParserUtils.validating = b;
696     }
697 
698     public void setListErrors(boolean b) {
699         listErrors = b;
700     }
701 
702     public void setOutputDir(String s) {
703         if (s != null) {
704             scratchDir = resolveFile(s).getAbsoluteFile();
705         } else {
706             scratchDir = null;
707         }
708     }
709 
710     public void setPackage(String p) {
711         targetPackage = p;
712     }
713 
714     /***
715      * Class name of the generated file ( without package ).
716      * Can only be used if a single file is converted.
717      * XXX Do we need this feature ?
718      */
719     public void setClassName(String p) {
720         targetClassName = p;
721     }
722 
723     /***
724      * File where we generate a web.xml fragment with the class definitions.
725      */
726     public void setWebXmlFragment(String s) {
727         webxmlFile = resolveFile(s).getAbsolutePath();
728         webxmlLevel = INC_WEBXML;
729     }
730 
731     /***
732      * File where we generate a complete web.xml with the class definitions.
733      */
734     public void setWebXml(String s) {
735         webxmlFile = resolveFile(s).getAbsolutePath();
736         webxmlLevel = ALL_WEBXML;
737     }
738 
739     /***
740      * Set the option that throws an exception in case of a compilation error.
741      */
742     public void setFailOnError(final boolean b) {
743         failOnError = b;
744     }
745 
746     public boolean getFailOnError() {
747         return failOnError;
748     }
749 
750     /***
751      * Obtain JSP configuration informantion specified in web.xml.
752      */
753     public JspConfig getJspConfig() {
754         return jspConfig;
755     }
756 
757     public TagPluginManager getTagPluginManager() {
758         return tagPluginManager;
759     }
760 
761     public void generateWebMapping(String file, JspCompilationContext clctxt)
762             throws IOException {
763         if (log.isDebugEnabled()) {
764             log.debug("Generating web mapping for file " + file
765                     + " using compilation context " + clctxt);
766         }
767 
768         String className = clctxt.getServletClassName();
769         String packageName = clctxt.getServletPackageName();
770 
771         String thisServletName;
772         if ("".equals(packageName)) {
773             thisServletName = className;
774         } else {
775             thisServletName = packageName + '.' + className;
776         }
777 
778         if (servletout != null) {
779             servletout.write("\n    <servlet>\n        <servlet-name>");
780             servletout.write(thisServletName);
781             servletout.write("</servlet-name>\n        <servlet-class>");
782             servletout.write(thisServletName);
783             servletout.write("</servlet-class>\n    </servlet>\n");
784         }
785         if (mappingout != null) {
786             mappingout.write("\n    <servlet-mapping>\n        <servlet-name>");
787             mappingout.write(thisServletName);
788             mappingout.write("</servlet-name>\n        <url-pattern>");
789             mappingout.write(file.replace('//', '/'));
790             mappingout.write("</url-pattern>\n    </servlet-mapping>\n");
791 
792         }
793     }
794 
795     private void processFile(String file)
796             throws JasperException {
797         if (log.isDebugEnabled()) {
798             log.debug("Processing file: " + file);
799         }
800 
801         ClassLoader originalClassLoader = null;
802 
803         try {
804             // set up a scratch/output dir if none is provided
805             if (scratchDir == null) {
806                 String temp = System.getProperty("java.io.tmpdir");
807                 if (temp == null) {
808                     temp = "";
809                 }
810                 scratchDir = new File(new File(temp).getAbsolutePath());
811             }
812 
813             String jspUri = file.replace('//', '/');
814             JspCompilationContext clctxt = new JspCompilationContext
815                     (jspUri, false, this, context, null, rctxt, classLoaderInterface);
816 
817             /* Override the defaults */
818             if ((targetClassName != null) && (targetClassName.length() > 0)) {
819                 clctxt.setServletClassName(targetClassName);
820                 targetClassName = null;
821             }
822             if (targetPackage != null) {
823                 clctxt.setServletPackageName(targetPackage);
824             }
825 
826             originalClassLoader = Thread.currentThread().getContextClassLoader();
827             if (loader == null) {
828                 initClassLoader(clctxt);
829             }
830             Thread.currentThread().setContextClassLoader(loader);
831 
832             clctxt.setClassLoader(loader);
833             clctxt.setClassPath(classPath);
834 
835             Compiler clc = clctxt.createCompiler();
836 
837             // If compile is set, generate both .java and .class, if
838             // .jsp file is newer than .class file;
839             // Otherwise only generate .java, if .jsp file is newer than
840             // the .java file
841             if (clc.isOutDated(compile)) {
842                 if (log.isDebugEnabled()) {
843                     log.debug(jspUri + " is out dated, compiling...");
844                 }
845 
846                 clc.compile(compile, true);
847             }
848 
849             // Generate mapping
850             generateWebMapping(file, clctxt);
851             if (showSuccess) {
852                 log.info("Built File: " + file);
853             }
854 
855             this.sourceCode = clctxt.getSourceCode();
856 
857         } catch (JasperException je) {
858             Throwable rootCause = je;
859             while (rootCause instanceof JasperException
860                     && ((JasperException) rootCause).getRootCause() != null) {
861                 rootCause = ((JasperException) rootCause).getRootCause();
862             }
863             if (rootCause != je) {
864                 log.error(Localizer.getMessage("jspc.error.generalException",
865                         file),
866                         rootCause);
867             }
868 
869             // Bugzilla 35114.
870             if (getFailOnError()) {
871                 throw je;
872             } else {
873                 log.error(je.getMessage(), je);
874                 ;
875             }
876 
877         } catch (Exception e) {
878             if ((e instanceof FileNotFoundException) && log.isWarnEnabled()) {
879                 log.warn(Localizer.getMessage("jspc.error.fileDoesNotExist",
880                         e.getMessage()));
881             }
882             throw new JasperException(e);
883         } finally {
884             if (originalClassLoader != null) {
885                 Thread.currentThread().setContextClassLoader(originalClassLoader);
886             }
887         }
888     }
889 
890     public Set<String> getTldAbsolutePaths() {
891         return tldLocationsCache.getAbsolutePathsOfLocations();
892     }
893 
894 
895     /***
896      * Executes the compilation.
897      *
898      * @throws JasperException If an error occurs
899      */
900     public void execute() throws JasperException {
901         if (log.isDebugEnabled()) {
902             log.debug("execute() starting for " + pages.size() + " pages.");
903         }
904 
905         try {
906             if (context == null) {
907                 initServletContext();
908             }
909 
910             initWebXml();
911 
912             Iterator iter = pages.iterator();
913             while (iter.hasNext()) {
914                 String nextjsp = iter.next().toString();
915 
916                 processFile(nextjsp);
917             }
918 
919             completeWebXml();
920         } catch (JasperException je) {
921             Throwable rootCause = je;
922             while (rootCause instanceof JasperException
923                     && ((JasperException) rootCause).getRootCause() != null) {
924                 rootCause = ((JasperException) rootCause).getRootCause();
925             }
926             if (rootCause != je) {
927                 rootCause.printStackTrace();
928             }
929             throw je;
930         } finally {
931             if (loader != null) {
932                 LogFactory.release(loader);
933             }
934         }
935     }
936 
937     // ==================== Private utility methods ====================
938 
939     private String nextArg() {
940         if ((argPos >= args.length)
941                 || (fullstop = SWITCH_FULL_STOP.equals(args[argPos]))) {
942             return null;
943         } else {
944             return args[argPos++];
945         }
946     }
947 
948     private String nextFile() {
949         if (fullstop) argPos++;
950         if (argPos >= args.length) {
951             return null;
952         } else {
953             return args[argPos++];
954         }
955     }
956 
957     private void initWebXml() {
958         try {
959             if (webxmlLevel >= INC_WEBXML) {
960                 File fmapings = new File(webxmlFile);
961                 mapout = new FileWriter(fmapings);
962                 servletout = new CharArrayWriter();
963                 mappingout = new CharArrayWriter();
964             } else {
965                 mapout = null;
966                 servletout = null;
967                 mappingout = null;
968             }
969             if (webxmlLevel >= ALL_WEBXML) {
970                 mapout.write(Localizer.getMessage("jspc.webxml.header"));
971                 mapout.flush();
972             } else if ((webxmlLevel >= INC_WEBXML)) {
973                 mapout.write(Localizer.getMessage("jspc.webinc.header"));
974                 mapout.flush();
975             }
976         } catch (IOException ioe) {
977             mapout = null;
978             servletout = null;
979             mappingout = null;
980         }
981     }
982 
983     private void completeWebXml() {
984         if (mapout != null) {
985             try {
986                 servletout.writeTo(mapout);
987                 mappingout.writeTo(mapout);
988                 if (webxmlLevel >= ALL_WEBXML) {
989                     mapout.write(Localizer.getMessage("jspc.webxml.footer"));
990                 } else if ((webxmlLevel >= INC_WEBXML)) {
991                     mapout.write(Localizer.getMessage("jspc.webinc.footer"));
992                 }
993                 mapout.close();
994             } catch (IOException ioe) {
995                 // noting to do if it fails since we are done with it
996             }
997         }
998     }
999 
1000     private void initServletContext() {
1001 
1002         context = new JspCServletContext
1003                 (new PrintWriter(System.out),
1004                         classLoaderInterface);
1005         tldLocationsCache = new TldLocationsCache(context, true);
1006 
1007         rctxt = new JspRuntimeContext(context, this);
1008         jspConfig = new JspConfig(context);
1009         tagPluginManager = new TagPluginManager(context);
1010     }
1011 
1012     /***
1013      * Initializes the classloader as/if needed for the given
1014      * compilation context.
1015      *
1016      * @param clctxt The compilation context
1017      * @throws IOException If an error occurs
1018      */
1019     private void initClassLoader(JspCompilationContext clctxt)
1020             throws IOException {
1021 
1022         classPath = getClassPath();
1023 
1024         ClassLoader jspcLoader = getClass().getClassLoader();
1025         // Turn the classPath into URLs
1026         ArrayList urls = new ArrayList();
1027         StringTokenizer tokenizer = new StringTokenizer(classPath,
1028                 File.pathSeparator);
1029         while (tokenizer.hasMoreTokens()) {
1030             String path = tokenizer.nextToken();
1031             try {
1032                 File libFile = new File(path);
1033                 urls.add(libFile.toURL());
1034             } catch (IOException ioe) {
1035                 // Failing a toCanonicalPath on a file that
1036                 // exists() should be a JVM regression test,
1037                 // therefore we have permission to freak uot
1038                 throw new RuntimeException(ioe.toString());
1039             }
1040         }
1041 
1042         //TODO: add .tld files to the URLCLassLoader
1043 
1044         URL urlsA[] = new URL[urls.size()];
1045         urls.toArray(urlsA);
1046         loader = new URLClassLoader(urlsA, this.getClass().getClassLoader());
1047 
1048     }
1049 
1050     /***
1051      * Resolves the relative or absolute pathname correctly
1052      * in both Ant and command-line situations.  If Ant launched
1053      * us, we should use the basedir of the current project
1054      * to resolve relative paths.
1055      * <p/>
1056      * See Bugzilla 35571.
1057      *
1058      * @param s The file
1059      * @return The file resolved
1060      */
1061     protected File resolveFile(final String s) {
1062         //TODO: what to do with this
1063         return new File(s);
1064     }
1065 }