FileDocCategorySizeDatePackage
TldConfig.javaAPI DocGlassfish v2 API31343Fri May 04 22:32:30 BST 2007org.apache.catalina.startup

TldConfig

public final class TldConfig extends Object
Startup event listener for a Context that configures the properties of that Context, and the associated defined servlets.
author
Craig R. McClanahan
author
Jean-Francois Arcand
author
Costin Manolache

Fields Summary
private static HashSet
noTldListeners
private static com.sun.org.apache.commons.logging.Log
log
private static final String
FILE_URL_PREFIX
private static final int
FILE_URL_PREFIX_LEN
private static boolean
cacheListeners
The variable that indicates whether or not to create/use a serialized cache of TLD listeners.
private static HashSet
systemTldUris
private static HashSet
systemTldUrisJsf
private org.apache.catalina.Context
context
The Context we are associated with.
private static final org.apache.catalina.util.StringManager
sm
The string resources for this package.
private com.sun.org.apache.commons.digester.Digester
tldDigester
The Digester we will use to process tag library descriptor files.
private boolean
isCurrentTldLocal
True if the TLD currently being scanned is locally bundled, false otherwise
private String
currentTldUri
The URI of the TLD currently being scanned
private boolean
tldValidation
Attribute value used to turn on/off TLD validation
private boolean
tldNamespaceAware
Attribute value used to turn on/off TLD namespace awarenes.
private boolean
rescan
private static boolean
scanParent
Scan the parent when searching for TLD listeners.
private ArrayList
listeners
private HashMap
tldUriToLocationMap
private String
currentTldResourcePath
private String
currentTldJarFile
private String
currentTldJarEntryName
private boolean
useMyFaces
Constructors Summary
Methods Summary
public voidaddApplicationListener(java.lang.String s)

        if (log.isDebugEnabled()) {
            log.debug( "Add tld listener " + s);
        }
        if ((isCurrentTldLocal
                    && !systemTldUris.contains(currentTldUri)
                    && (!systemTldUrisJsf.contains(currentTldUri)
                        || useMyFaces))
                || (!isCurrentTldLocal
                    && (!systemTldUrisJsf.contains(currentTldUri)
                        || !useMyFaces))) {
            listeners.add(s);
        }
    
private com.sun.org.apache.commons.digester.DigestercreateTldDigester()
Create (if necessary) and return a Digester configured to process a tag library descriptor, looking for additional listener classes to be registered.


        /* SJSAS 6384538
        return DigesterFactory.newDigester(tldValidation, 
                                           tldNamespaceAware, 
                                           new TldRuleSet());
        */
        // START SJSAS 6384538
        return DigesterFactory.newDigester(false, 
                                           tldNamespaceAware, 
                                           new TldRuleSet());
        // END SJSAS 6384538
    
public voidexecute()
Scan for and configure all tag library descriptors found in this web application.

exception
Exception if a fatal input/output or parsing error occurs


        
        long t1=System.currentTimeMillis();

        File tldCache=null;

        /* CR 6402120   
        if (context instanceof StandardContext) {
        */
        // START CR 6402120
        if (log.isDebugEnabled()) { 
            log.debug("Create/use TLD listener cache? "
                      + (isCacheListeners()));
        }

        // If multiple JVMs are running, then do not create tldCache.ser 
        // file as it will cause exceptions on the server side 
        // because of unsynchronized access of tldCache.ser file.
        if ((context instanceof StandardContext) && isCacheListeners()) {
        // END CR 6402120   
            File workDir= (File)
                ((StandardContext)context).getServletContext().getAttribute(Globals.WORK_DIR_ATTR);
            tldCache=new File( workDir, "tldCache.ser");
        }

        // Option to not rescan
        if( ! rescan ) {
            // find the cache
            if( tldCache!= null && tldCache.exists()) {
                try {
                    processCache(tldCache);
                    return;
                } catch (Throwable t) {
                    log.warn("Error scanning " + tldCache, t);
                }
            }
        }

        /*
         * Acquire the list of TLD resource paths, possibly embedded in JAR
         * files, to be processed
         */
        Set resourcePaths = tldScanResourcePaths();              
        Map jarPaths = getJarPaths();
        
        // Check to see if we can use cached listeners
        if (tldCache != null && tldCache.exists()) {
            long lastModified = getLastModified(resourcePaths, jarPaths);
            if (lastModified < tldCache.lastModified()) {
                try {
                    processCache(tldCache);
                    return;
                } catch (Throwable t) {
                    log.warn("Error scanning " + tldCache, t);
                }
            }
        }

        // Scan each accumulated resource path for TLDs to be processed
        Iterator<String> paths = resourcePaths.iterator();
        while (paths.hasNext()) {
            tldScanTld(paths.next());
        }
        if (jarPaths != null) {
            Iterator<JarPathElement> elems = jarPaths.values().iterator();
            while (elems.hasNext()) {
                JarPathElement elem = elems.next();
                tldScanJar(elem.getJarFile(), elem.getIsLocal());
            }
        }

        String list[] = getTldListeners();

        if( tldCache!= null ) {
            log.debug( "Saving tld cache: " + tldCache + " " + list.length);
            try {
                FileOutputStream out=new FileOutputStream(tldCache);
                ObjectOutputStream oos=new ObjectOutputStream( out );
                oos.writeObject( list );
                oos.close();
            } catch( IOException ex ) {
                ex.printStackTrace();
            }
        }

        if( log.isDebugEnabled() )
            log.debug( "Adding tld listeners:" + list.length);
        for( int i=0; list!=null && i<list.length; i++ ) {
            context.addApplicationListener(list[i]);
        }

        long t2=System.currentTimeMillis();
        if( context instanceof StandardContext ) {
            ((StandardContext)context).setTldScanTime(t2-t1);
        }

        // START GlassFish 747
        context.getServletContext().setAttribute(
            Globals.JSP_TLD_URI_TO_LOCATION_MAP,
            tldUriToLocationMap);
        // END GlassFish 747
    
public org.apache.catalina.ContextgetContext()

        return context;
    
private java.util.MapgetJarPaths()
Returns a map of the paths to all JAR files that are accessible to the webapp and will be scanned for TLDs and their listeners. The map always includes all the JARs under WEB-INF/lib, as well as shared JARs in the classloader delegation chain of the webapp's classloader. The latter constitutes a Tomcat-specific extension to the TLD search order defined in the JSP spec. It allows tag libraries packaged as JAR files to be shared by web applications by simply dropping them in a location that all web applications have access to (e.g., /common/lib). The set of shared JARs to be scanned for TLDs is narrowed down by the noTldListeners class variable, which contains the names of JARs that are known not to contain any TLDs that declare servlet listeners.

return
Map of JAR file paths


        HashMap jarPathMap = null;

        ClassLoader webappLoader = Thread.currentThread().getContextClassLoader();
        ClassLoader loader = webappLoader;
        while (loader != null) {
            if (loader instanceof URLClassLoader) {
                URL[] urls = ((URLClassLoader) loader).getURLs();
                for (int i=0; i<urls.length; i++) {
                    // Expect file URLs
                    // This is definitely not as clean as using JAR URLs either
                    // over file or the custom jndi handler, but a lot less
                    // buggy overall
                    File file = new File(
                            RequestUtil.URLDecode(urls[i].getFile()));
                    try {
                        file = file.getCanonicalFile();
                    } catch (IOException e) {
                        // Ignore
                    }
                    if (!file.exists()) {
                        continue;
                    }
                    String path = file.getAbsolutePath();
                    if (!path.endsWith(".jar")) {
                        continue;
                    }
                    /*
                     * Scan all JARs from WEB-INF/lib, plus any shared JARs
                     * that are not known not to contain any TLDs with
                     * listeners
                     */
                    if (loader == webappLoader
                            || noTldListeners == null
                            || !noTldListeners.contains(file.getName())) {
                        JarPathElement elem = new JarPathElement(
                                file, loader == webappLoader);
                        if (jarPathMap == null) {
                            jarPathMap = new HashMap();
                            jarPathMap.put(path, elem);
                        } else if (!jarPathMap.containsKey(path)) {
                            jarPathMap.put(path, elem);
                        }
                    }
                }
            }
            
            // START SJSAS 8.1 5049111
            if ( scanParent || context.isJsfApplication() ) {
            // END SJSAS 8.1 5049111    
                loader = loader.getParent();
            // START SJSAS 8.1 5049111
            } else {
                loader = null;
            }
            // END SJSAS 8.1 5049111                
        }

        return jarPathMap;
    
private longgetLastModified(java.util.Set resourcePaths, java.util.Map jarPaths)


        long lastModified = 0;

        Iterator paths = resourcePaths.iterator();
        while (paths.hasNext()) {
            String path = (String) paths.next();
            URL url = context.getServletContext().getResource(path);
            if (url == null) {
                log.debug( "Null url "+ path );
                break;
            }
            long lastM = url.openConnection().getLastModified();
            if (lastM > lastModified) lastModified = lastM;
            if (log.isDebugEnabled()) {
                log.debug( "Last modified " + path + " " + lastM);
            }
        }

        if (jarPaths != null) {
            Iterator<JarPathElement> elems = jarPaths.values().iterator();
            while (elems.hasNext()) {
                JarPathElement elem = elems.next();
                File jarFile = elem.getJarFile();
                long lastM = jarFile.lastModified();
                if (lastM > lastModified) lastModified = lastM;
                if (log.isDebugEnabled()) {
                    log.debug("Last modified " + jarFile.getAbsolutePath()
                              + " " + lastM);
                }
            }
        }

        return lastModified;
    
public static booleangetScanParentTldListener()

        return scanParent;
    
public java.lang.String[]getTldListeners()

        String result[]=new String[listeners.size()];
        listeners.toArray(result);
        return result;
    
public booleangetTldNamespaceAware()
Get the server.xml attribute's xmlNamespaceAware.

return
true if namespace awarenes is enabled.

        return tldNamespaceAware;
    
public booleangetTldValidation()
Get the server.xml attribute's xmlValidation.

return
true if validation is enabled.

        return tldValidation;
    
public static booleanisCacheListeners()
Indicates if a serialized cache of listeners is to be created and used

return
true if the listener cache file is to be used, false otherwise.

        return cacheListeners;
    
public booleanisRescan()

        return rescan;
    
private voidprocessCache(java.io.File tldCache)
Reads the cache of listeners specified in TLD files.

        FileInputStream in=new FileInputStream(tldCache);
        ObjectInputStream ois=new ObjectInputStream( in );
        String list[]=(String [])ois.readObject();
        if( log.isDebugEnabled() ) {
            log.debug("Reusing tldCache " + tldCache + " " + list.length);
        }
        for( int i=0; list!=null && i<list.length; i++ ) {
            // Load the listener class. Failure to do so is an indication
            // that the cache has become stale, in which case it must be
            // ignored. See GlassFish Issue 2653.
            context.getLoader().getClassLoader().loadClass(list[i]);
            context.addApplicationListener(list[i]);
        }
        ois.close();
    
public static voidsetCacheListeners(boolean cache)
Sets the flag that indicates whether to create/use a serialized cache of listeners

param
cache true to create/use a listener cache, false otherwise

        cacheListeners = cache;
    
public voidsetContext(org.apache.catalina.Context context)

        this.context = context;
        this.useMyFaces = ((StandardContext) context).isUseMyFaces();
    
public static voidsetNoTldListeners(java.lang.String jarNames)
Sets the list of JAR files that are known not to contain any TLDs that declare servlet listeners. Only shared JAR files (that is, those loaded by a delegation parent of the webapp's classloader) will be checked against this list.

param
jarNames List of comma-separated names of JAR files that are known not to contain any TLDs that declare servlet listeners

        if (jarNames != null) {
            if (noTldListeners == null) {
                noTldListeners = new HashSet<String>();
            } else {
                noTldListeners.clear();
            }
            StringTokenizer tokenizer = new StringTokenizer(jarNames, ",");
            while (tokenizer.hasMoreElements()) {
                noTldListeners.add(tokenizer.nextToken());
            }
        }
    
public static voidsetNoTldListeners(java.util.HashSet set)
Sets the list of JAR files that are known not to contain any TLDs that declare servlet listeners. Only shared JAR files (that is, those loaded by a delegation parent of the webapp's classloader) will be checked against this list.

param
set HashSet containing the names of JAR file known not to contain any TLDs that declare servlet listeners

        noTldListeners = set;
    
public voidsetRescan(boolean rescan)

        this.rescan = rescan;
    
public static voidsetScanParentTldListener(boolean scan)
Scan the parent when searching for TLD listeners.

        scanParent = scan;
    
public static voidsetSingleProcess(boolean isSingleProcess)

deprecated
Provided for backwards compatibility only. Use setCacheListeners instead.

        cacheListeners = isSingleProcess;
    
public voidsetTldNamespaceAware(boolean tldNamespaceAware)
Set the namespace aware feature of the XML parser used when parsing xml instances.

param
xmlNamespaceAware true to enable namespace awareness

        this.tldNamespaceAware = tldNamespaceAware;
    
public voidsetTldUri(java.lang.String uri)

        this.currentTldUri = uri;
        // START GlassFish 747
        /*
         * Add the mapping for the given URI only if
         * - the corresponding TLD is local, and
         * - the URI is not one of the standard (i.e., JSTL or JSF) ones, and
         * - the URI is not already mapped (this check is necessary because 
         *   taglibs specified in web.xml are supposed to take precedence)
         */
        if (isCurrentTldLocal
                && !systemTldUris.contains(currentTldUri)
                && (!systemTldUrisJsf.contains(currentTldUri)
                    || useMyFaces)
                && tldUriToLocationMap.get(currentTldUri) == null) {
            String[] currentTldLocation = new String[2];
            if (currentTldResourcePath != null) {
                currentTldLocation[0] = currentTldResourcePath;
            } else if (currentTldJarFile != null
                    && currentTldJarEntryName != null) {
                currentTldLocation[0] = "file:" + currentTldJarFile;
                currentTldLocation[1] = currentTldJarEntryName;
            }
            tldUriToLocationMap.put(currentTldUri, currentTldLocation);
        }
        // END GlassFish 747
    
public voidsetTldValidation(boolean tldValidation)
Set the validation feature of the XML parser used when parsing xml instances.

param
xmlValidation true to enable xml instance validation

        this.tldValidation = tldValidation;
    
public voidsetUseMyFaces(boolean useMyFaces)



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


        
        this.useMyFaces = useMyFaces;
    
private voidtldScanJar(java.io.File file)
Scans all TLD entries in the given JAR for application listeners.

param
file JAR file whose TLD entries are scanned for application listeners

        tldScanJar(file, false);
    
private voidtldScanJar(java.io.File file, boolean isLocal)


        JarFile jarFile = null;
        String name = null;

        String jarPath = file.getAbsolutePath();

        try {
            jarFile = new JarFile(file);
            Enumeration entries = jarFile.entries();
            while (entries.hasMoreElements()) {
                JarEntry entry = (JarEntry) entries.nextElement();
                name = entry.getName();
                if (!name.startsWith("META-INF/")) {
                    continue;
                }
                if (!name.endsWith(".tld")) {
                    continue;
                }
                if (log.isTraceEnabled()) {
                    log.trace("  Processing TLD at '" + name + "'");
                }
                // START GlassFish 747
                currentTldJarFile = jarPath;
                currentTldJarEntryName = name;
                // END GlassFish 747
                try {
                    tldScanStream(new InputSource(jarFile.getInputStream(entry)),
                                  isLocal);
                } catch (Exception e) {
                    log.error(sm.getString("contextConfig.tldEntryException",
                                           name, jarPath, context.getPath()),
                              e);
                }
            }
        } catch (Exception e) {
            log.error(sm.getString("contextConfig.tldJarException",
                                   jarPath, context.getPath()),
                      e);
        } finally {
            if (jarFile != null) {
                try {
                    jarFile.close();
                } catch (Throwable t) {
                    // Ignore
                }
            }
        }
    
private java.util.SettldScanResourcePaths()
Accumulate and return a Set of resource paths to be analyzed for tag library descriptors. Each element of the returned set will be the context-relative path to either a tag library descriptor file, or to a JAR file that may contain tag library descriptors in its META-INF subdirectory.

exception
IOException if an input/output error occurs while accumulating the list of resource paths

        if (log.isTraceEnabled()) {
            log.trace(" Accumulating TLD resource paths");
        }
        Set resourcePaths = new HashSet();

        // Accumulate resource paths explicitly listed in the web application
        // deployment descriptor
        if (log.isTraceEnabled()) {
            log.trace("  Scanning <taglib> elements in web.xml");
        }
        String taglibs[] = context.findTaglibs();
        for (int i = 0; i < taglibs.length; i++) {
            String resourcePath = context.findTaglib(taglibs[i]);
            // FIXME - Servlet 2.4 DTD implies that the location MUST be
            // a context-relative path starting with '/'?
            if (!resourcePath.startsWith("/")) {
                resourcePath = "/WEB-INF/" + resourcePath;
            }
            if (log.isTraceEnabled()) {
                log.trace("   Adding path '" + resourcePath +
                    "' for URI '" + taglibs[i] + "'");
            }
            // START GlassFish 747
            tldUriToLocationMap.put(taglibs[i],
                                    new String[] { resourcePath, null});
            // END GlassFish 747
            resourcePaths.add(resourcePath);
        }

        DirContext resources = context.getResources();
        if (resources != null) {
            tldScanResourcePathsWebInf(resources, "/WEB-INF", resourcePaths);
        }

        // Return the completed set
        return (resourcePaths);

    
private voidtldScanResourcePathsWebInf(javax.naming.directory.DirContext resources, java.lang.String rootPath, java.util.Set tldPaths)


        if (log.isTraceEnabled()) {
            log.trace("  Scanning TLDs in " + rootPath + " subdirectory");
        }

        try {
            NamingEnumeration items = resources.list(rootPath);
            while (items.hasMoreElements()) {
                NameClassPair item = (NameClassPair) items.nextElement();
                String resourcePath = rootPath + "/" + item.getName();
                if (resourcePath.startsWith("/WEB-INF/tags")) {
                    continue;
                }
                if (resourcePath.endsWith(".tld")) {
                    if (log.isTraceEnabled()) {
                        log.trace("   Adding path '" + resourcePath + "'");
                    }
                    tldPaths.add(resourcePath);
                } else {
                    tldScanResourcePathsWebInf(resources, resourcePath,
                                               tldPaths);
                }
            }
        } catch (NamingException e) {
            ; // Silent catch: it's valid that no /WEB-INF directory exists
        }
    
private voidtldScanStream(org.xml.sax.InputSource resourceStream)
Scan the TLD contents in the specified input stream, and register any application event listeners found there. NOTE - It is the responsibility of the caller to close the InputStream after this method returns.

param
resourceStream InputStream containing a tag library descriptor
exception
Exception if an exception occurs while scanning this TLD

        tldScanStream(resourceStream, false);
    
private voidtldScanStream(org.xml.sax.InputSource resourceStream, boolean isLocal)


        if (tldDigester == null){
            tldDigester = createTldDigester();
        }
        
        synchronized (tldDigester) {
            try {
                tldDigester.push(this);
                isCurrentTldLocal = isLocal;
                tldDigester.parse(resourceStream);
            } finally {
                isCurrentTldLocal = false;
                currentTldUri = null;
                // START GlassFish 747
                currentTldJarFile = null;
                currentTldJarEntryName = null;
                currentTldResourcePath = null;
                // END GlassFish 747
                tldDigester.push(null);
                tldDigester.clear();
            }
        }

    
private voidtldScanTld(java.lang.String resourcePath)
Scan the TLD contents at the specified resource path, and register any application event listeners found there.

param
resourcePath Resource path being scanned
exception
Exception if an exception occurs while scanning this TLD


        if (log.isDebugEnabled()) {
            log.debug(" Scanning TLD at resource path '" + resourcePath + "'");
        }

        InputStream tldStream =
            context.getServletContext().getResourceAsStream(resourcePath);
        if (tldStream == null) {
            throw new ServletException
                (sm.getString("contextConfig.tldResourcePath",
                              resourcePath));
        }

        InputSource inputSource = new InputSource(tldStream);

        // START GlassFish 747
        currentTldResourcePath = resourcePath;
        // END GlassFish 747

        try {
            tldScanStream(inputSource, true);
        } catch (Exception e) {
             throw new ServletException
                 (sm.getString("contextConfig.tldFileException", resourcePath,
                               context.getPath()),
                  e);
        }