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.jasper;
19
20 import java.io.BufferedReader;
21 import java.io.CharArrayWriter;
22 import java.io.File;
23 import java.io.FileInputStream;
24 import java.io.FileNotFoundException;
25 import java.io.FileOutputStream;
26 import java.io.FileReader;
27 import java.io.FileWriter;
28 import java.io.IOException;
29 import java.io.PrintWriter;
30 import java.io.Writer;
31 import java.net.MalformedURLException;
32 import java.net.URL;
33 import java.net.URLClassLoader;
34 import java.util.ArrayList;
35 import java.util.Iterator;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.HashMap;
39 import java.util.Stack;
40 import java.util.StringTokenizer;
41 import java.util.Vector;
42
43 import org.apache.jasper.compiler.Compiler;
44 import org.apache.jasper.compiler.JspConfig;
45 import org.apache.jasper.compiler.JspRuntimeContext;
46 import org.apache.jasper.compiler.Localizer;
47 import org.apache.jasper.compiler.TagPluginManager;
48 import org.apache.jasper.compiler.TldLocationsCache;
49 import org.apache.jasper.servlet.JspCServletContext;
50 import org.apache.juli.logging.Log;
51 import org.apache.juli.logging.LogFactory;
52
53 import org.apache.tools.ant.AntClassLoader;
54 import org.apache.tools.ant.Project;
55 import org.apache.tools.ant.util.FileUtils;
56
57 /**
58 * Shell for the jspc compiler. Handles all options associated with the
59 * command line and creates compilation contexts which it then compiles
60 * according to the specified options.
61 *
62 * This version can process files from a _single_ webapp at once, i.e.
63 * a single docbase can be specified.
64 *
65 * It can be used as an Ant task using:
66 * <pre>
67 * <taskdef classname="org.apache.jasper.JspC" name="jasper2" >
68 * <classpath>
69 * <pathelement location="${java.home}/../lib/tools.jar"/>
70 * <fileset dir="${ENV.CATALINA_HOME}/server/lib">
71 * <include name="*.jar"/>
72 * </fileset>
73 * <fileset dir="${ENV.CATALINA_HOME}/common/lib">
74 * <include name="*.jar"/>
75 * </fileset>
76 * <path refid="myjars"/>
77 * </classpath>
78 * </taskdef>
79 *
80 * <jasper2 verbose="0"
81 * package="my.package"
82 * uriroot="${webapps.dir}/${webapp.name}"
83 * webXmlFragment="${build.dir}/generated_web.xml"
84 * outputDir="${webapp.dir}/${webapp.name}/WEB-INF/src/my/package" />
85 * </pre>
86 *
87 * @author Danno Ferrin
88 * @author Pierre Delisle
89 * @author Costin Manolache
90 * @author Yoav Shapira
91 */
92 public class JspC implements Options {
93
94 public static final String DEFAULT_IE_CLASS_ID =
95 "clsid:8AD9C840-044E-11D1-B3E9-00805F499D93";
96
97 // Logger
98 protected static Log log = LogFactory.getLog(JspC.class);
99
100 protected static final String SWITCH_VERBOSE = "-v";
101 protected static final String SWITCH_HELP = "-help";
102 protected static final String SWITCH_OUTPUT_DIR = "-d";
103 protected static final String SWITCH_PACKAGE_NAME = "-p";
104 protected static final String SWITCH_CACHE = "-cache";
105 protected static final String SWITCH_CLASS_NAME = "-c";
106 protected static final String SWITCH_FULL_STOP = "--";
107 protected static final String SWITCH_COMPILE = "-compile";
108 protected static final String SWITCH_SOURCE = "-source";
109 protected static final String SWITCH_TARGET = "-target";
110 protected static final String SWITCH_URI_BASE = "-uribase";
111 protected static final String SWITCH_URI_ROOT = "-uriroot";
112 protected static final String SWITCH_FILE_WEBAPP = "-webapp";
113 protected static final String SWITCH_WEBAPP_INC = "-webinc";
114 protected static final String SWITCH_WEBAPP_XML = "-webxml";
115 protected static final String SWITCH_MAPPED = "-mapped";
116 protected static final String SWITCH_XPOWERED_BY = "-xpoweredBy";
117 protected static final String SWITCH_TRIM_SPACES = "-trimSpaces";
118 protected static final String SWITCH_CLASSPATH = "-classpath";
119 protected static final String SWITCH_DIE = "-die";
120 protected static final String SWITCH_POOLING = "-poolingEnabled";
121 protected static final String SWITCH_ENCODING = "-javaEncoding";
122 protected static final String SWITCH_SMAP = "-smap";
123 protected static final String SWITCH_DUMP_SMAP = "-dumpsmap";
124
125 protected static final String SHOW_SUCCESS ="-s";
126 protected static final String LIST_ERRORS = "-l";
127 protected static final int INC_WEBXML = 10;
128 protected static final int ALL_WEBXML = 20;
129 protected static final int DEFAULT_DIE_LEVEL = 1;
130 protected static final int NO_DIE_LEVEL = 0;
131
132 protected static final String[] insertBefore =
133 { "</web-app>", "<servlet-mapping>", "<session-config>",
134 "<mime-mapping>", "<welcome-file-list>", "<error-page>", "<taglib>",
135 "<resource-env-ref>", "<resource-ref>", "<security-constraint>",
136 "<login-config>", "<security-role>", "<env-entry>", "<ejb-ref>",
137 "<ejb-local-ref>" };
138
139 protected static int die;
140 protected String classPath = null;
141 protected URLClassLoader loader = null;
142 protected boolean trimSpaces = false;
143 protected boolean genStringAsCharArray = false;
144 protected boolean xpoweredBy;
145 protected boolean mappedFile = false;
146 protected boolean poolingEnabled = true;
147 protected File scratchDir;
148 protected String ieClassId = DEFAULT_IE_CLASS_ID;
149 protected String targetPackage;
150 protected String targetClassName;
151 protected String uriBase;
152 protected String uriRoot;
153 protected Project project;
154 protected int dieLevel;
155 protected boolean helpNeeded = false;
156 protected boolean compile = false;
157 protected boolean smapSuppressed = true;
158 protected boolean smapDumped = false;
159 protected boolean caching = true;
160 protected Map cache = new HashMap();
161
162 protected String compiler = null;
163
164 protected String compilerTargetVM = "1.4";
165 protected String compilerSourceVM = "1.4";
166
167 protected boolean classDebugInfo = true;
168
169 /**
170 * Throw an exception if there's a compilation error, or swallow it.
171 * Default is true to preserve old behavior.
172 */
173 protected boolean failOnError = true;
174
175 /**
176 * The file extensions to be handled as JSP files.
177 * Default list is .jsp and .jspx.
178 */
179 protected List extensions;
180
181 /**
182 * The pages.
183 */
184 protected List pages = new Vector();
185
186 /**
187 * Needs better documentation, this data member does.
188 * True by default.
189 */
190 protected boolean errorOnUseBeanInvalidClassAttribute = true;
191
192 /**
193 * The java file encoding. Default
194 * is UTF-8. Added per bugzilla 19622.
195 */
196 protected String javaEncoding = "UTF-8";
197
198 // Generation of web.xml fragments
199 protected String webxmlFile;
200 protected int webxmlLevel;
201 protected boolean addWebXmlMappings = false;
202
203 protected Writer mapout;
204 protected CharArrayWriter servletout;
205 protected CharArrayWriter mappingout;
206
207 /**
208 * The servlet context.
209 */
210 protected JspCServletContext context;
211
212 /**
213 * The runtime context.
214 * Maintain a dummy JspRuntimeContext for compiling tag files.
215 */
216 protected JspRuntimeContext rctxt;
217
218 /**
219 * Cache for the TLD locations
220 */
221 protected TldLocationsCache tldLocationsCache = null;
222
223 protected JspConfig jspConfig = null;
224 protected TagPluginManager tagPluginManager = null;
225
226 protected boolean verbose = false;
227 protected boolean listErrors = false;
228 protected boolean showSuccess = false;
229 protected int argPos;
230 protected boolean fullstop = false;
231 protected String args[];
232
233 public static void main(String arg[]) {
234 if (arg.length == 0) {
235 System.out.println(Localizer.getMessage("jspc.usage"));
236 } else {
237 try {
238 JspC jspc = new JspC();
239 jspc.setArgs(arg);
240 if (jspc.helpNeeded) {
241 System.out.println(Localizer.getMessage("jspc.usage"));
242 } else {
243 jspc.execute();
244 }
245 } catch (JasperException je) {
246 System.err.println(je);
247 if (die != NO_DIE_LEVEL) {
248 System.exit(die);
249 }
250 }
251 }
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(SWITCH_URI_ROOT)) {
278 setUriroot( nextArg());
279 } else if (tok.equals(SWITCH_FILE_WEBAPP)) {
280 setUriroot( nextArg());
281 } else if ( tok.equals( SHOW_SUCCESS ) ) {
282 showSuccess = true;
283 } else if ( tok.equals( LIST_ERRORS ) ) {
284 listErrors = true;
285 } else if (tok.equals(SWITCH_WEBAPP_INC)) {
286 webxmlFile = nextArg();
287 if (webxmlFile != null) {
288 webxmlLevel = INC_WEBXML;
289 }
290 } else if (tok.equals(SWITCH_WEBAPP_XML)) {
291 webxmlFile = nextArg();
292 if (webxmlFile != null) {
293 webxmlLevel = ALL_WEBXML;
294 }
295 } else if (tok.equals(SWITCH_MAPPED)) {
296 mappedFile = true;
297 } else if (tok.equals(SWITCH_XPOWERED_BY)) {
298 xpoweredBy = true;
299 } else if (tok.equals(SWITCH_TRIM_SPACES)) {
300 setTrimSpaces(true);
301 } else if (tok.equals(SWITCH_CACHE)) {
302 tok = nextArg();
303 if ("false".equals(tok)) {
304 caching = false;
305 } else {
306 caching = true;
307 }
308 } else if (tok.equals(SWITCH_CLASSPATH)) {
309 setClassPath(nextArg());
310 } else if (tok.startsWith(SWITCH_DIE)) {
311 try {
312 dieLevel = Integer.parseInt(
313 tok.substring(SWITCH_DIE.length()));
314 } catch (NumberFormatException nfe) {
315 dieLevel = DEFAULT_DIE_LEVEL;
316 }
317 die = dieLevel;
318 } else if (tok.equals(SWITCH_HELP)) {
319 helpNeeded = true;
320 } else if (tok.equals(SWITCH_POOLING)) {
321 tok = nextArg();
322 if ("false".equals(tok)) {
323 poolingEnabled = false;
324 } else {
325 poolingEnabled = true;
326 }
327 } else if (tok.equals(SWITCH_ENCODING)) {
328 setJavaEncoding(nextArg());
329 } else if (tok.equals(SWITCH_SOURCE)) {
330 setCompilerSourceVM(nextArg());
331 } else if (tok.equals(SWITCH_TARGET)) {
332 setCompilerTargetVM(nextArg());
333 } else if (tok.equals(SWITCH_SMAP)) {
334 smapSuppressed = false;
335 } else if (tok.equals(SWITCH_DUMP_SMAP)) {
336 smapDumped = true;
337 } else {
338 if (tok.startsWith("-")) {
339 throw new JasperException("Unrecognized option: " + tok +
340 ". Use -help for help.");
341 }
342 if (!fullstop) {
343 argPos--;
344 }
345 // Start treating the rest as JSP Pages
346 break;
347 }
348 }
349
350 // Add all extra arguments to the list of files
351 while( true ) {
352 String file = nextFile();
353 if( file==null ) {
354 break;
355 }
356 pages.add( file );
357 }
358 }
359
360 public boolean getKeepGenerated() {
361 // isn't this why we are running jspc?
362 return true;
363 }
364
365 public boolean getTrimSpaces() {
366 return trimSpaces;
367 }
368
369 public void setTrimSpaces(boolean ts) {
370 this.trimSpaces = ts;
371 }
372
373 public boolean isPoolingEnabled() {
374 return poolingEnabled;
375 }
376
377 public void setPoolingEnabled(boolean poolingEnabled) {
378 this.poolingEnabled = poolingEnabled;
379 }
380
381 public boolean isXpoweredBy() {
382 return xpoweredBy;
383 }
384
385 public void setXpoweredBy(boolean xpoweredBy) {
386 this.xpoweredBy = xpoweredBy;
387 }
388
389 public boolean getDisplaySourceFragment() {
390 return true;
391 }
392
393 public boolean getErrorOnUseBeanInvalidClassAttribute() {
394 return errorOnUseBeanInvalidClassAttribute;
395 }
396
397 public void setErrorOnUseBeanInvalidClassAttribute(boolean b) {
398 errorOnUseBeanInvalidClassAttribute = b;
399 }
400
401 public int getTagPoolSize() {
402 return Constants.MAX_POOL_SIZE;
403 }
404
405 /**
406 * Are we supporting HTML mapped servlets?
407 */
408 public boolean getMappedFile() {
409 return mappedFile;
410 }
411
412 // Off-line compiler, no need for security manager
413 public Object getProtectionDomain() {
414 return null;
415 }
416
417 /**
418 * @deprecated
419 */
420 @Deprecated
421 public boolean getSendErrorToClient() {
422 return true;
423 }
424
425 public void setClassDebugInfo( boolean b ) {
426 classDebugInfo=b;
427 }
428
429 public boolean getClassDebugInfo() {
430 // compile with debug info
431 return classDebugInfo;
432 }
433
434 /**
435 * @see Options#isCaching()
436 */
437 public boolean isCaching() {
438 return caching;
439 }
440
441 /**
442 * @see Options#isCaching()
443 */
444 public void setCaching(boolean caching) {
445 this.caching = caching;
446 }
447
448 /**
449 * @see Options#getCache()
450 */
451 public Map getCache() {
452 return cache;
453 }
454
455 /**
456 * Background compilation check intervals in seconds
457 */
458 public int getCheckInterval() {
459 return 0;
460 }
461
462 /**
463 * Modification test interval.
464 */
465 public int getModificationTestInterval() {
466 return 0;
467 }
468
469 /**
470 * Is Jasper being used in development mode?
471 */
472 public boolean getDevelopment() {
473 return false;
474 }
475
476 /**
477 * Is the generation of SMAP info for JSR45 debuggin suppressed?
478 */
479 public boolean isSmapSuppressed() {
480 return smapSuppressed;
481 }
482
483 /**
484 * Set smapSuppressed flag.
485 */
486 public void setSmapSuppressed(boolean smapSuppressed) {
487 this.smapSuppressed = smapSuppressed;
488 }
489
490
491 /**
492 * Should SMAP info for JSR45 debugging be dumped to a file?
493 */
494 public boolean isSmapDumped() {
495 return smapDumped;
496 }
497
498 /**
499 * Set smapSuppressed flag.
500 */
501 public void setSmapDumped(boolean smapDumped) {
502 this.smapDumped = smapDumped;
503 }
504
505
506 /**
507 * Determines whether text strings are to be generated as char arrays,
508 * which improves performance in some cases.
509 *
510 * @param genStringAsCharArray true if text strings are to be generated as
511 * char arrays, false otherwise
512 */
513 public void setGenStringAsCharArray(boolean genStringAsCharArray) {
514 this.genStringAsCharArray = genStringAsCharArray;
515 }
516
517 /**
518 * Indicates whether text strings are to be generated as char arrays.
519 *
520 * @return true if text strings are to be generated as char arrays, false
521 * otherwise
522 */
523 public boolean genStringAsCharArray() {
524 return genStringAsCharArray;
525 }
526
527 /**
528 * Sets the class-id value to be sent to Internet Explorer when using
529 * <jsp:plugin> tags.
530 *
531 * @param ieClassId Class-id value
532 */
533 public void setIeClassId(String ieClassId) {
534 this.ieClassId = ieClassId;
535 }
536
537 /**
538 * Gets the class-id value that is sent to Internet Explorer when using
539 * <jsp:plugin> tags.
540 *
541 * @return Class-id value
542 */
543 public String getIeClassId() {
544 return ieClassId;
545 }
546
547 public File getScratchDir() {
548 return scratchDir;
549 }
550
551 public Class getJspCompilerPlugin() {
552 // we don't compile, so this is meanlingless
553 return null;
554 }
555
556 public String getJspCompilerPath() {
557 // we don't compile, so this is meanlingless
558 return null;
559 }
560
561 /**
562 * Compiler to use.
563 */
564 public String getCompiler() {
565 return compiler;
566 }
567
568 public void setCompiler(String c) {
569 compiler=c;
570 }
571
572 /**
573 * Compiler class name to use.
574 */
575 public String getCompilerClassName() {
576 return null;
577 }
578
579 /**
580 * @see Options#getCompilerTargetVM
581 */
582 public String getCompilerTargetVM() {
583 return compilerTargetVM;
584 }
585
586 public void setCompilerTargetVM(String vm) {
587 compilerTargetVM = vm;
588 }
589
590 /**
591 * @see Options#getCompilerSourceVM()
592 */
593 public String getCompilerSourceVM() {
594 return compilerSourceVM;
595 }
596
597 /**
598 * @see Options#getCompilerSourceVM()
599 */
600 public void setCompilerSourceVM(String vm) {
601 compilerSourceVM = vm;
602 }
603
604 public TldLocationsCache getTldLocationsCache() {
605 return tldLocationsCache;
606 }
607
608 /**
609 * Returns the encoding to use for
610 * java files. The default is UTF-8.
611 *
612 * @return String The encoding
613 */
614 public String getJavaEncoding() {
615 return javaEncoding;
616 }
617
618 /**
619 * Sets the encoding to use for
620 * java files.
621 *
622 * @param encodingName The name, e.g. "UTF-8"
623 */
624 public void setJavaEncoding(String encodingName) {
625 javaEncoding = encodingName;
626 }
627
628 public boolean getFork() {
629 return false;
630 }
631
632 public String getClassPath() {
633 if( classPath != null )
634 return classPath;
635 return System.getProperty("java.class.path");
636 }
637
638 public void setClassPath(String s) {
639 classPath=s;
640 }
641
642 /**
643 * Returns the list of file extensions
644 * that are treated as JSP files.
645 *
646 * @return The list of extensions
647 */
648 public List getExtensions() {
649 return extensions;
650 }
651
652 /**
653 * Adds the given file extension to the
654 * list of extensions handled as JSP files.
655 *
656 * @param extension The extension to add, e.g. "myjsp"
657 */
658 protected void addExtension(final String extension) {
659 if(extension != null) {
660 if(extensions == null) {
661 extensions = new Vector();
662 }
663
664 extensions.add(extension);
665 }
666 }
667
668 /**
669 * Sets the project.
670 *
671 * @param theProject The project
672 */
673 public void setProject(final Project theProject) {
674 project = theProject;
675 }
676
677 /**
678 * Returns the project: may be null if not running
679 * inside an Ant project.
680 *
681 * @return The project
682 */
683 public Project getProject() {
684 return project;
685 }
686
687 /**
688 * Base dir for the webapp. Used to generate class names and resolve
689 * includes
690 */
691 public void setUriroot( String s ) {
692 if( s==null ) {
693 uriRoot = s;
694 return;
695 }
696 try {
697 uriRoot = resolveFile(s).getCanonicalPath();
698 } catch( Exception ex ) {
699 uriRoot = s;
700 }
701 }
702
703 /**
704 * Parses comma-separated list of JSP files to be processed. If the argument
705 * is null, nothing is done.
706 *
707 * <p>Each file is interpreted relative to uriroot, unless it is absolute,
708 * in which case it must start with uriroot.</p>
709 *
710 * @param jspFiles Comma-separated list of JSP files to be processed
711 */
712 public void setJspFiles(final String jspFiles) {
713 if(jspFiles == null) {
714 return;
715 }
716
717 StringTokenizer tok = new StringTokenizer(jspFiles, ",");
718 while (tok.hasMoreTokens()) {
719 pages.add(tok.nextToken());
720 }
721 }
722
723 /**
724 * Sets the compile flag.
725 *
726 * @param b Flag value
727 */
728 public void setCompile( final boolean b ) {
729 compile = b;
730 }
731
732 /**
733 * Sets the verbosity level. The actual number doesn't
734 * matter: if it's greater than zero, the verbose flag will
735 * be true.
736 *
737 * @param level Positive means verbose
738 */
739 public void setVerbose( final int level ) {
740 if (level > 0) {
741 verbose = true;
742 showSuccess = true;
743 listErrors = true;
744 }
745 }
746
747 public void setValidateXml( boolean b ) {
748 org.apache.jasper.xmlparser.ParserUtils.validating=b;
749 }
750
751 public void setListErrors( boolean b ) {
752 listErrors = b;
753 }
754
755 public void setOutputDir( String s ) {
756 if( s!= null ) {
757 scratchDir = resolveFile(s).getAbsoluteFile();
758 } else {
759 scratchDir=null;
760 }
761 }
762
763 public void setPackage( String p ) {
764 targetPackage=p;
765 }
766
767 /**
768 * Class name of the generated file ( without package ).
769 * Can only be used if a single file is converted.
770 * XXX Do we need this feature ?
771 */
772 public void setClassName( String p ) {
773 targetClassName=p;
774 }
775
776 /**
777 * File where we generate a web.xml fragment with the class definitions.
778 */
779 public void setWebXmlFragment( String s ) {
780 webxmlFile=resolveFile(s).getAbsolutePath();
781 webxmlLevel=INC_WEBXML;
782 }
783
784 /**
785 * File where we generate a complete web.xml with the class definitions.
786 */
787 public void setWebXml( String s ) {
788 webxmlFile=resolveFile(s).getAbsolutePath();
789 webxmlLevel=ALL_WEBXML;
790 }
791
792 public void setAddWebXmlMappings(boolean b) {
793 addWebXmlMappings = b;
794 }
795
796 /**
797 * Set the option that throws an exception in case of a compilation error.
798 */
799 public void setFailOnError(final boolean b) {
800 failOnError = b;
801 }
802
803 public boolean getFailOnError() {
804 return failOnError;
805 }
806
807 /**
808 * Obtain JSP configuration informantion specified in web.xml.
809 */
810 public JspConfig getJspConfig() {
811 return jspConfig;
812 }
813
814 public TagPluginManager getTagPluginManager() {
815 return tagPluginManager;
816 }
817
818 public void generateWebMapping( String file, JspCompilationContext clctxt )
819 throws IOException
820 {
821 if (log.isDebugEnabled()) {
822 log.debug("Generating web mapping for file " + file
823 + " using compilation context " + clctxt);
824 }
825
826 String className = clctxt.getServletClassName();
827 String packageName = clctxt.getServletPackageName();
828
829 String thisServletName;
830 if ("".equals(packageName)) {
831 thisServletName = className;
832 } else {
833 thisServletName = packageName + '.' + className;
834 }
835
836 if (servletout != null) {
837 servletout.write("\n <servlet>\n <servlet-name>");
838 servletout.write(thisServletName);
839 servletout.write("</servlet-name>\n <servlet-class>");
840 servletout.write(thisServletName);
841 servletout.write("</servlet-class>\n </servlet>\n");
842 }
843 if (mappingout != null) {
844 mappingout.write("\n <servlet-mapping>\n <servlet-name>");
845 mappingout.write(thisServletName);
846 mappingout.write("</servlet-name>\n <url-pattern>");
847 mappingout.write(file.replace('\\', '/'));
848 mappingout.write("</url-pattern>\n </servlet-mapping>\n");
849
850 }
851 }
852
853 /**
854 * Include the generated web.xml inside the webapp's web.xml.
855 */
856 protected void mergeIntoWebXml() throws IOException {
857
858 File webappBase = new File(uriRoot);
859 File webXml = new File(webappBase, "WEB-INF/web.xml");
860 File webXml2 = new File(webappBase, "WEB-INF/web2.xml");
861 String insertStartMarker =
862 Localizer.getMessage("jspc.webinc.insertStart");
863 String insertEndMarker =
864 Localizer.getMessage("jspc.webinc.insertEnd");
865
866 BufferedReader reader = new BufferedReader(new FileReader(webXml));
867 BufferedReader fragmentReader =
868 new BufferedReader(new FileReader(webxmlFile));
869 PrintWriter writer = new PrintWriter(new FileWriter(webXml2));
870
871 // Insert the <servlet> and <servlet-mapping> declarations
872 int pos = -1;
873 String line = null;
874 while (true) {
875 line = reader.readLine();
876 if (line == null) {
877 break;
878 }
879 // Skip anything previously generated by JSPC
880 if (line.indexOf(insertStartMarker) >= 0) {
881 while (true) {
882 line = reader.readLine();
883 if (line == null) {
884 return;
885 }
886 if (line.indexOf(insertEndMarker) >= 0) {
887 line = reader.readLine();
888 line = reader.readLine();
889 if (line == null) {
890 return;
891 }
892 break;
893 }
894 }
895 }
896 for (int i = 0; i < insertBefore.length; i++) {
897 pos = line.indexOf(insertBefore[i]);
898 if (pos >= 0)
899 break;
900 }
901 if (pos >= 0) {
902 writer.print(line.substring(0, pos));
903 break;
904 } else {
905 writer.println(line);
906 }
907 }
908
909 writer.println(insertStartMarker);
910 while (true) {
911 String line2 = fragmentReader.readLine();
912 if (line2 == null) {
913 writer.println();
914 break;
915 }
916 writer.println(line2);
917 }
918 writer.println(insertEndMarker);
919 writer.println();
920
921 for (int i = 0; i < pos; i++) {
922 writer.print(" ");
923 }
924 writer.println(line.substring(pos));
925
926 while (true) {
927 line = reader.readLine();
928 if (line == null) {
929 break;
930 }
931 writer.println(line);
932 }
933 writer.close();
934
935 reader.close();
936 fragmentReader.close();
937
938 FileInputStream fis = new FileInputStream(webXml2);
939 FileOutputStream fos = new FileOutputStream(webXml);
940
941 byte buf[] = new byte[512];
942 while (true) {
943 int n = fis.read(buf);
944 if (n < 0) {
945 break;
946 }
947 fos.write(buf, 0, n);
948 }
949
950 fis.close();
951 fos.close();
952
953 webXml2.delete();
954 (new File(webxmlFile)).delete();
955
956 }
957
958 protected void processFile(String file)
959 throws JasperException
960 {
961 if (log.isDebugEnabled()) {
962 log.debug("Processing file: " + file);
963 }
964
965 ClassLoader originalClassLoader = null;
966
967 try {
968 // set up a scratch/output dir if none is provided
969 if (scratchDir == null) {
970 String temp = System.getProperty("java.io.tmpdir");
971 if (temp == null) {
972 temp = "";
973 }
974 scratchDir = new File(new File(temp).getAbsolutePath());
975 }
976
977 String jspUri=file.replace('\\','/');
978 JspCompilationContext clctxt = new JspCompilationContext
979 ( jspUri, false, this, context, null, rctxt );
980
981 /* Override the defaults */
982 if ((targetClassName != null) && (targetClassName.length() > 0)) {
983 clctxt.setServletClassName(targetClassName);
984 targetClassName = null;
985 }
986 if (targetPackage != null) {
987 clctxt.setServletPackageName(targetPackage);
988 }
989
990 originalClassLoader = Thread.currentThread().getContextClassLoader();
991 if( loader==null ) {
992 initClassLoader( clctxt );
993 }
994 Thread.currentThread().setContextClassLoader(loader);
995
996 clctxt.setClassLoader(loader);
997 clctxt.setClassPath(classPath);
998
999 Compiler clc = clctxt.createCompiler();
1000
1001 // If compile is set, generate both .java and .class, if
1002 // .jsp file is newer than .class file;
1003 // Otherwise only generate .java, if .jsp file is newer than
1004 // the .java file
1005 if( clc.isOutDated(compile) ) {
1006 if (log.isDebugEnabled()) {
1007 log.debug(jspUri + " is out dated, compiling...");
1008 }
1009
1010 clc.compile(compile, true);
1011 }
1012
1013 // Generate mapping
1014 generateWebMapping( file, clctxt );
1015 if ( showSuccess ) {
1016 log.info( "Built File: " + file );
1017 }
1018
1019 } catch (JasperException je) {
1020 Throwable rootCause = je;
1021 while (rootCause instanceof JasperException
1022 && ((JasperException) rootCause).getRootCause() != null) {
1023 rootCause = ((JasperException) rootCause).getRootCause();
1024 }
1025 if (rootCause != je) {
1026 log.error(Localizer.getMessage("jspc.error.generalException",
1027 file),
1028 rootCause);
1029 }
1030
1031 // Bugzilla 35114.
1032 if(getFailOnError()) {
1033 throw je;
1034 } else {
1035 log.error(je.getMessage());
1036 }
1037
1038 } catch (Exception e) {
1039 if ((e instanceof FileNotFoundException) && log.isWarnEnabled()) {
1040 log.warn(Localizer.getMessage("jspc.error.fileDoesNotExist",
1041 e.getMessage()));
1042 }
1043 throw new JasperException(e);
1044 } finally {
1045 if(originalClassLoader != null) {
1046 Thread.currentThread().setContextClassLoader(originalClassLoader);
1047 }
1048 }
1049 }
1050
1051 /**
1052 * Locate all jsp files in the webapp. Used if no explicit
1053 * jsps are specified.
1054 */
1055 public void scanFiles( File base ) throws JasperException {
1056 Stack<String> dirs = new Stack<String>();
1057 dirs.push(base.toString());
1058
1059 // Make sure default extensions are always included
1060 if ((getExtensions() == null) || (getExtensions().size() < 2)) {
1061 addExtension("jsp");
1062 addExtension("jspx");
1063 }
1064
1065 while (!dirs.isEmpty()) {
1066 String s = dirs.pop();
1067 File f = new File(s);
1068 if (f.exists() && f.isDirectory()) {
1069 String[] files = f.list();
1070 String ext;
1071 for (int i = 0; (files != null) && i < files.length; i++) {
1072 File f2 = new File(s, files[i]);
1073 if (f2.isDirectory()) {
1074 dirs.push(f2.getPath());
1075 } else {
1076 String path = f2.getPath();
1077 String uri = path.substring(uriRoot.length());
1078 ext = files[i].substring(files[i].lastIndexOf('.') +1);
1079 if (getExtensions().contains(ext) ||
1080 jspConfig.isJspPage(uri)) {
1081 pages.add(path);
1082 }
1083 }
1084 }
1085 }
1086 }
1087 }
1088
1089 /**
1090 * Executes the compilation.
1091 *
1092 * @throws JasperException If an error occurs
1093 */
1094 public void execute() throws JasperException {
1095 if(log.isDebugEnabled()) {
1096 log.debug("execute() starting for " + pages.size() + " pages.");
1097 }
1098
1099 try {
1100 if (uriRoot == null) {
1101 if( pages.size() == 0 ) {
1102 throw new JasperException(
1103 Localizer.getMessage("jsp.error.jspc.missingTarget"));
1104 }
1105 String firstJsp = (String) pages.get( 0 );
1106 File firstJspF = new File( firstJsp );
1107 if (!firstJspF.exists()) {
1108 throw new JasperException(
1109 Localizer.getMessage("jspc.error.fileDoesNotExist",
1110 firstJsp));
1111 }
1112 locateUriRoot( firstJspF );
1113 }
1114
1115 if (uriRoot == null) {
1116 throw new JasperException(
1117 Localizer.getMessage("jsp.error.jspc.no_uriroot"));
1118 }
1119
1120 if( context==null ) {
1121 initServletContext();
1122 }
1123
1124 // No explicit pages, we'll process all .jsp in the webapp
1125 if (pages.size() == 0) {
1126 scanFiles( new File( uriRoot ));
1127 }
1128
1129 File uriRootF = new File(uriRoot);
1130 if (!uriRootF.exists() || !uriRootF.isDirectory()) {
1131 throw new JasperException(
1132 Localizer.getMessage("jsp.error.jspc.uriroot_not_dir"));
1133 }
1134
1135 initWebXml();
1136
1137 Iterator iter = pages.iterator();
1138 while (iter.hasNext()) {
1139 String nextjsp = iter.next().toString();
1140 File fjsp = new File(nextjsp);
1141 if (!fjsp.isAbsolute()) {
1142 fjsp = new File(uriRootF, nextjsp);
1143 }
1144 if (!fjsp.exists()) {
1145 if (log.isWarnEnabled()) {
1146 log.warn
1147 (Localizer.getMessage
1148 ("jspc.error.fileDoesNotExist", fjsp.toString()));
1149 }
1150 continue;
1151 }
1152 String s = fjsp.getAbsolutePath();
1153 if (s.startsWith(uriRoot)) {
1154 nextjsp = s.substring(uriRoot.length());
1155 }
1156 if (nextjsp.startsWith("." + File.separatorChar)) {
1157 nextjsp = nextjsp.substring(2);
1158 }
1159 processFile(nextjsp);
1160 }
1161
1162 completeWebXml();
1163
1164 if (addWebXmlMappings) {
1165 mergeIntoWebXml();
1166 }
1167
1168 } catch (IOException ioe) {
1169 throw new JasperException(ioe);
1170
1171 } catch (JasperException je) {
1172 Throwable rootCause = je;
1173 while (rootCause instanceof JasperException
1174 && ((JasperException) rootCause).getRootCause() != null) {
1175 rootCause = ((JasperException) rootCause).getRootCause();
1176 }
1177 if (rootCause != je) {
1178 rootCause.printStackTrace();
1179 }
1180 throw je;
1181 } finally {
1182 if (loader != null) {
1183 LogFactory.release(loader);
1184 }
1185 }
1186 }
1187
1188 // ==================== protected utility methods ====================
1189
1190 protected String nextArg() {
1191 if ((argPos >= args.length)
1192 || (fullstop = SWITCH_FULL_STOP.equals(args[argPos]))) {
1193 return null;
1194 } else {
1195 return args[argPos++];
1196 }
1197 }
1198
1199 protected String nextFile() {
1200 if (fullstop) argPos++;
1201 if (argPos >= args.length) {
1202 return null;
1203 } else {
1204 return args[argPos++];
1205 }
1206 }
1207
1208 protected void initWebXml() {
1209 try {
1210 if (webxmlLevel >= INC_WEBXML) {
1211 File fmapings = new File(webxmlFile);
1212 mapout = new FileWriter(fmapings);
1213 servletout = new CharArrayWriter();
1214 mappingout = new CharArrayWriter();
1215 } else {
1216 mapout = null;
1217 servletout = null;
1218 mappingout = null;
1219 }
1220 if (webxmlLevel >= ALL_WEBXML) {
1221 mapout.write(Localizer.getMessage("jspc.webxml.header"));
1222 mapout.flush();
1223 } else if ((webxmlLevel>= INC_WEBXML) && !addWebXmlMappings) {
1224 mapout.write(Localizer.getMessage("jspc.webinc.header"));
1225 mapout.flush();
1226 }
1227 } catch (IOException ioe) {
1228 mapout = null;
1229 servletout = null;
1230 mappingout = null;
1231 }
1232 }
1233
1234 protected void completeWebXml() {
1235 if (mapout != null) {
1236 try {
1237 servletout.writeTo(mapout);
1238 mappingout.writeTo(mapout);
1239 if (webxmlLevel >= ALL_WEBXML) {
1240 mapout.write(Localizer.getMessage("jspc.webxml.footer"));
1241 } else if ((webxmlLevel >= INC_WEBXML) && !addWebXmlMappings) {
1242 mapout.write(Localizer.getMessage("jspc.webinc.footer"));
1243 }
1244 mapout.close();
1245 } catch (IOException ioe) {
1246 // noting to do if it fails since we are done with it
1247 }
1248 }
1249 }
1250
1251 protected void initServletContext() {
1252 try {
1253 context =new JspCServletContext
1254 (new PrintWriter(System.out),
1255 new URL("file:" + uriRoot.replace('\\','/') + '/'));
1256 tldLocationsCache = new TldLocationsCache(context, true);
1257 } catch (MalformedURLException me) {
1258 System.out.println("**" + me);
1259 }
1260 rctxt = new JspRuntimeContext(context, this);
1261 jspConfig = new JspConfig(context);
1262 tagPluginManager = new TagPluginManager(context);
1263 }
1264
1265 /**
1266 * Initializes the classloader as/if needed for the given
1267 * compilation context.
1268 *
1269 * @param clctxt The compilation context
1270 * @throws IOException If an error occurs
1271 */
1272 protected void initClassLoader(JspCompilationContext clctxt)
1273 throws IOException {
1274
1275 classPath = getClassPath();
1276
1277 ClassLoader jspcLoader = getClass().getClassLoader();
1278 if (jspcLoader instanceof AntClassLoader) {
1279 classPath += File.pathSeparator
1280 + ((AntClassLoader) jspcLoader).getClasspath();
1281 }
1282
1283 // Turn the classPath into URLs
1284 ArrayList<URL> urls = new ArrayList<URL>();
1285 StringTokenizer tokenizer = new StringTokenizer(classPath,
1286 File.pathSeparator);
1287 while (tokenizer.hasMoreTokens()) {
1288 String path = tokenizer.nextToken();
1289 try {
1290 File libFile = new File(path);
1291 urls.add(libFile.toURL());
1292 } catch (IOException ioe) {
1293 // Failing a toCanonicalPath on a file that
1294 // exists() should be a JVM regression test,
1295 // therefore we have permission to freak uot
1296 throw new RuntimeException(ioe.toString());
1297 }
1298 }
1299
1300 File webappBase = new File(uriRoot);
1301 if (webappBase.exists()) {
1302 File classes = new File(webappBase, "/WEB-INF/classes");
1303 try {
1304 if (classes.exists()) {
1305 classPath = classPath + File.pathSeparator
1306 + classes.getCanonicalPath();
1307 urls.add(classes.getCanonicalFile().toURL());
1308 }
1309 } catch (IOException ioe) {
1310 // failing a toCanonicalPath on a file that
1311 // exists() should be a JVM regression test,
1312 // therefore we have permission to freak out
1313 throw new RuntimeException(ioe.toString());
1314 }
1315 File lib = new File(webappBase, "/WEB-INF/lib");
1316 if (lib.exists() && lib.isDirectory()) {
1317 String[] libs = lib.list();
1318 for (int i = 0; i < libs.length; i++) {
1319 if( libs[i].length() <5 ) continue;
1320 String ext=libs[i].substring( libs[i].length() - 4 );
1321 if (! ".jar".equalsIgnoreCase(ext)) {
1322 if (".tld".equalsIgnoreCase(ext)) {
1323 log.warn("TLD files should not be placed in "
1324 + "/WEB-INF/lib");
1325 }
1326 continue;
1327 }
1328 try {
1329 File libFile = new File(lib, libs[i]);
1330 classPath = classPath + File.pathSeparator
1331 + libFile.getAbsolutePath();
1332 urls.add(libFile.getAbsoluteFile().toURL());
1333 } catch (IOException ioe) {
1334 // failing a toCanonicalPath on a file that
1335 // exists() should be a JVM regression test,
1336 // therefore we have permission to freak out
1337 throw new RuntimeException(ioe.toString());
1338 }
1339 }
1340 }
1341 }
1342
1343 // What is this ??
1344 urls.add(new File(clctxt.getRealPath("/")).getCanonicalFile().toURL());
1345
1346 URL urlsA[]=new URL[urls.size()];
1347 urls.toArray(urlsA);
1348 loader = new URLClassLoader(urlsA, this.getClass().getClassLoader());
1349
1350 }
1351
1352 /**
1353 * Find the WEB-INF dir by looking up in the directory tree.
1354 * This is used if no explicit docbase is set, but only files.
1355 * XXX Maybe we should require the docbase.
1356 */
1357 protected void locateUriRoot( File f ) {
1358 String tUriBase = uriBase;
1359 if (tUriBase == null) {
1360 tUriBase = "/";
1361 }
1362 try {
1363 if (f.exists()) {
1364 f = new File(f.getAbsolutePath());
1365 while (f != null) {
1366 File g = new File(f, "WEB-INF");
1367 if (g.exists() && g.isDirectory()) {
1368 uriRoot = f.getCanonicalPath();
1369 uriBase = tUriBase;
1370 if (log.isInfoEnabled()) {
1371 log.info(Localizer.getMessage(
1372 "jspc.implicit.uriRoot",
1373 uriRoot));
1374 }
1375 break;
1376 }
1377 if (f.exists() && f.isDirectory()) {
1378 tUriBase = "/" + f.getName() + "/" + tUriBase;
1379 }
1380
1381 String fParent = f.getParent();
1382 if (fParent == null) {
1383 break;
1384 } else {
1385 f = new File(fParent);
1386 }
1387
1388 // If there is no acceptible candidate, uriRoot will
1389 // remain null to indicate to the CompilerContext to
1390 // use the current working/user dir.
1391 }
1392
1393 if (uriRoot != null) {
1394 File froot = new File(uriRoot);
1395 uriRoot = froot.getCanonicalPath();
1396 }
1397 }
1398 } catch (IOException ioe) {
1399 // since this is an optional default and a null value
1400 // for uriRoot has a non-error meaning, we can just
1401 // pass straight through
1402 }
1403 }
1404
1405 /**
1406 * Resolves the relative or absolute pathname correctly
1407 * in both Ant and command-line situations. If Ant launched
1408 * us, we should use the basedir of the current project
1409 * to resolve relative paths.
1410 *
1411 * See Bugzilla 35571.
1412 *
1413 * @param s The file
1414 * @return The file resolved
1415 */
1416 protected File resolveFile(final String s) {
1417 if(getProject() == null) {
1418 // Note FileUtils.getFileUtils replaces FileUtils.newFileUtils in Ant 1.6.3
1419 return FileUtils.newFileUtils().resolveFile(null, s);
1420 } else {
1421 return FileUtils.newFileUtils().resolveFile(getProject().getBaseDir(), s);
1422 }
1423 }
1424 }