Java tutorial
/* * Licensed under the GPL License. You may not use this file except in * compliance with the License. You may obtain a copy of the License at * * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html * * THIS PACKAGE IS PROVIDED "AS IS" AND WITHOUT ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTIES OF * MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. */ package com.googlecode.psiprobe; import java.io.File; import java.lang.reflect.InvocationTargetException; import java.net.URL; import java.net.URLClassLoader; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.naming.NamingException; import org.apache.catalina.Container; import org.apache.catalina.Context; import org.apache.catalina.Engine; import org.apache.catalina.Host; import org.apache.catalina.core.StandardContext; import org.apache.naming.ContextBindings; import org.apache.commons.lang.reflect.MethodUtils; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.jasper.EmbeddedServletOptions; import org.apache.jasper.JasperException; import org.apache.jasper.JspCompilationContext; import org.apache.jasper.Options; import org.apache.jasper.compiler.JspRuntimeContext; import org.springframework.util.ClassUtils; import com.googlecode.psiprobe.model.jsp.Item; import com.googlecode.psiprobe.model.jsp.Summary; /** * Abstraction layer to implement some functionality, which is common between different container adaptors. * * @author Vlad Ilyushchenko * @author Mark Lewis */ public abstract class AbstractTomcatContainer implements TomcatContainer { protected Log logger = LogFactory.getLog(getClass()); public boolean installContext(String contextName) throws Exception { contextName = formatContextName(contextName); String contextFilename = formatContextFilename(contextName); File f = new File(getConfigBase(), contextFilename + ".xml"); installContextInternal(contextName, f); return findContext(contextName) != null; } public void remove(String contextName) throws Exception { contextName = formatContextName(contextName); Context ctx = findContext(contextName); if (ctx != null) { try { stop(contextName); } catch (Throwable e) { logger.info("Stopping " + contextName + " threw this exception:", e); // // make sure we always re-throw ThreadDeath // if (e instanceof ThreadDeath) { throw (ThreadDeath) e; } } File appDir; File docBase = new File(ctx.getDocBase()); if (!docBase.isAbsolute()) { appDir = new File(getAppBase(), ctx.getDocBase()); } else { appDir = docBase; } logger.debug("Deleting " + appDir.getAbsolutePath()); Utils.delete(appDir); String warFilename = formatContextFilename(contextName); File warFile = new File(getAppBase(), warFilename + ".war"); logger.debug("Deleting " + warFile.getAbsolutePath()); Utils.delete(warFile); File configFile = getConfigFile(ctx); if (configFile != null) { logger.debug("Deleting " + configFile.getAbsolutePath()); Utils.delete(configFile); } removeInternal(contextName); } } /** * Binds a naming context to the current thread's classloader. * * @param context the catalina context */ public void bindToContext(Context context) throws NamingException { Object token = null; ContextBindings.bindClassLoader(context, token, Thread.currentThread().getContextClassLoader()); } /** * Unbinds a naming context from the current thread's classloader. * * @param context the catalina context */ public void unbindFromContext(Context context) throws NamingException { Object token = null; ContextBindings.unbindClassLoader(context, token, Thread.currentThread().getContextClassLoader()); } public Context findContext(String name) { String safeName = formatContextName(name); if (safeName == null) { return null; } Context result = findContextInternal(safeName); if (result == null && "".equals(safeName)) { result = findContextInternal("/"); } return result; } public String formatContextName(String name) { if (name == null) { return null; } String result = name.trim(); if (!result.startsWith("/")) { result = "/" + result; } if ("/".equals(result) || "/ROOT".equals(result)) { result = ""; } return result; } public String formatContextFilename(String contextName) { if (contextName == null) { return null; } else if ("".equals(contextName)) { return "ROOT"; } else if (contextName.startsWith("/")) { return contextName.substring(1); } else { return contextName; } } public void discardWorkDir(Context context) { if (context instanceof StandardContext) { StandardContext standardContext = (StandardContext) context; logger.info("Discarding " + standardContext.getWorkPath()); Utils.delete(new File(standardContext.getWorkPath(), "org")); } else { logger.error("context " + context.getName() + " is not an instance of " + context.getClass().getName() + ", expected StandardContext"); } } public String getServletFileNameForJsp(Context context, String jspName) { String servletName = null; ServletConfig servletConfig = (ServletConfig) context.findChild("jsp"); if (servletConfig != null) { ServletContext sctx = context.getServletContext(); Options opt = new EmbeddedServletOptions(servletConfig, sctx); JspRuntimeContext jrctx = new JspRuntimeContext(sctx, opt); JspCompilationContext jcctx = createJspCompilationContext(jspName, false, opt, sctx, jrctx, null); servletName = jcctx.getServletJavaFileName(); } else { logger.error("Context " + context.getName() + " does not have \"jsp\" servlet"); } return servletName; } /** * Compiles a list of JSPs. Names of JSP files are expected to be relative to the webapp root. The method * updates summary with compilation details. * * @param context * @param summary * @param names */ public void recompileJsps(Context context, Summary summary, List names) { ServletConfig servletConfig = (ServletConfig) context.findChild("jsp"); if (servletConfig != null) { if (summary != null) { synchronized (servletConfig) { ServletContext sctx = context.getServletContext(); Options opt = new EmbeddedServletOptions(servletConfig, sctx); JspRuntimeContext jrctx = new JspRuntimeContext(sctx, opt); try { // // we need to pass context classloader here, so the jsps can reference /WEB-INF/classes and // /WEB-INF/lib. JspCompilationContext would only take URLClassLoader, so we fake it // URLClassLoader classLoader = new URLClassLoader(new URL[] {}, context.getLoader().getClassLoader()); for (Iterator it = names.iterator(); it.hasNext();) { String name = (String) it.next(); long time = System.currentTimeMillis(); JspCompilationContext jcctx = createJspCompilationContext(name, false, opt, sctx, jrctx, classLoader); ClassLoader prevCl = ClassUtils.overrideThreadContextClassLoader(classLoader); try { Item item = (Item) summary.getItems().get(name); if (item != null) { try { org.apache.jasper.compiler.Compiler c = jcctx.createCompiler(); c.compile(); item.setState(Item.STATE_READY); item.setException(null); logger.info("Compiled " + name + ": OK"); } catch (Exception e) { item.setState(Item.STATE_FAILED); item.setException(e); logger.info("Compiled " + name + ": FAILED", e); } item.setCompileTime(System.currentTimeMillis() - time); } else { logger.error(name + " is not on the summary list, ignored"); } } finally { ClassUtils.overrideThreadContextClassLoader(prevCl); } } } finally { jrctx.destroy(); } } } else { logger.error("summary is null for " + context.getName() + ", request ignored"); } } else { logger.error("Context " + context.getName() + " does not have \"jsp\" servlet"); } } /** * Lists and optionally compiles all JSPs for the given context. Compilation details are added to the summary. * * @param context * @param summary * @param compile * @throws Exception */ public void listContextJsps(Context context, Summary summary, boolean compile) throws Exception { ServletConfig servletConfig = (ServletConfig) context.findChild("jsp"); if (servletConfig != null) { synchronized (servletConfig) { ServletContext sctx = context.getServletContext(); Options opt = new EmbeddedServletOptions(servletConfig, sctx); JspRuntimeContext jrctx = new JspRuntimeContext(sctx, opt); try { if (summary.getItems() == null) { summary.setItems(new HashMap()); } // // mark all items as missing // for (Iterator it = summary.getItems().keySet().iterator(); it.hasNext();) { Item item = (Item) summary.getItems().get(it.next()); item.setMissing(true); } // // we need to pass context classloader here, so the jsps can reference /WEB-INF/classes and // /WEB-INF/lib. JspCompilationContext would only take URLClassLoader, so we fake it // compileItem("/", opt, context, jrctx, summary, new URLClassLoader(new URL[] {}, context.getLoader().getClassLoader()), 0, compile); } finally { jrctx.destroy(); } } // // delete "missing" items by keeping "not missing" ones // Map hashMap = new HashMap(); for (Iterator it = summary.getItems().keySet().iterator(); it.hasNext();) { Object key = it.next(); Item item = (Item) summary.getItems().get(key); if (!item.isMissing()) { hashMap.put(key, item); } } summary.setItems(hashMap); } else { logger.error("Context " + context.getName() + " does not have \"jsp\" servlet"); } } public File getConfigFile(Context ctx) { String configFilePath = ctx.getConfigFile(); if (configFilePath != null) { return new File(configFilePath); } else { return null; } } protected String getConfigBase(Container container) { File configBase = new File(System.getProperty("catalina.base"), "conf"); Container baseHost = null; Container baseEngine = null; while (container != null) { if (container instanceof Host) { baseHost = container; } if (container instanceof Engine) { baseEngine = container; } container = container.getParent(); } if (baseEngine != null) { configBase = new File(configBase, baseEngine.getName()); } if (baseHost != null) { configBase = new File(configBase, baseHost.getName()); } return configBase.getAbsolutePath(); } /** * Lists and optionally compiles a directory recursively. * * @param jspName name of JSP file or directory to be listed and compiled. * @param opt * @param ctx * @param jrctx * @param summary * @param classLoader * @param level * @param compile */ protected void compileItem(String jspName, Options opt, Context ctx, JspRuntimeContext jrctx, Summary summary, URLClassLoader classLoader, int level, boolean compile) { ServletContext sctx = ctx.getServletContext(); Set paths = sctx.getResourcePaths(jspName); if (paths != null) { for (Iterator it = paths.iterator(); it.hasNext();) { String name = (String) it.next(); boolean isJsp = false; try { isJsp = name.endsWith(".jsp") || name.endsWith(".jspx") || opt.getJspConfig().isJspPage(name); } catch (JasperException e) { logger.info("isJspPage() thrown an error for " + name, e); } if (isJsp) { JspCompilationContext jcctx = createJspCompilationContext(name, false, opt, sctx, jrctx, classLoader); ClassLoader prevCl = ClassUtils.overrideThreadContextClassLoader(classLoader); try { Item item = (Item) summary.getItems().get(name); if (item == null) { item = new Item(); item.setName(name); } item.setLevel(level); item.setCompileTime(-1); Long objects[] = this.getResourceAttributes(name, ctx); item.setSize(objects[0].longValue()); item.setLastModified(objects[1].longValue()); // } catch (NamingException e) { // logger.error("Cannot lookup attributes for " + name); // } long time = System.currentTimeMillis(); try { org.apache.jasper.compiler.Compiler c = jcctx.createCompiler(); if (compile) { c.compile(); item.setState(Item.STATE_READY); item.setException(null); } else { if (!c.isOutDated()) { item.setState(Item.STATE_READY); item.setException(null); } else if (item.getState() != Item.STATE_FAILED) { item.setState(Item.STATE_OOD); item.setException(null); } } logger.info("Compiled " + name + ": OK"); } catch (Exception e) { item.setState(Item.STATE_FAILED); item.setException(e); logger.info("Compiled " + name + ": FAILED", e); } if (compile) { item.setCompileTime(System.currentTimeMillis() - time); } item.setMissing(false); summary.getItems().put(name, item); } finally { ClassUtils.overrideThreadContextClassLoader(prevCl); } } else { compileItem(name, opt, ctx, jrctx, summary, classLoader, level + 1, compile); } } } else { logger.debug("getResourcePaths() is null for " + jspName + ". Empty dir? Or Tomcat bug?"); } } protected JspCompilationContext createJspCompilationContext(String name, boolean isErrPage, Options opt, ServletContext sctx, JspRuntimeContext jrctx, ClassLoader cl) { JspCompilationContext jcctx = new JspCompilationContext(name, false, opt, sctx, null, jrctx); if (cl != null && cl instanceof URLClassLoader) { try { jcctx.setClassLoader((URLClassLoader) cl); } catch (NoSuchMethodError err) { //JBoss Web 2.1 has a different method signature for setClassLoader(). try { MethodUtils.invokeMethod(jcctx, "setClassLoader", cl); } catch (NoSuchMethodException ex) { throw new RuntimeException(ex); } catch (IllegalAccessException ex) { throw new RuntimeException(ex); } catch (InvocationTargetException ex) { throw new RuntimeException(ex); } } } return jcctx; } protected abstract void removeInternal(String name) throws Exception; protected abstract void installContextInternal(String contextName, File f) throws Exception; protected abstract Context findContextInternal(String contextName); }