/* * Copyright (c) 1998-2011 Caucho Technology -- all rights reserved * * This file is part of Resin(R) Open Source * * Each copy or derived work must preserve the copyright notice and this * notice unmodified. * * Resin Open Source is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * Resin Open Source is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty * of NON-INFRINGEMENT. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License * along with Resin Open Source; if not, write to the * * Free Software Foundation, Inc. * 59 Temple Place, Suite 330 * Boston, MA 02111-1307 USA * * @author Scott Ferguson */ package com.caucho.jsp; import com.caucho.config.*; import com.caucho.config.program.ConfigProgram; import com.caucho.config.program.ContainerProgram; import com.caucho.config.types.*; import com.caucho.env.service.ResinSystem; import com.caucho.java.*; import com.caucho.jsp.cfg.JspConfig; import com.caucho.jsp.cfg.JspPropertyGroup; import com.caucho.jsp.cfg.JspTaglib; import com.caucho.loader.CompilingLoader; import com.caucho.loader.DirectoryLoader; import com.caucho.loader.DynamicClassLoader; import com.caucho.loader.EnvironmentBean; import com.caucho.loader.EnvironmentClassLoader; import com.caucho.loader.SimpleLoader; import com.caucho.resin.ResinEmbed; import com.caucho.resin.WebAppEmbed; import com.caucho.server.cluster.Server; import com.caucho.server.util.CauchoSystem; import com.caucho.server.webapp.WebApp; import com.caucho.server.webapp.WebAppContainer; import com.caucho.server.webapp.WebAppController; import com.caucho.util.L10N; import com.caucho.vfs.Path; import com.caucho.vfs.Vfs; import com.caucho.jsf.cfg.JsfPropertyGroup; import javax.annotation.*; import javax.servlet.SingleThreadModel; import javax.servlet.jsp.HttpJspPage; import javax.servlet.jsp.JspPage; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.HashSet; import java.util.logging.Level; import java.util.logging.Logger; /** * Compilation interface for JSP pages. * * <pre> * com.caucho.jsp.JspCompiler [flags] jsp1 jsp2 ..."); * -app-dir : The directory root of the web-app."); * -class-dir: The working directory to use as output."); * -conf: A configuration file for the compiler."); * </pre> */ public class JspCompiler implements EnvironmentBean { private static final L10N L = new L10N(JspCompiler.class); private static final Logger log = Logger.getLogger(JspCompiler.class.getName()); private ResinSystem _system; private ClassLoader _loader; private WebApp _webApp; private Path _classDir; private Path _appDir; private JspResourceManager _resourceManager; private TaglibManager _taglibManager; private final TagFileManager _tagFileManager; private JspPropertyGroup _jspPropertyGroup; private JsfPropertyGroup _jsfPropertyGroup; private boolean _isXml; private ArrayList<String> _preludeList = new ArrayList<String>(); private ArrayList<String> _codaList = new ArrayList<String>(); private HashSet<String> _compilingTags = new HashSet<String>(); private boolean _hasRecursiveCompile; private ArrayList<JspCompilerInstance> _pending = new ArrayList<JspCompilerInstance>(); private ResinEmbed _resin; public JspCompiler() { _system = ResinSystem.getCurrent(); if (_system == null) _system = new ResinSystem("jsp-compiler"); _loader = _system.getClassLoader(); _tagFileManager = new TagFileManager(this); } /** * Returns the classloader for configuration. */ public ClassLoader getClassLoader() { return _loader; } public void setClassLoader(ClassLoader loader) { _loader = loader; } /** * Sets the destination class directory. */ public void setClassDir(Path path) { _classDir = path; } /** * Sets the destination class directory. */ public void setClassDirectory(Path path) { setClassDir(path); } /** * Gets the destination class directory. */ public Path getClassDir() { if (_classDir != null) return _classDir; else return CauchoSystem.getWorkPath(); } /** * Sets the source webApp directory. */ public void setAppDir(Path path) { _appDir = path; } /** * Gets the source webApp directory. */ public Path getAppDir() { if (_appDir != null) return _appDir; else if (_webApp != null) return _webApp.getRootDirectory(); else return null; } /** * Adds a prelude include. */ public void addPrelude(String prelude) { _preludeList.add(prelude); } /** * Adds a coda include. */ public void addCoda(String coda) { _codaList.add(coda); } /** * Set true when XML is the default parser. */ public void setXml(boolean isXml) { _isXml = isXml; } /** * True when XML is the default parser. */ public boolean isXml() { return _isXml; } /** * Sets the resource manager. */ public void setResourceManager(JspResourceManager manager) { _resourceManager = manager; } /** * Gets the resource manager. */ public JspResourceManager getResourceManager() { return _resourceManager; } /** * Gets the tag file manager. */ public TagFileManager getTagFileManager() { return _tagFileManager; } public TaglibManager getTaglibManager() throws JspParseException, IOException { synchronized (this) { if (_taglibManager == null) { WebApp webApp = getWebApp(); Path appDir = getAppDir(); if (appDir == null && webApp != null) appDir = webApp.getRootDirectory(); JspResourceManager resourceManager = getResourceManager(); if (resourceManager != null) { } else if (webApp != null) resourceManager = new AppResourceManager(webApp); else { resourceManager = new AppDirResourceManager(appDir); } _taglibManager = new TaglibManager(resourceManager, webApp, _tagFileManager); _taglibManager.setWebApp(webApp); JspConfig jspConfig = null; if (webApp != null) jspConfig = (JspConfig) webApp.getExtension("jsp-config"); if (jspConfig != null) { ArrayList<JspTaglib> tldMapList = jspConfig.getTaglibList(); for (int i = 0; i < tldMapList.size(); i++) { JspTaglib taglib = tldMapList.get(i); _taglibManager.addLocationMap(taglib.getTaglibUri(), taglib.getTaglibLocation()); } } if (webApp != null) { ArrayList<JspTaglib> taglibs = webApp.getTaglibList(); for (int i = 0; taglibs != null && i < taglibs.size(); i++) { JspTaglib taglib = taglibs.get(i); _taglibManager.addLocationMap(taglib.getTaglibUri(), taglib.getTaglibLocation()); } } } } return _taglibManager; } /** * Sets the JspPropertyGroup */ public JspPropertyGroup createJsp() { if (_jspPropertyGroup == null) { _jspPropertyGroup = new JspPropertyGroup(); } return _jspPropertyGroup; } /** * Sets the JspPropertyGroup */ public JspPropertyGroup getJspPropertyGroup() { return _jspPropertyGroup; } /** * Sets JsfPropertyGropu */ public JsfPropertyGroup createJsf() { if (_jsfPropertyGroup == null) _jsfPropertyGroup = new JsfPropertyGroup(); return _jsfPropertyGroup; } /** * Gets JsfPropertyGroup */ public JsfPropertyGroup getJsfPropertyGroup() { return _jsfPropertyGroup; } /** * Initialize values based on the ServletContext. When the calling code * has the ServletContext available, it can take advantage of it. */ public WebApp createWebApp(Path rootDirectory) { if (_webApp == null) { if (rootDirectory == null) rootDirectory = getAppDir(); _resin = new ResinEmbed(); _resin.setRootDirectory(rootDirectory.getURL()); WebAppEmbed webAppEmbed = new WebAppEmbed(); webAppEmbed.setRootDirectory(rootDirectory.getURL()); webAppEmbed.setDisableStart(true); _resin.addWebApp(webAppEmbed); // jsp/193h, #4397 _resin.start(); _webApp = webAppEmbed.getWebApp(); } return _webApp; } /** * Initialize values based on the ServletContext. When the calling code * has the ServletContext available, it can take advantage of it. */ public void setWebApp(WebApp app) { _webApp = app; if (_resourceManager == null) _resourceManager = new AppResourceManager(_webApp); } /** * Initialize values based on the ServletContext. When the calling code * has the ServletContext available, it can take advantage of it. */ public ApplicationConfig createApplication() { return new ApplicationConfig(); } /** * Returns the app. */ public WebApp getWebApp() { return _webApp; } /** * Adds a new tag being compiled. */ public boolean addTag(String className) { if (_compilingTags.contains(className)) { _hasRecursiveCompile = true; return true; } _compilingTags.add(className); return false; } /** * Has recursive compile. */ public boolean hasRecursiveCompile() { return _hasRecursiveCompile; } /** * Mangles the name. */ public static String urlToClassName(String name) { return JavaCompiler.mangleName("jsp/" + name); } /** * Adds a pending compilation. */ void addPending(JspCompilerInstance pending) { _pending.add(pending); } /** * Compiles pending compilations. */ void compilePending() throws Exception { if (_pending.size() == 0) return; ArrayList<JspCompilerInstance> pendingList; pendingList = new ArrayList<JspCompilerInstance>(_pending); for (int i = 0; i < pendingList.size(); i++) { JspCompilerInstance pending = pendingList.get(i); pending.completeTag(); } _pending.clear(); } /** * Compiles the JSP file specified with jspFile. * * @param jspPath the path to the JSP source * @param uri the uri for the JSP file * * @return a JspPage instance */ public Page compile(Path jspPath, String uri) throws Exception { return getCompilerInstance(jspPath, uri).compile(); } /** * Returns the compilation instance. */ public JspCompilerInstance getCompilerInstance(Path jspPath, String uri) throws Exception { return getCompilerInstance(jspPath, uri, null); } public void init() throws JspParseException, IOException { getTaglibManager(); } /** * Returns the compilation instance. */ public JspCompilerInstance getCompilerInstance(Path jspPath, String uri, String className) throws Exception { JspCompilerInstance instance = new JspCompilerInstance(this); instance.setJspPath(jspPath); instance.setURI(uri); instance.setClassName(className); instance.init(); return instance; } /** * Loads an already-compiled JSP class. * * @param className the mangled classname for the JSP file. */ public Page loadPage(String className, boolean isAutoCompile) throws Throwable { JspPage jspPage = (JspPage) loadClass(className, isAutoCompile); Page page; if (jspPage instanceof Page) page = (Page) jspPage; else if (jspPage instanceof SingleThreadModel) page = new SingleThreadWrapperPage((HttpJspPage) jspPage); else page = new WrapperPage((HttpJspPage) jspPage); return page; } /** * Loads an already-compiled JSP class. * * @param className the mangled classname for the JSP file. */ public Object loadClass(String className, boolean autoCompile) throws Throwable { ClassLoader parentLoader = Thread.currentThread().getContextClassLoader(); ClassLoader jspLoader = SimpleLoader.create(parentLoader, getClassDir(), null); // If the loading fails, remove the class because it may be corrupted try { Class<?> cl = Class.forName(className, false, jspLoader); readSmap(parentLoader, className); return cl.newInstance(); } catch (Throwable e) { if (autoCompile) { try { String pathName = className.replace('.', '/') + ".class"; Path classPath = getClassDir().lookup(pathName); classPath.remove(); } catch (IOException e1) { log.log(Level.FINE, e1.toString(), e1); } } throw e; } } /** * Loads an already-compiled JSP class. * * @param className the mangled classname for the JSP file. */ public Page loadStatic(String className, boolean isSession) throws Exception { // If the loading fails, remove the class because it may be corrupted String staticName = className.replace('.', '/') + ".static"; Path path = getClassDir().lookup(staticName); return new StaticPage(path, isSession); } private void readSmap(ClassLoader loader, String className) { if (loader == null) return; String smapName = className.replace('.', '/') + ".java.smap"; InputStream is = null; try { is = loader.getResourceAsStream(smapName); } catch (Exception e) { log.log(Level.FINE, e.toString(), e); } finally { if (is != null) { try { is.close(); } catch (IOException e) { } } } } public void close() { ResinEmbed resin = _resin; if (resin != null) resin.destroy(); } public static void main(String []args) throws Exception { if (args.length == 0) { System.out.println("usage: com.caucho.jsp.JspCompiler [flags] jsp1 jsp2 ..."); System.out.println(" -app-dir : The directory root of the web-app."); System.out.println(" -class-dir: The working directory to use as output."); System.out.println(" -compiler: sets the javac."); System.out.println(" -conf: A configuration file for the compiler."); System.exit(1); } // needed at minimum to handle the qa jsp/1933 Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { JspCompiler compiler = new JspCompiler(); int i = compiler.configureFromArgs(args); ClassLoader loader = compiler.getClassLoader(); thread.setContextClassLoader(loader); ArrayList<String> pendingClasses = new ArrayList<String>(); if (i == args.length) { compiler.compilePath(pendingClasses, "."); } for (; i < args.length; i++) { String uri = args[i]; compiler.compilePath(pendingClasses, uri); } String files[] = new String[pendingClasses.size()]; pendingClasses.toArray(files); compiler.compileBatch(files); } finally { Thread.currentThread().setContextClassLoader(oldLoader); } } /** * Callable by applications to initialize the compiler. This call * will configure the JspCompiler, but not start any compilations. * * <pre> * com.caucho.jsp.JspCompiler [flags] jsp1 jsp2 ..."); * -app-dir : The directory root of the web-app."); * -class-dir: The working directory to use as output."); * -conf: A configuration file for the compiler."); * </pre> */ public int configureFromArgs(String []args) throws Exception { if (args.length == 0) { System.out.println("usage: com.caucho.jsp.JspCompiler [flags] jsp1 jsp2 ..."); System.out.println(" -app-dir : The directory root of the web-app."); System.out.println(" -class-dir: The working directory to use as output."); System.out.println(" -conf: A configuration file for the compiler."); System.out.println(" -compiler: javac|internal|eclipse|groovyc."); System.exit(1); } // needed at minimum to handle the qa jsp/1933 Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { ClassLoader loader = getClassLoader(); thread.setContextClassLoader(loader); JspPropertyGroup jsp = createJsp(); jsp.setRequireSource(false); int i = 0; boolean hasConf = false; while (i < args.length) { if (args[i].equals("-app-dir")) { Path appDir = Vfs.lookup(args[i + 1]); WebApp webApp = createWebApp(appDir); if (webApp != null) setWebApp(webApp); setAppDir(appDir); i += 2; } else if (args[i].equals("-class-dir") || args[i].equals("-d")) { setClassDirectory(Vfs.lookup(args[i + 1])); i += 2; } else if (args[i].equals("-compiler")) { JavacConfig.getLocalConfig().setCompiler(args[i + 1]); i += 2; } else if (args[i].equals("-conf")) { Path path = Vfs.lookup(args[i + 1]); new Config().configureBean(this, path); hasConf = true; i += 2; } else break; } WebApp webApp = getWebApp(); if (webApp != null && ! hasConf) { Path appDir = webApp.getRootDirectory(); DynamicClassLoader dynLoader = webApp.getEnvironmentClassLoader(); dynLoader.addLoader(new CompilingLoader(dynLoader, appDir.lookup("WEB-INF/classes"))); dynLoader.addLoader(new DirectoryLoader(dynLoader, appDir.lookup("WEB-INF/lib"))); Path webXml = appDir.lookup("WEB-INF/web.xml"); if (webXml.canRead()) { try { new Config().configureBean(webApp, webXml); } catch (Exception e) { log.log(Level.WARNING, e.toString(), e); } } } Path appDir = null; if (webApp == null && getAppDir() != null) { webApp = createWebApp(null); if (webApp != null) webApp.setRootDirectory(getAppDir()); setWebApp(webApp); } if (webApp != null) { webApp.setCompileContext(true); webApp.init(); appDir = getWebApp().getRootDirectory(); setClassLoader(getWebApp().getClassLoader()); } if (appDir == null) { appDir = Vfs.lookup(); if (getAppDir() == null && getWebApp() == null) { System.err.println(L.l("-app-dir must be specified for JspCompiler")); return 0; } } setResourceManager(new AppDirResourceManager(appDir)); return i; } finally { Thread.currentThread().setContextClassLoader(oldLoader); } } public void compilePath(ArrayList<String> pendingClasses, String uri) throws Exception { Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { thread.setContextClassLoader(getClassLoader()); Path path = Vfs.lookup(uri); if (path.isDirectory()) compileDirectory(path, getAppDir(), this, pendingClasses); else compileJsp(path, getAppDir(), this, pendingClasses); } finally { thread.setContextClassLoader(oldLoader); } } public void compileBatch(String []pendingClasses) throws Exception { Thread thread = Thread.currentThread(); ClassLoader oldLoader = thread.getContextClassLoader(); try { thread.setContextClassLoader(getClassLoader()); JavaCompiler javaCompiler = JavaCompiler.create(getClassLoader()); javaCompiler.setClassDir(getClassDir()); javaCompiler.compileBatch(pendingClasses); } finally { thread.setContextClassLoader(oldLoader); } } private static void compileDirectory(Path path, Path appDir, JspCompiler compiler, ArrayList<String> pendingClasses) throws Exception { if (path.isDirectory()) { String []list = path.list(); for (int i = 0; i < list.length; i++) { Path subpath = path.lookup(list[i]); compileDirectory(subpath, appDir, compiler, pendingClasses); } } else if (path.getPath().endsWith(".jsp") || path.getPath().endsWith(".jsfx") || path.getPath().endsWith(".jspx") || path.getPath().endsWith(".jsfx")) { compileJsp(path, appDir, compiler, pendingClasses); } } private static void compileJsp(Path path, Path appDir, JspCompiler compiler, ArrayList<String> pendingClasses) throws Exception { String uri; uri = path.getPath().substring(appDir.getPath().length()); if (uri.endsWith("x")) compiler.setXml(true); else compiler.setXml(false); String className = JspCompiler.urlToClassName(uri); JspCompilerInstance compInst; compInst = compiler.getCompilerInstance(path, uri, className); JspGenerator gen = compInst.generate(); if (! gen.isStatic()) pendingClasses.add(className.replace('.', '/') + ".java"); } public class ApplicationConfig { private Path _rootDir; private ContainerProgram _program = new ContainerProgram(); ApplicationConfig() { _rootDir = Vfs.lookup(); } public void setRootDirectory(Path path) { _rootDir = path; } public void setDocumentDirectory(Path path) { _rootDir = path; } public void setAppDir(Path path) { _rootDir = path; } public void addBuilderProgram(ConfigProgram program) { _program.addProgram(program); } @PostConstruct public void init() throws Exception { WebApp webApp = createWebApp(_rootDir); if (webApp == null) throw new NullPointerException(); _program.configure(webApp); Config.init(webApp); webApp.init(); webApp.start(); } } }