src/misc/compiler/net/sf/basedb/clients/web/extensions/XJspCompiler.java

Code
Comments
Other
Rev Date Author Line
4471 05 Sep 08 jari 1 /**
4471 05 Sep 08 jari 2   $Id $
4471 05 Sep 08 jari 3
4471 05 Sep 08 jari 4   Copyright (C) Authors contributing to this file.
4471 05 Sep 08 jari 5
4471 05 Sep 08 jari 6   This file is part of BASE - BioArray Software Environment.
4471 05 Sep 08 jari 7   Available at http://base.thep.lu.se/
4471 05 Sep 08 jari 8
4471 05 Sep 08 jari 9   BASE is free software; you can redistribute it and/or
4471 05 Sep 08 jari 10   modify it under the terms of the GNU General Public License
4480 05 Sep 08 jari 11   as published by the Free Software Foundation; either version 3
4471 05 Sep 08 jari 12   of the License, or (at your option) any later version.
4471 05 Sep 08 jari 13
4471 05 Sep 08 jari 14   BASE is distributed in the hope that it will be useful,
4471 05 Sep 08 jari 15   but WITHOUT ANY WARRANTY; without even the implied warranty of
4471 05 Sep 08 jari 16   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
4471 05 Sep 08 jari 17   GNU General Public License for more details.
4471 05 Sep 08 jari 18
4471 05 Sep 08 jari 19   You should have received a copy of the GNU General Public License
4512 11 Sep 08 jari 20   along with BASE. If not, see <http://www.gnu.org/licenses/>.
4471 05 Sep 08 jari 21 */
4471 05 Sep 08 jari 22 package net.sf.basedb.clients.web.extensions;
4471 05 Sep 08 jari 23
4471 05 Sep 08 jari 24 import java.io.File;
4471 05 Sep 08 jari 25 import java.io.FileNotFoundException;
4471 05 Sep 08 jari 26 import java.lang.reflect.Method;
4471 05 Sep 08 jari 27 import java.net.URL;
4471 05 Sep 08 jari 28 import java.net.URLClassLoader;
4471 05 Sep 08 jari 29 import java.util.regex.Matcher;
4471 05 Sep 08 jari 30 import java.util.regex.Pattern;
4471 05 Sep 08 jari 31
4471 05 Sep 08 jari 32 import org.apache.jasper.JasperException;
4471 05 Sep 08 jari 33 import org.apache.jasper.JspCompilationContext;
4471 05 Sep 08 jari 34 import org.apache.jasper.compiler.JDTCompiler;
4471 05 Sep 08 jari 35 import org.apache.jasper.servlet.JspServletWrapper;
5840 01 Nov 11 nicklas 36 import org.apache.juli.logging.Log;
5840 01 Nov 11 nicklas 37 import org.apache.juli.logging.LogFactory;
4471 05 Sep 08 jari 38
4471 05 Sep 08 jari 39 /**
4471 05 Sep 08 jari 40   Class that enabled compilation of custom JSP pages that are 
4471 05 Sep 08 jari 41   part of an extension that uses classes from the extension JAR
4471 05 Sep 08 jari 42   file (eg. a JAR file in the /WEB-INF/extensions directory). 
4471 05 Sep 08 jari 43   <p>
4471 05 Sep 08 jari 44   
4471 05 Sep 08 jari 45   To make this compiler work the following is required:
4471 05 Sep 08 jari 46   <ul>
4471 05 Sep 08 jari 47   <li>The JSP file must have a '.xjsp' extension.
4471 05 Sep 08 jari 48   <li>This compiler class must be installed in Tomcat's /lib
4471 05 Sep 08 jari 49     directory.
4471 05 Sep 08 jari 50   <li>The '.xjsp' extensions must be mapped to the regular JSP servlet
4471 05 Sep 08 jari 51     with the 'compilerClass' parameter set to this class.
4471 05 Sep 08 jari 52   </ul>
4471 05 Sep 08 jari 53   
7612 01 Mar 19 nicklas 54   Note! This feature is experimental. We only test it at unregular intervals
7612 01 Mar 19 nicklas 55   and since it depends on internal implementation details of Tomcat it may 
7612 01 Mar 19 nicklas 56   or may not work with future or older versions  of Tomcat.
4471 05 Sep 08 jari 57   
4471 05 Sep 08 jari 58   @author nicklas
4471 05 Sep 08 jari 59   @version 2.7
4471 05 Sep 08 jari 60   @base.modified $Date:2008-03-20 12:15:25 +0100 (Thu, 20 Mar 2008) $
4471 05 Sep 08 jari 61 */
4471 05 Sep 08 jari 62 public class XJspCompiler 
4471 05 Sep 08 jari 63   extends JDTCompiler
4471 05 Sep 08 jari 64 {
4471 05 Sep 08 jari 65   
4471 05 Sep 08 jari 66   /**
4471 05 Sep 08 jari 67     The location where the extensions resources are extracted.
4471 05 Sep 08 jari 68   */
4471 05 Sep 08 jari 69   public static final String RESOURCES_URL = "/extensions";
4471 05 Sep 08 jari 70
4471 05 Sep 08 jari 71   /**
4471 05 Sep 08 jari 72     Path to JSP must match: /extensions/[jar-file-name]/[jsp-path].xjsp
4471 05 Sep 08 jari 73     group(1) = JAR name, group(2) = JSP path
4471 05 Sep 08 jari 74   */
4471 05 Sep 08 jari 75   private static final Pattern PATH_MATCH = 
4471 05 Sep 08 jari 76     Pattern.compile(RESOURCES_URL + "/([^/]+)/(.+\\.xjsp)");
5840 01 Nov 11 nicklas 77    
5840 01 Nov 11 nicklas 78   private final Log log;
5840 01 Nov 11 nicklas 79   
5746 15 Sep 11 nicklas 80   // The path to the plug-ins directory
5746 15 Sep 11 nicklas 81   private File pluginsDir;
4471 05 Sep 08 jari 82   // If this JSP is an extensions JSP
4471 05 Sep 08 jari 83   private boolean isXJsp;
4471 05 Sep 08 jari 84   // The extensions JAR file
4471 05 Sep 08 jari 85   private File extensionsJar;
4471 05 Sep 08 jari 86   // The last modified time of the currently loaded extensions JAR file
4471 05 Sep 08 jari 87   private long lastModified;
4471 05 Sep 08 jari 88   
4471 05 Sep 08 jari 89   public XJspCompiler()
5840 01 Nov 11 nicklas 90   {
5840 01 Nov 11 nicklas 91     log = LogFactory.getLog(XJspCompiler.class);
5840 01 Nov 11 nicklas 92   }
4471 05 Sep 08 jari 93   
4471 05 Sep 08 jari 94   /**
4471 05 Sep 08 jari 95     From the JDTCompiler class and it's superclasses
4471 05 Sep 08 jari 96     ------------------------------------------------
4471 05 Sep 08 jari 97   */
4471 05 Sep 08 jari 98   /**
4471 05 Sep 08 jari 99     Initialises the compiler. The JSP name is matched against the 
4471 05 Sep 08 jari 100     required pattern and the name of the extensions JAR file is
4471 05 Sep 08 jari 101     extracted from the request path.
4471 05 Sep 08 jari 102   */
4471 05 Sep 08 jari 103   @Override
5746 15 Sep 11 nicklas 104   public void init(JspCompilationContext ctxt, JspServletWrapper jsw)
4471 05 Sep 08 jari 105   {
5746 15 Sep 11 nicklas 106     super.init(ctxt, jsw);
4471 05 Sep 08 jari 107     String jspName = ctxt.getJspFile();
5746 15 Sep 11 nicklas 108     if (log.isDebugEnabled())
5746 15 Sep 11 nicklas 109     {
5746 15 Sep 11 nicklas 110       log.debug("Initialising XJSP compiler for file: " + jspName);
5746 15 Sep 11 nicklas 111     }
4471 05 Sep 08 jari 112     Matcher m = PATH_MATCH.matcher(jspName);
4471 05 Sep 08 jari 113     if (m.matches())
4471 05 Sep 08 jari 114     {
4471 05 Sep 08 jari 115       String jarName = m.group(1);
5746 15 Sep 11 nicklas 116       File pluginsDir = getPluginsDir();
5746 15 Sep 11 nicklas 117       extensionsJar = new File(pluginsDir, jarName);
4471 05 Sep 08 jari 118       isXJsp = extensionsJar.exists();
5746 15 Sep 11 nicklas 119       }
4471 05 Sep 08 jari 120     if (log.isDebugEnabled())
4471 05 Sep 08 jari 121     {
5746 15 Sep 11 nicklas 122       log.debug("JAR=" + extensionsJar + "; isXJsp=" + isXJsp);
4471 05 Sep 08 jari 123     }
4471 05 Sep 08 jari 124   }
4471 05 Sep 08 jari 125
4471 05 Sep 08 jari 126   /**
4471 05 Sep 08 jari 127     Called when a JSP page needs recompiling. We check if the extension JAR file
4471 05 Sep 08 jari 128     has been modified and update the class loader if it has.
4471 05 Sep 08 jari 129   */
4471 05 Sep 08 jari 130   @Override
7612 01 Mar 19 nicklas 131   public void compile(boolean compileClass, boolean jspcMode)
4471 05 Sep 08 jari 132     throws FileNotFoundException, JasperException, Exception 
4471 05 Sep 08 jari 133   {
4471 05 Sep 08 jari 134     if (log.isDebugEnabled())
4471 05 Sep 08 jari 135     {
4471 05 Sep 08 jari 136       log.debug("Generate class for file: " + ctxt.getJspFile() + 
4471 05 Sep 08 jari 137         "; JAR=" + extensionsJar + "; isXJsp=" + isXJsp);
4471 05 Sep 08 jari 138     }
4471 05 Sep 08 jari 139     setContextClassLoader();
7612 01 Mar 19 nicklas 140     super.compile(compileClass, jspcMode);
4471 05 Sep 08 jari 141   }
4471 05 Sep 08 jari 142
4471 05 Sep 08 jari 143   /**
4471 05 Sep 08 jari 144     Checks if the extensions JAR file has been modified. If not,
4471 05 Sep 08 jari 145     calls super.isOutDated(checkClass)
4471 05 Sep 08 jari 146   */
4471 05 Sep 08 jari 147   @Override
4471 05 Sep 08 jari 148   public boolean isOutDated(boolean checkClass)
4471 05 Sep 08 jari 149   {
4471 05 Sep 08 jari 150     if (isXJsp && lastModified != extensionsJar.lastModified()) 
4471 05 Sep 08 jari 151     {
4471 05 Sep 08 jari 152       return true;
4471 05 Sep 08 jari 153     }
4471 05 Sep 08 jari 154     return super.isOutDated(checkClass);
4471 05 Sep 08 jari 155   }
4471 05 Sep 08 jari 156   // ---------------------------------------------
4471 05 Sep 08 jari 157   
4471 05 Sep 08 jari 158   /**
4471 05 Sep 08 jari 159     Sets the context class loader if this JSP page is an
4471 05 Sep 08 jari 160     extension JSP page and if the extensions JAR file has been
4471 05 Sep 08 jari 161     modified since the last time the class loader was set.
4471 05 Sep 08 jari 162     This method does nothing if the JSP file is not an extensions
4471 05 Sep 08 jari 163     JSP file, or if the JAR file hasn't changed since the last
4471 05 Sep 08 jari 164     use.
4471 05 Sep 08 jari 165   */
4471 05 Sep 08 jari 166   protected void setContextClassLoader()
4471 05 Sep 08 jari 167     throws Exception
4471 05 Sep 08 jari 168   {
4471 05 Sep 08 jari 169     if (!isXJsp || lastModified == extensionsJar.lastModified()) return;
4471 05 Sep 08 jari 170
4471 05 Sep 08 jari 171     if (log.isDebugEnabled())
4471 05 Sep 08 jari 172     {
4471 05 Sep 08 jari 173       log.debug("Setting class loader for file: " + ctxt.getJspFile() + 
4471 05 Sep 08 jari 174         "; JAR=" + extensionsJar + "; isXJsp=" + isXJsp);
4471 05 Sep 08 jari 175     }
4471 05 Sep 08 jari 176
4471 05 Sep 08 jari 177     URLClassLoader classLoader = getExtensionClassLoader();
4471 05 Sep 08 jari 178     ctxt.setClassLoader(classLoader);
4471 05 Sep 08 jari 179     lastModified = extensionsJar.lastModified();
4471 05 Sep 08 jari 180   }
4471 05 Sep 08 jari 181   
4471 05 Sep 08 jari 182   /**
4471 05 Sep 08 jari 183     Get a class loader that can load classes from the current web application
4471 05 Sep 08 jari 184     as well as the extensions JAR file. The class loader for the current
4471 05 Sep 08 jari 185     web application can be found in JspRuntimeContext.getParentClassLoader().
4471 05 Sep 08 jari 186     The extensions class loader is loaded from JarClassLoader in BASE.
4471 05 Sep 08 jari 187   */
4471 05 Sep 08 jari 188   protected URLClassLoader getExtensionClassLoader()
4471 05 Sep 08 jari 189     throws Exception
4471 05 Sep 08 jari 190   {
4471 05 Sep 08 jari 191     ClassLoader webAppLoader = getWebAppClassLoader();
4471 05 Sep 08 jari 192
4471 05 Sep 08 jari 193     // Get a reference to the JarClassLoader.getInstance(String, boolean) method
4471 05 Sep 08 jari 194     Class<?> jarClass = Class.forName("net.sf.basedb.util.JarClassLoader", true, webAppLoader);
4471 05 Sep 08 jari 195     Method getInstance = jarClass.getMethod("getInstance", String.class, boolean.class);
4471 05 Sep 08 jari 196
4471 05 Sep 08 jari 197     // Call JarClassLoader.getInstance(jarPath, true)
4471 05 Sep 08 jari 198     ClassLoader jarLoader = (ClassLoader)getInstance.invoke(null, extensionsJar.getAbsolutePath(), true);
4471 05 Sep 08 jari 199     return new URLClassLoader(new URL[0], jarLoader);
4471 05 Sep 08 jari 200   }
4471 05 Sep 08 jari 201   
4471 05 Sep 08 jari 202   /**
4471 05 Sep 08 jari 203     Get the class loader for the web application this JSP is
4471 05 Sep 08 jari 204     located in. Default implementation returns JspRuntimeContext.getParentClassLoader()
4471 05 Sep 08 jari 205   */
4471 05 Sep 08 jari 206   protected ClassLoader getWebAppClassLoader()
4471 05 Sep 08 jari 207   {
5746 15 Sep 11 nicklas 208     return getCompilationContext().getRuntimeContext().getParentClassLoader();
4471 05 Sep 08 jari 209   }
4471 05 Sep 08 jari 210
5746 15 Sep 11 nicklas 211   /**
5746 15 Sep 11 nicklas 212     Get the path to the plug-in directory from BASE.
5746 15 Sep 11 nicklas 213     @since 3.0
5746 15 Sep 11 nicklas 214   */
5746 15 Sep 11 nicklas 215   protected File getPluginsDir()
5746 15 Sep 11 nicklas 216   {
5746 15 Sep 11 nicklas 217     if (pluginsDir == null)
5746 15 Sep 11 nicklas 218     {
5746 15 Sep 11 nicklas 219       try
5746 15 Sep 11 nicklas 220       {
5746 15 Sep 11 nicklas 221         ClassLoader webAppLoader = getWebAppClassLoader();
5746 15 Sep 11 nicklas 222         Class<?> appClass = Class.forName("net.sf.basedb.core.Application", true, webAppLoader);
5746 15 Sep 11 nicklas 223         Method getPluginsDirectory = appClass.getMethod("getPluginsDirectory");
5746 15 Sep 11 nicklas 224         pluginsDir = (File)getPluginsDirectory.invoke(null);
5746 15 Sep 11 nicklas 225       }
5746 15 Sep 11 nicklas 226       catch (Exception ex)
5746 15 Sep 11 nicklas 227       {
5746 15 Sep 11 nicklas 228         log.error("Could not get plug-ins directory", ex);
5746 15 Sep 11 nicklas 229       }
5746 15 Sep 11 nicklas 230     }
5746 15 Sep 11 nicklas 231     return pluginsDir;
5746 15 Sep 11 nicklas 232   }
4471 05 Sep 08 jari 233   
4471 05 Sep 08 jari 234 }