001: /*
002: * Copyright (c) 1998-2008 Caucho Technology -- all rights reserved
003: *
004: * This file is part of Resin(R) Open Source
005: *
006: * Each copy or derived work must preserve the copyright notice and this
007: * notice unmodified.
008: *
009: * Resin Open Source is free software; you can redistribute it and/or modify
010: * it under the terms of the GNU General Public License as published by
011: * the Free Software Foundation; either version 2 of the License, or
012: * (at your option) any later version.
013: *
014: * Resin Open Source is distributed in the hope that it will be useful,
015: * but WITHOUT ANY WARRANTY; without even the implied warranty of
016: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
017: * of NON-INFRINGEMENT. See the GNU General Public License for more
018: * details.
019: *
020: * You should have received a copy of the GNU General Public License
021: * along with Resin Open Source; if not, write to the
022: *
023: * Free Software Foundation, Inc.
024: * 59 Temple Place, Suite 330
025: * Boston, MA 02111-1307 USA
026: *
027: * @author Scott Ferguson
028: */
029:
030: package com.caucho.jsp;
031:
032: import com.caucho.config.*;
033: import com.caucho.config.program.ConfigProgram;
034: import com.caucho.config.program.ContainerProgram;
035: import com.caucho.config.types.*;
036: import com.caucho.java.*;
037: import com.caucho.jsp.cfg.JspConfig;
038: import com.caucho.jsp.cfg.JspPropertyGroup;
039: import com.caucho.jsp.cfg.JspTaglib;
040: import com.caucho.loader.CompilingLoader;
041: import com.caucho.loader.DirectoryLoader;
042: import com.caucho.loader.DynamicClassLoader;
043: import com.caucho.loader.EnvironmentBean;
044: import com.caucho.loader.EnvironmentClassLoader;
045: import com.caucho.loader.SimpleLoader;
046: import com.caucho.log.Log;
047: import com.caucho.server.util.CauchoSystem;
048: import com.caucho.server.webapp.WebApp;
049: import com.caucho.server.webapp.WebAppController;
050: import com.caucho.util.L10N;
051: import com.caucho.vfs.Path;
052: import com.caucho.vfs.Vfs;
053:
054: import javax.annotation.*;
055: import javax.servlet.SingleThreadModel;
056: import javax.servlet.jsp.HttpJspPage;
057: import javax.servlet.jsp.JspPage;
058: import java.io.IOException;
059: import java.io.InputStream;
060: import java.util.ArrayList;
061: import java.util.HashSet;
062: import java.util.logging.Level;
063: import java.util.logging.Logger;
064:
065: /**
066: * Compilation interface for JSP pages.
067: */
068: public class JspCompiler implements EnvironmentBean {
069: private static final L10N L = new L10N(JspCompiler.class);
070: private static final Logger log = Log.open(JspCompiler.class);
071:
072: private ClassLoader _loader;
073:
074: private WebApp _app;
075:
076: private Path _classDir;
077: private Path _appDir;
078:
079: private JspResourceManager _resourceManager;
080: private TaglibManager _taglibManager;
081: private final TagFileManager _tagFileManager;
082:
083: private JspPropertyGroup _jspPropertyGroup;
084:
085: private boolean _isXml;
086: private ArrayList<String> _preludeList = new ArrayList<String>();
087: private ArrayList<String> _codaList = new ArrayList<String>();
088:
089: private HashSet<String> _compilingTags = new HashSet<String>();
090: private boolean _hasRecursiveCompile;
091:
092: private ArrayList<JspCompilerInstance> _pending = new ArrayList<JspCompilerInstance>();
093:
094: public JspCompiler() {
095: _loader = new EnvironmentClassLoader();
096:
097: _tagFileManager = new TagFileManager(this );
098: }
099:
100: /**
101: * Returns the classloader for configuration.
102: */
103: public ClassLoader getClassLoader() {
104: return _loader;
105: }
106:
107: /**
108: * Returns the classloader for configuration.
109: */
110: private void setClassLoader(ClassLoader loader) {
111: _loader = loader;
112: }
113:
114: /**
115: * Sets the destination class directory.
116: */
117: public void setClassDir(Path path) {
118: _classDir = path;
119: }
120:
121: /**
122: * Sets the destination class directory.
123: */
124: public void setClassDirectory(Path path) {
125: setClassDir(path);
126: }
127:
128: /**
129: * Gets the destination class directory.
130: */
131: public Path getClassDir() {
132: if (_classDir != null)
133: return _classDir;
134: else
135: return CauchoSystem.getWorkPath();
136: }
137:
138: /**
139: * Sets the source webApp directory.
140: */
141: public void setAppDir(Path path) {
142: _appDir = path;
143: }
144:
145: /**
146: * Gets the source webApp directory.
147: */
148: public Path getAppDir() {
149: if (_appDir != null)
150: return _appDir;
151: else if (_app != null)
152: return _app.getAppDir();
153: else
154: return null;
155: }
156:
157: /**
158: * Adds a prelude include.
159: */
160: public void addPrelude(String prelude) {
161: _preludeList.add(prelude);
162: }
163:
164: /**
165: * Adds a coda include.
166: */
167: public void addCoda(String coda) {
168: _codaList.add(coda);
169: }
170:
171: /**
172: * Set true when XML is the default parser.
173: */
174: public void setXml(boolean isXml) {
175: _isXml = isXml;
176: }
177:
178: /**
179: * True when XML is the default parser.
180: */
181: public boolean isXml() {
182: return _isXml;
183: }
184:
185: /**
186: * Sets the resource manager.
187: */
188: public void setResourceManager(JspResourceManager manager) {
189: _resourceManager = manager;
190: }
191:
192: /**
193: * Gets the resource manager.
194: */
195: public JspResourceManager getResourceManager() {
196: return _resourceManager;
197: }
198:
199: /**
200: * Gets the tag file manager.
201: */
202: public TagFileManager getTagFileManager() {
203: return _tagFileManager;
204: }
205:
206: public TaglibManager getTaglibManager() throws JspParseException,
207: IOException {
208: synchronized (this ) {
209: if (_taglibManager == null) {
210: WebApp app = getWebApp();
211:
212: Path appDir = getAppDir();
213: if (appDir == null && app != null)
214: appDir = app.getAppDir();
215:
216: JspResourceManager resourceManager = getResourceManager();
217: if (resourceManager != null) {
218: } else if (app != null)
219: resourceManager = new AppResourceManager(app);
220: else {
221: resourceManager = new AppDirResourceManager(appDir);
222: }
223:
224: _taglibManager = new TaglibManager(resourceManager,
225: app, _tagFileManager);
226: _taglibManager.setWebApp(app);
227:
228: JspConfig jspConfig = null;
229:
230: if (app != null)
231: jspConfig = (JspConfig) app
232: .getExtension("jsp-config");
233:
234: if (jspConfig != null) {
235: ArrayList<JspTaglib> tldMapList = jspConfig
236: .getTaglibList();
237: for (int i = 0; i < tldMapList.size(); i++) {
238: JspTaglib taglib = tldMapList.get(i);
239:
240: _taglibManager.addLocationMap(taglib
241: .getTaglibUri(), taglib
242: .getTaglibLocation());
243: }
244: }
245:
246: if (app != null) {
247: ArrayList<JspTaglib> taglibs = app.getTaglibList();
248: for (int i = 0; taglibs != null
249: && i < taglibs.size(); i++) {
250: JspTaglib taglib = taglibs.get(i);
251:
252: _taglibManager.addLocationMap(taglib
253: .getTaglibUri(), taglib
254: .getTaglibLocation());
255: }
256: }
257: }
258: }
259:
260: return _taglibManager;
261: }
262:
263: /**
264: * Sets the JspPropertyGroup
265: */
266: public JspPropertyGroup createJsp() {
267: if (_jspPropertyGroup == null) {
268: _jspPropertyGroup = new JspPropertyGroup();
269: }
270:
271: return _jspPropertyGroup;
272: }
273:
274: /**
275: * Sets the JspPropertyGroup
276: */
277: public JspPropertyGroup getJspPropertyGroup() {
278: return _jspPropertyGroup;
279: }
280:
281: /**
282: * Initialize values based on the ServletContext. When the calling code
283: * has the ServletContext available, it can take advantage of it.
284: */
285: public WebApp createWebApp(Path rootDirectory) {
286: if (_app == null) {
287: if (rootDirectory == null)
288: rootDirectory = getAppDir();
289:
290: WebAppController controller = new WebAppController("", "",
291: rootDirectory, null);
292:
293: _app = controller.getDeployInstance();
294: }
295:
296: return _app;
297: }
298:
299: /**
300: * Initialize values based on the ServletContext. When the calling code
301: * has the ServletContext available, it can take advantage of it.
302: */
303: public void setWebApp(WebApp app) {
304: _app = app;
305:
306: if (_resourceManager == null)
307: _resourceManager = new AppResourceManager(_app);
308: }
309:
310: /**
311: * Initialize values based on the ServletContext. When the calling code
312: * has the ServletContext available, it can take advantage of it.
313: */
314: public ApplicationConfig createApplication() {
315: return new ApplicationConfig();
316: }
317:
318: /**
319: * Returns the app.
320: */
321: public WebApp getWebApp() {
322: return _app;
323: }
324:
325: /**
326: * Adds a new tag being compiled.
327: */
328: public boolean addTag(String className) {
329: if (_compilingTags.contains(className)) {
330: _hasRecursiveCompile = true;
331: return true;
332: }
333:
334: _compilingTags.add(className);
335:
336: return false;
337: }
338:
339: /**
340: * Has recursive compile.
341: */
342: public boolean hasRecursiveCompile() {
343: return _hasRecursiveCompile;
344: }
345:
346: /**
347: * Mangles the name.
348: */
349: public static String urlToClassName(String name) {
350: return JavaCompiler.mangleName("jsp/" + name);
351: }
352:
353: /**
354: * Adds a pending compilation.
355: */
356: void addPending(JspCompilerInstance pending) {
357: _pending.add(pending);
358: }
359:
360: /**
361: * Compiles pending compilations.
362: */
363: void compilePending() throws Exception {
364: if (_pending.size() == 0)
365: return;
366:
367: ArrayList<JspCompilerInstance> pendingList;
368: pendingList = new ArrayList<JspCompilerInstance>(_pending);
369:
370: for (int i = 0; i < pendingList.size(); i++) {
371: JspCompilerInstance pending = pendingList.get(i);
372:
373: pending.completeTag();
374: }
375:
376: _pending.clear();
377: }
378:
379: /**
380: * Compiles the JSP file specified with jspFile.
381: *
382: * @param jspPath the path to the JSP source
383: * @param uri the uri for the JSP file
384: *
385: * @return a JspPage instance
386: */
387: public Page compile(Path jspPath, String uri) throws Exception {
388: return getCompilerInstance(jspPath, uri).compile();
389: }
390:
391: /**
392: * Returns the compilation instance.
393: */
394: public JspCompilerInstance getCompilerInstance(Path jspPath,
395: String uri) throws Exception {
396: return getCompilerInstance(jspPath, uri, null);
397: }
398:
399: public void init() throws JspParseException, IOException {
400: getTaglibManager();
401: }
402:
403: /**
404: * Returns the compilation instance.
405: */
406: public JspCompilerInstance getCompilerInstance(Path jspPath,
407: String uri, String className) throws Exception {
408: JspCompilerInstance instance = new JspCompilerInstance(this );
409:
410: instance.setJspPath(jspPath);
411: instance.setURI(uri);
412: instance.setClassName(className);
413:
414: instance.init();
415:
416: return instance;
417: }
418:
419: /**
420: * Loads an already-compiled JSP class.
421: *
422: * @param className the mangled classname for the JSP file.
423: */
424: public Page loadPage(String className, boolean isAutoCompile)
425: throws Throwable {
426: JspPage jspPage = (JspPage) loadClass(className, isAutoCompile);
427:
428: Page page;
429: if (jspPage instanceof Page)
430: page = (Page) jspPage;
431: else if (jspPage instanceof SingleThreadModel)
432: page = new SingleThreadWrapperPage((HttpJspPage) jspPage);
433: else
434: page = new WrapperPage((HttpJspPage) jspPage);
435:
436: return page;
437: }
438:
439: /**
440: * Loads an already-compiled JSP class.
441: *
442: * @param className the mangled classname for the JSP file.
443: */
444: public Object loadClass(String className, boolean autoCompile)
445: throws Throwable {
446: ClassLoader parentLoader = Thread.currentThread()
447: .getContextClassLoader();
448: ClassLoader jspLoader = SimpleLoader.create(parentLoader,
449: getClassDir(), null);
450:
451: // If the loading fails, remove the class because it may be corrupted
452: try {
453: Class cl = CauchoSystem.loadClass(className, false,
454: jspLoader);
455:
456: readSmap(parentLoader, className);
457:
458: return cl.newInstance();
459: } catch (Throwable e) {
460: if (autoCompile) {
461: try {
462: String pathName = className.replace('.', '/')
463: + ".class";
464: Path classPath = getClassDir().lookup(pathName);
465:
466: classPath.remove();
467: } catch (IOException e1) {
468: log.log(Level.FINE, e1.toString(), e1);
469: }
470: }
471:
472: throw e;
473: }
474: }
475:
476: /**
477: * Loads an already-compiled JSP class.
478: *
479: * @param className the mangled classname for the JSP file.
480: */
481: public Page loadStatic(String className, boolean isSession)
482: throws Exception {
483: ClassLoader loader = Thread.currentThread()
484: .getContextClassLoader();
485:
486: // If the loading fails, remove the class because it may be corrupted
487: String staticName = className.replace('.', '/') + ".static";
488:
489: Path path = getClassDir().lookup(staticName);
490:
491: return new StaticPage(path, isSession);
492: }
493:
494: private void readSmap(ClassLoader loader, String className) {
495: if (loader == null)
496: return;
497:
498: String smapName = className.replace('.', '/') + ".java.smap";
499:
500: InputStream is = null;
501: try {
502: is = loader.getResourceAsStream(smapName);
503: } catch (Exception e) {
504: log.log(Level.FINE, e.toString(), e);
505: } finally {
506: if (is != null) {
507: try {
508: is.close();
509: } catch (IOException e) {
510: }
511: }
512: }
513: }
514:
515: public static void main(String[] args) throws Exception {
516: if (args.length == 0) {
517: System.out
518: .println("usage: com.caucho.jsp.JspCompiler [flags] jsp1 jsp2 ...");
519: System.out
520: .println(" -app-dir : The directory root of the web-app.");
521: System.out
522: .println(" -class-dir: The working directory to use as output.");
523: System.out.println(" -compiler: sets the javac.");
524: System.out
525: .println(" -conf: A configuration file for the compiler.");
526: System.exit(1);
527: }
528:
529: // needed at minimum to handle the qa jsp/1933
530: Thread thread = Thread.currentThread();
531: ClassLoader oldLoader = thread.getContextClassLoader();
532: try {
533: JspCompiler compiler = new JspCompiler();
534:
535: int i = compiler.configureFromArgs(args);
536:
537: ClassLoader loader = compiler.getClassLoader();
538:
539: thread.setContextClassLoader(loader);
540:
541: ArrayList<String> pendingClasses = new ArrayList<String>();
542:
543: if (i == args.length) {
544: compiler.compilePath(pendingClasses, ".");
545: }
546:
547: for (; i < args.length; i++) {
548: String uri = args[i];
549:
550: compiler.compilePath(pendingClasses, uri);
551: }
552:
553: String files[] = new String[pendingClasses.size()];
554: pendingClasses.toArray(files);
555:
556: compiler.compileBatch(files);
557: } finally {
558: Thread.currentThread().setContextClassLoader(oldLoader);
559: }
560: }
561:
562: /**
563: * Callable by applications to initialize the compiler. This call
564: * will configure the JspCompiler, but not start any compilations.
565: */
566: public int configureFromArgs(String[] args) throws Exception {
567: if (args.length == 0) {
568: System.out
569: .println("usage: com.caucho.jsp.JspCompiler [flags] jsp1 jsp2 ...");
570: System.out
571: .println(" -app-dir : The directory root of the web-app.");
572: System.out
573: .println(" -class-dir: The working directory to use as output.");
574: System.out
575: .println(" -conf: A configuration file for the compiler.");
576: System.exit(1);
577: }
578:
579: // needed at minimum to handle the qa jsp/1933
580: Thread thread = Thread.currentThread();
581: ClassLoader oldLoader = thread.getContextClassLoader();
582: try {
583: ClassLoader loader = getClassLoader();
584:
585: thread.setContextClassLoader(loader);
586:
587: JspPropertyGroup jsp = createJsp();
588: jsp.setRequireSource(false);
589:
590: int i = 0;
591: boolean hasConf = false;
592:
593: while (i < args.length) {
594: if (args[i].equals("-app-dir")) {
595: Path appDir = Vfs.lookup(args[i + 1]);
596:
597: WebApp app = createWebApp(appDir);
598:
599: setWebApp(app);
600: setAppDir(appDir);
601:
602: i += 2;
603: } else if (args[i].equals("-class-dir")
604: || args[i].equals("-d")) {
605: setClassDirectory(Vfs.lookup(args[i + 1]));
606: i += 2;
607: } else if (args[i].equals("-compiler")) {
608: JavacConfig.getLocalConfig().setCompiler(
609: args[i + 1]);
610:
611: i += 2;
612: } else if (args[i].equals("-conf")) {
613: Path path = Vfs.lookup(args[i + 1]);
614:
615: new Config().configureBean(this , path);
616: hasConf = true;
617:
618: i += 2;
619: } else
620: break;
621: }
622:
623: WebApp app = getWebApp();
624: if (app != null && !hasConf) {
625: Path appDir = app.getAppDir();
626:
627: DynamicClassLoader dynLoader = app
628: .getEnvironmentClassLoader();
629: dynLoader.addLoader(new CompilingLoader(appDir
630: .lookup("WEB-INF/classes")));
631: dynLoader.addLoader(new DirectoryLoader(appDir
632: .lookup("WEB-INF/lib")));
633:
634: Path webXml = appDir.lookup("WEB-INF/web.xml");
635:
636: if (webXml.canRead()) {
637: try {
638: new Config().configureBean(app, webXml);
639: } catch (Exception e) {
640: log.log(Level.WARNING, e.toString(), e);
641: }
642: }
643: }
644:
645: Path appDir = null;
646:
647: if (app == null && getAppDir() != null) {
648: app = createWebApp(null);
649:
650: app.setRootDirectory(getAppDir());
651: setWebApp(app);
652: }
653:
654: if (app != null) {
655: app.init();
656:
657: appDir = getWebApp().getAppDir();
658: setClassLoader(getWebApp().getClassLoader());
659: }
660:
661: if (appDir == null) {
662: appDir = Vfs.lookup();
663:
664: if (getAppDir() == null && getWebApp() == null) {
665: System.err
666: .println(L
667: .l("-app-dir must be specified for JspCompiler"));
668: return -1;
669: }
670: }
671:
672: setResourceManager(new AppDirResourceManager(appDir));
673:
674: return i;
675: } finally {
676: Thread.currentThread().setContextClassLoader(oldLoader);
677: }
678: }
679:
680: public void compilePath(ArrayList<String> pendingClasses, String uri)
681: throws Exception {
682: Thread thread = Thread.currentThread();
683: ClassLoader oldLoader = thread.getContextClassLoader();
684:
685: try {
686: thread.setContextClassLoader(getClassLoader());
687:
688: Path path = Vfs.lookup(uri);
689:
690: if (path.isDirectory())
691: compileDirectory(path, getAppDir(), this ,
692: pendingClasses);
693: else
694: compileJsp(path, getAppDir(), this , pendingClasses);
695: } finally {
696: thread.setContextClassLoader(oldLoader);
697: }
698: }
699:
700: public void compileBatch(String[] pendingClasses) throws Exception {
701: Thread thread = Thread.currentThread();
702: ClassLoader oldLoader = thread.getContextClassLoader();
703:
704: try {
705: thread.setContextClassLoader(getClassLoader());
706:
707: JavaCompiler javaCompiler = JavaCompiler
708: .create(getClassLoader());
709: javaCompiler.setClassDir(getClassDir());
710:
711: javaCompiler.compileBatch(pendingClasses);
712: } finally {
713: thread.setContextClassLoader(oldLoader);
714: }
715: }
716:
717: private static void compileDirectory(Path path, Path appDir,
718: JspCompiler compiler, ArrayList<String> pendingClasses)
719: throws Exception {
720: if (path.isDirectory()) {
721: String[] list = path.list();
722:
723: for (int i = 0; i < list.length; i++) {
724: Path subpath = path.lookup(list[i]);
725:
726: compileDirectory(subpath, appDir, compiler,
727: pendingClasses);
728: }
729: } else if (path.getPath().endsWith(".jsp")
730: || path.getPath().endsWith(".jsfx")
731: || path.getPath().endsWith(".jspx")
732: || path.getPath().endsWith(".jsfx")) {
733: compileJsp(path, appDir, compiler, pendingClasses);
734: }
735: }
736:
737: private static void compileJsp(Path path, Path appDir,
738: JspCompiler compiler, ArrayList<String> pendingClasses)
739: throws Exception {
740: String uri;
741:
742: uri = path.getPath().substring(appDir.getPath().length());
743:
744: if (uri.endsWith("x"))
745: compiler.setXml(true);
746: else
747: compiler.setXml(false);
748:
749: String className = JspCompiler.urlToClassName(uri);
750: JspCompilerInstance compInst;
751: compInst = compiler.getCompilerInstance(path, uri, className);
752:
753: JspGenerator gen = compInst.generate();
754:
755: if (!gen.isStatic())
756: pendingClasses.add(className.replace('.', '/') + ".java");
757: }
758:
759: public class ApplicationConfig {
760: private Path _rootDir;
761: private ContainerProgram _program = new ContainerProgram();
762:
763: ApplicationConfig() {
764: _rootDir = Vfs.lookup();
765: }
766:
767: public void setRootDirectory(Path path) {
768: _rootDir = path;
769: }
770:
771: public void setDocumentDirectory(Path path) {
772: _rootDir = path;
773: }
774:
775: public void setAppDir(Path path) {
776: _rootDir = path;
777: }
778:
779: public void addBuilderProgram(ConfigProgram program) {
780: _program.addProgram(program);
781: }
782:
783: @PostConstruct
784: public void init() throws Exception {
785: WebApp webApp = createWebApp(_rootDir);
786: _program.configure(webApp);
787: Config.init(webApp);
788:
789: webApp.init();
790: webApp.start();
791: }
792: }
793: }
|