FileDocCategorySizeDatePackage
JspRuntimeContext.javaAPI DocGlassfish v2 API23140Fri May 04 22:32:52 BST 2007org.apache.jasper.compiler

JspRuntimeContext

public final class JspRuntimeContext extends Object implements Runnable
Class for tracking JSP compile time file dependencies when the &060;%@include file="..."%&062; directive is used. A background thread periodically checks the files a JSP page is dependent upon. If a dpendent file changes the JSP page which included it is recompiled. Only used if a web application context is a directory.
author
Glenn L. Nielsen
version
$Revision: 1.14 $

Fields Summary
private static com.sun.org.apache.commons.logging.Log
log
private int
jspReloadCount
private ServletContext
context
This web applications ServletContext
private org.apache.jasper.Options
options
private URLClassLoader
parentClassLoader
private PermissionCollection
permissionCollection
private CodeSource
codeSource
private String
classpath
private Map
jsps
Maps JSP pages to their JspServletWrapper's
private Map
bytecodes
Maps class names to in-memory bytecodes
private Map
bytecodeBirthTimes
private Map
packageMap
Maps classes in packages compiled by the JSP compiler. Used only by Jsr199Compiler. Should be Map>, is this way now so not to be dependent on the JSP199 API at build time.
private Thread
thread
The background thread.
private boolean
threadDone
The background thread completion semaphore.
private String
threadName
Name to register for the background thread.
Constructors Summary
public JspRuntimeContext(ServletContext context, org.apache.jasper.Options options)
Create a JspRuntimeContext for a web application context. Loads in any previously generated dependencies from file.

param
context ServletContext for web application


                           
     
        JspFactoryImpl factory = new JspFactoryImpl();
        SecurityClassLoad.securityClassLoad(factory.getClass().getClassLoader());
        if( System.getSecurityManager() != null ) {
            String basePackage = "org.apache.jasper.";
            try {
                factory.getClass().getClassLoader().loadClass(
                    basePackage
                    + "runtime.JspFactoryImpl$PrivilegedGetPageContext");
                factory.getClass().getClassLoader().loadClass(
                    basePackage
                    + "runtime.JspFactoryImpl$PrivilegedReleasePageContext");
                factory.getClass().getClassLoader().loadClass(
                    basePackage
                    + "runtime.JspRuntimeLibrary");
                factory.getClass().getClassLoader().loadClass(
                    basePackage
                    + "runtime.JspRuntimeLibrary$PrivilegedIntrospectHelper");
                factory.getClass().getClassLoader().loadClass(
                    basePackage
                    + "runtime.ServletResponseWrapperInclude");
                factory.getClass().getClassLoader().loadClass(
                    basePackage
                    + "servlet.JspServletWrapper");
            } catch (ClassNotFoundException ex) {
                log.error("Jasper JspRuntimeContext preload of class failed: "
                          + ex.getMessage(), ex);
            }
        }

        JspFactory.setDefaultFactory(factory);
    

        System.setErr(new SystemLogHandler(System.err));

        this.context = context;
        this.options = options;

        int hashSize = options.getInitialCapacity();
        jsps = new ConcurrentHashMap<String, JspServletWrapper>(hashSize);

        bytecodes = new ConcurrentHashMap<String, byte[]>(hashSize);
        bytecodeBirthTimes = new ConcurrentHashMap<String, Long>(hashSize);

        // Get the parent class loader
        parentClassLoader =
            (URLClassLoader) Thread.currentThread().getContextClassLoader();
        if (parentClassLoader == null) {
            parentClassLoader =
                (URLClassLoader)this.getClass().getClassLoader();
        }

	if (log.isTraceEnabled()) {
	    if (parentClassLoader != null) {
		log.trace(Localizer.getMessage("jsp.message.parent_class_loader_is",
					       parentClassLoader.toString()));
	    } else {
		log.trace(Localizer.getMessage("jsp.message.parent_class_loader_is",
					       "<none>"));
	    }
        }

        initClassPath();

	if (context instanceof org.apache.jasper.servlet.JspCServletContext) {
	    return;
	}

        if (Constants.IS_SECURITY_ENABLED) {
            initSecurity();
        }

        // If this web application context is running from a
        // directory, start the background compilation thread
        String appBase = context.getRealPath("/");         
        if (!options.getDevelopment()
                && appBase != null
                && options.getCheckInterval() > 0) {
            if (appBase.endsWith(File.separator) ) {
                appBase = appBase.substring(0,appBase.length()-1);
            }
            String directory =
                appBase.substring(appBase.lastIndexOf(File.separator));
            threadName = threadName + "[" + directory + "]";
            threadStart();
        }                                            
    
Methods Summary
public voidaddWrapper(java.lang.String jspUri, org.apache.jasper.servlet.JspServletWrapper jsw)
Add a new JspServletWrapper.

param
jspUri JSP URI
param
jsw Servlet wrapper for JSP


    // ------------------------------------------------------ Public Methods

                       
          
        jsps.remove(jspUri);
        jsps.put(jspUri,jsw);
    
public voidadjustBytecodeTime(java.lang.String name, long reference)

        Long time = bytecodeBirthTimes.get(name);
        if (time == null)
            return;

        if (time.longValue() < reference) {
            bytecodeBirthTimes.put(name, Long.valueOf(reference));
        }
    
private voidcheckCompile()
Method used by background thread to check the JSP dependencies registered with this class for JSP's.

        for (JspServletWrapper jsw: jsps.values()) {
            if (jsw.isTagFile()) {
                // Skip tag files in background compiliations, since modified
                // tag files will be recompiled anyway when their client JSP
                // pages are compiled.  This also avoids problems when the
                // tag files and their clients are not modified simultaneously.
                continue;
            }

            JspCompilationContext ctxt = jsw.getJspEngineContext();
            // JspServletWrapper also synchronizes on this when
            // it detects it has to do a reload
            synchronized(jsw) {
                try {
                    ctxt.compile();
                } catch (FileNotFoundException ex) {
                    ctxt.incrementRemoved();
                } catch (Throwable t) {
                    jsw.getServletContext().log("Background compile failed",
						t);
                }
            }
        }
    
public voiddestroy()
Process a "destory" event for this web application context.


        if(System.err instanceof SystemLogHandler)
            System.setErr(((SystemLogHandler)System.err).getWrapped());

        threadStop();

        for (JspServletWrapper jsw: jsps.values()) {
            jsw.destroy();
        }

        parentClassLoader = null;
    
public byte[]getBytecode(java.lang.String name)
Retrieve the bytecode associated with the class

        return bytecodes.get(name);
    
public longgetBytecodeBirthTime(java.lang.String name)
Retrieve the time the bytecode for a class was created

        Long time = bytecodeBirthTimes.get(name);
        return (time != null? time.longValue(): 0);
    
public java.util.MapgetBytecodes()
Get the class-name to bytecode map

        return bytecodes;
    
public java.lang.StringgetClassPath()
The classpath that is passed off to the Java compiler.

        return classpath;
    
public java.security.CodeSourcegetCodeSource()
Get the SecurityManager Policy CodeSource for this web applicaiton context.

return
CodeSource for JSP

        return codeSource;
    
public intgetJspCount()
Returns the number of JSPs for which JspServletWrappers exist, i.e., the number of JSPs that have been loaded into the webapp.

return
The number of JSPs that have been loaded into the webapp

        return jsps.size();
    
public intgetJspReloadCount()
Gets the current value of the JSP reload counter.

return
The current value of the JSP reload counter

        return jspReloadCount;
    
public java.util.MapgetPackageMap()
The packageMap keeps track of the bytecode files in a package generated by a java compiler. This is in turn loaded by the java compiler during compilation. This is gets around the fact that JSR199 API does not provide a way for the compiler use current classloader.

        if (packageMap == null) {
            packageMap = new HashMap<String, ArrayList<Object>>();
        }
        return packageMap;
    
public java.net.URLClassLoadergetParentClassLoader()
Get the parent URLClassLoader.

return
URLClassLoader parent

        return parentClassLoader;
    
public java.security.PermissionCollectiongetPermissionCollection()
Get the SecurityManager PermissionCollection for this web application context.

return
PermissionCollection permissions

        return permissionCollection;
    
public org.apache.jasper.servlet.JspServletWrappergetWrapper(java.lang.String jspUri)
Get an already existing JspServletWrapper.

param
jspUri JSP URI
return
JspServletWrapper for JSP

        return jsps.get(jspUri);
    
public synchronized voidincrementJspReloadCount()
Increments the JSP reload counter.

        jspReloadCount++;
    
private voidinitClassPath()
Method used to initialize classpath for compiles.


        URL [] urls = parentClassLoader.getURLs();
        StringBuffer cpath = new StringBuffer();
        String sep = System.getProperty("path.separator");

        for(int i = 0; i < urls.length; i++) {
            // Tomcat 4 can use URL's other than file URL's,
            // a protocol other than file: will generate a
            // bad file system path, so only add file:
            // protocol URL's to the classpath.
            
            if( urls[i].getProtocol().equals("file") ) {
                cpath.append((String)urls[i].getFile()+sep);
            }
        }    

	cpath.append(options.getScratchDir() + sep);

        String cp = (String) context.getAttribute(Constants.SERVLET_CLASSPATH);
        if (cp == null || cp.equals("")) {
            cp = options.getClassPath();
        }

        classpath = cpath.toString() + cp;

        // START GlassFish Issue 845
        try {
            classpath = URLDecoder.decode(classpath, "UTF-8");

        } catch (UnsupportedEncodingException e) {
            if (log.isDebugEnabled())
                log.debug("Exception decoding classpath : " + classpath, e);
        }
        // END GlassFish Issue 845
    
private voidinitSecurity()
Method used to initialize SecurityManager data.


        // Setup the PermissionCollection for this web app context
        // based on the permissions configured for the root of the
        // web app context directory, then add a file read permission
        // for that directory.
        Policy policy = Policy.getPolicy();
        if( policy != null ) {
            try {          
                // Get the permissions for the web app context
                String docBase = context.getRealPath("/");
                if( docBase == null ) {
                    docBase = options.getScratchDir().toString();
                }
                String codeBase = docBase;
                if (!codeBase.endsWith(File.separator)){
                    codeBase = codeBase + File.separator;
                }
                File contextDir = new File(codeBase);
                URL url = contextDir.getCanonicalFile().toURL();
                codeSource = new CodeSource(url,(Certificate[])null);
                permissionCollection = policy.getPermissions(codeSource);

                // Create a file read permission for web app context directory
                if (!docBase.endsWith(File.separator)){
                    permissionCollection.add
                        (new FilePermission(docBase,"read"));
                    docBase = docBase + File.separator;
                } else {
                    permissionCollection.add
                        (new FilePermission
                            (docBase.substring(0,docBase.length() - 1),"read"));
                }
                docBase = docBase + "-";
                permissionCollection.add(new FilePermission(docBase,"read"));

                // Create a file read permission for web app tempdir (work)
                // directory
                String workDir = options.getScratchDir().toString();
                if (!workDir.endsWith(File.separator)){
                    permissionCollection.add
                        (new FilePermission(workDir,"read"));
                    workDir = workDir + File.separator;
                }
                workDir = workDir + "-";
                permissionCollection.add(new FilePermission(workDir,"read"));

                // Allow the JSP to access org.apache.jasper.runtime.HttpJspBase
                permissionCollection.add( new RuntimePermission(
                    "accessClassInPackage.org.apache.jasper.runtime") );

                if (parentClassLoader instanceof URLClassLoader) {
                    URL [] urls = parentClassLoader.getURLs();
                    String jarUrl = null;
                    String jndiUrl = null;
                    for (int i=0; i<urls.length; i++) {
                        if (jndiUrl == null
                                && urls[i].toString().startsWith("jndi:") ) {
                            jndiUrl = urls[i].toString() + "-";
                        }
                        if (jarUrl == null
                                && urls[i].toString().startsWith("jar:jndi:")
                                ) {
                            jarUrl = urls[i].toString();
                            jarUrl = jarUrl.substring(0,jarUrl.length() - 2);
                            jarUrl = jarUrl.substring(0,
                                     jarUrl.lastIndexOf('/")) + "/-";
                        }
                    }
                    if (jarUrl != null) {
                        permissionCollection.add(
                                new FilePermission(jarUrl,"read"));
                        permissionCollection.add(
                                new FilePermission(jarUrl.substring(4),"read"));
                    }
                    if (jndiUrl != null)
                        permissionCollection.add(
                                new FilePermission(jndiUrl,"read") );
                }
            } catch(Exception e) {
                context.log("Security Init for context failed",e);
            }
        }
    
public voidremoveWrapper(java.lang.String jspUri)
Remove a JspServletWrapper.

param
jspUri JSP URI of JspServletWrapper to remove

        jsps.remove(jspUri);
    
public voidrun()
The background thread that checks for changes to files included by a JSP and flags that a recompile is required.

        
        // Loop until the termination semaphore is set
        while (!threadDone) {

            // Wait for our check interval
            threadSleep();

            // Check for included files which are newer than the
            // JSP which uses them.
            try {
                checkCompile();
            } catch (Throwable t) {
                t.printStackTrace();
            }
        }
        
    
public voidsaveBytecode(java.lang.String className, java.lang.String classFileName)
Save the bytecode for a class to disk.

        byte[] bytecode = getBytecode(className);
        if (bytecode != null) {
            try {
                FileOutputStream fos = new FileOutputStream(classFileName);
                fos.write(bytecode);
                fos.close();
            } catch (IOException ex) {
                context.log("Error in saving bytecode for " + className +
                    " to " + classFileName, ex);
            }
        }
    
public voidsetBytecode(java.lang.String name, byte[] bytecode)
Save the bytecode for the class in a map. The current time is noted.

param
name The name of the class
param
bytecode The bytecode in byte array

        if (bytecode == null) {
            bytecodes.remove(name);
            bytecodeBirthTimes.remove(name);
            return;
        }
        bytecodes.put(name, bytecode);
        bytecodeBirthTimes.put(name, Long.valueOf(System.currentTimeMillis()));
    
public synchronized voidsetJspReloadCount(int count)
Resets the JSP reload counter.

param
count Value to which to reset the JSP reload counter

        this.jspReloadCount = count;
    
protected voidthreadSleep()
Sleep for the duration specified by the checkInterval property.

        
        try {
            Thread.sleep(options.getCheckInterval() * 1000L);
        } catch (InterruptedException e) {
            ;
        }
        
    
protected voidthreadStart()
Start the background thread that will periodically check for changes to compile time included files in a JSP.

exception
IllegalStateException if we should not be starting a background thread now


        // Has the background thread already been started?
        if (thread != null) {
            return;
        }

        // Start the background thread
        threadDone = false;
        thread = new Thread(this, threadName);
        thread.setDaemon(true);
        thread.start();

    
protected voidthreadStop()
Stop the background thread that is periodically checking for changes to compile time included files in a JSP.


        if (thread == null) {
            return;
        }

        threadDone = true;
        thread.interrupt();
        try {
            thread.join();
        } catch (InterruptedException e) {
            ;
        }
        
        thread = null;