All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.catalina.startup.TldConfig Maven / Gradle / Ivy

There is a newer version: 11.0.0-M26
Show newest version
/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package org.apache.catalina.startup;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.JarURLConnection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

import javax.servlet.ServletContext;
import javax.servlet.descriptor.JspConfigDescriptor;
import javax.servlet.descriptor.TaglibDescriptor;

import org.apache.catalina.Context;
import org.apache.catalina.Lifecycle;
import org.apache.catalina.LifecycleEvent;
import org.apache.catalina.LifecycleListener;
import org.apache.catalina.core.StandardContext;
import org.apache.tomcat.JarScanType;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.JarScannerCallback;
import org.apache.tomcat.util.ExceptionUtils;
import org.apache.tomcat.util.descriptor.DigesterFactory;
import org.apache.tomcat.util.descriptor.XmlErrorHandler;
import org.apache.tomcat.util.descriptor.web.ApplicationListener;
import org.apache.tomcat.util.digester.Digester;
import org.apache.tomcat.util.res.StringManager;
import org.apache.tomcat.util.scan.Jar;
import org.apache.tomcat.util.scan.JarFactory;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;


/**
 * Startup event listener for a Context that configures application
 * listeners configured in any TLD files.
 *
 * @author Craig R. McClanahan
 * @author Jean-Francois Arcand
 * @author Costin Manolache
 */
public final class TldConfig  implements LifecycleListener {

    private static final String TLD_EXT = ".tld";
    private static final String WEB_INF = "/WEB-INF/";
    private static final String WEB_INF_CLASSES = "/WEB-INF/classes/";
    private static final String WEB_INF_LIB = "/WEB-INF/lib/";


    private static final org.apache.juli.logging.Log log=
        org.apache.juli.logging.LogFactory.getLog( TldConfig.class );

    /**
     * The string resources for this package.
     */
    private static final StringManager sm =
        StringManager.getManager(Constants.Package);

    /**
     * The Digesters available to process tld files.
     */
    private static final Digester[] tldDigesters = new Digester[4];

    /**
     * Create (if necessary) and return a Digester configured to process the
     * tld.
     */
    private static Digester createTldDigester(boolean namespaceAware,
            boolean validation) {

        Digester digester = null;
        if (!namespaceAware && !validation) {
            if (tldDigesters[0] == null) {
                tldDigesters[0] = DigesterFactory.newDigester(validation,
                        namespaceAware, new TldRuleSet());
                tldDigesters[0].getParser();
            }
            digester = tldDigesters[0];
        } else if (!namespaceAware && validation) {
            if (tldDigesters[1] == null) {
                tldDigesters[1] = DigesterFactory.newDigester(validation,
                        namespaceAware, new TldRuleSet());
                tldDigesters[1].getParser();
            }
            digester = tldDigesters[1];
        } else if (namespaceAware && !validation) {
            if (tldDigesters[2] == null) {
                tldDigesters[2] = DigesterFactory.newDigester(validation,
                        namespaceAware, new TldRuleSet());
                tldDigesters[2].getParser();
            }
            digester = tldDigesters[2];
        } else {
            if (tldDigesters[3] == null) {
                tldDigesters[3] = DigesterFactory.newDigester(validation,
                        namespaceAware, new TldRuleSet());
                tldDigesters[3].getParser();
            }
            digester = tldDigesters[3];
        }
        return digester;
    }


    // ----------------------------------------------------- Instance Variables

    /**
     * The Context we are associated with.
     */
    private Context context = null;


    /**
     * The Digester we will use to process tag library
     * descriptor files.
     */
    private Digester tldDigester = null;


    /**
     * Set of URIs discovered for the associated context. Used to enforce the
     * correct processing priority. Only the TLD associated with the first
     * instance of any URI will be processed.
     */
    private final Set taglibUris = new HashSet<>();

    private final Set webxmlTaglibUris = new HashSet<>();

    private final ArrayList listeners = new ArrayList<>();

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

    /**
     * Adds a taglib URI to the list of known URIs.
     */
    public void addTaglibUri(String uri) {
        taglibUris.add(uri);
    }

    /**
     * Determines if the provided URI is a known taglib URI.
     */
    public boolean isKnownTaglibUri(String uri) {
        return taglibUris.contains(uri);
    }

    /**
     * Determines if the provided URI is a known taglib URI.
     */
    public boolean isKnownWebxmlTaglibUri(String uri) {
        return webxmlTaglibUris.contains(uri);
    }

    public void addApplicationListener( String s ) {
        if(log.isDebugEnabled())
            log.debug( "Add tld listener " + s);
        listeners.add(s);
    }


    public String[] getTldListeners() {
        String result[]=new String[listeners.size()];
        listeners.toArray(result);
        return result;
    }


    /**
     * Scan for and configure all tag library descriptors found in this
     * web application.
     *
     * This supports 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.,
     * /lib). It also supports some of the weird and
     * wonderful arrangements present when Tomcat gets embedded.
     *
     * The set of shared JARs to be scanned for TLDs is narrowed down by
     * the noTldJars class variable, which contains the names of JARs
     * that are known not to contain any TLDs.
     */
    public void execute() {
        long t1=System.currentTimeMillis();

        /*
         * Priority order of URIs required by spec is:
         * 1. J2EE platform taglibs - Tomcat doesn't provide these
         * 2. web.xml entries
         * 3. JARS in WEB-INF/lib & TLDs under WEB-INF (equal priority)
         * 4. Additional entries from the container
         *
         * Keep processing order in sync with o.a.j.compiler.TldLocationsCache
         */

        // Stage 2 - web.xml entries
        tldScanWebXml();

        // Stage 3a - TLDs under WEB-INF (not lib or classes)
        tldScanResourcePaths(WEB_INF, false);

        // Stages 3b & 4
        JarScanner jarScanner = context.getJarScanner();
        jarScanner.scan(JarScanType.TLD, context.getServletContext(),
                new TldJarScannerCallback());

        // Now add all the listeners we found to the listeners for this context
        String list[] = getTldListeners();

        if( log.isDebugEnabled() )
            log.debug(sm.getString("tldConfig.addListeners",
                    Integer.valueOf(list.length)));

        for( int i=0; list!=null && i descriptors = jspConfigDescriptor.getTaglibs();
        for (TaglibDescriptor descriptor : descriptors) {
            String resourcePath = descriptor.getTaglibLocation();
            // Note: Whilst the Servlet 2.4 DTD implies that the location must
            // be a context-relative path starting with '/', JSP.7.3.6.1 states
            // explicitly how paths that do not start with '/' should be
            // handled.
            if (!resourcePath.startsWith("/")) {
                resourcePath = WEB_INF + resourcePath;
            }
            if (taglibUris.contains(descriptor.getTaglibURI())) {
                log.warn(sm.getString("tldConfig.webxmlSkip", resourcePath,
                        descriptor.getTaglibURI()));
            } else {
                if (log.isTraceEnabled()) {
                    log.trace(sm.getString("tldConfig.webxmlAdd", resourcePath,
                            descriptor.getTaglibURI()));
                }
                InputStream stream = null;
                try {
                    stream = context.getServletContext().getResourceAsStream(
                            resourcePath);
                    XmlErrorHandler handler = tldScanStream(stream);
                    handler.logFindings(log, resourcePath);
                    taglibUris.add(descriptor.getTaglibURI());
                    webxmlTaglibUris.add(descriptor.getTaglibURI());
                } catch (IOException ioe) {
                    log.warn(sm.getString("tldConfig.webxmlFail", resourcePath,
                            descriptor.getTaglibURI()), ioe);
                } finally {
                    if (stream != null) {
                        try {
                            stream.close();
                        } catch (Throwable t) {
                            ExceptionUtils.handleThrowable(t);
                        }
                    }
                }
            }
        }
    }

    /*
     * Scans the web application's sub-directory identified by startPath,
     * along with its sub-directories, for TLDs.
     *
     * Initially, rootPath equals /WEB-INF/. The /WEB-INF/classes and
     * /WEB-INF/lib sub-directories are excluded from the search, as per the
     * JSP 2.0 spec unless the JarScanner is configured to treat
     * /WEB-INF/classes/ as an exploded JAR.
     *
     * Keep in sync with o.a.j.comiler.TldLocationsCache
     */
    private void tldScanResourcePaths(String startPath, boolean webInfAsJar) {

        if (log.isTraceEnabled()) {
            log.trace(sm.getString("tldConfig.webinfScan", startPath));
        }

        ServletContext ctxt = context.getServletContext();

        Set dirList = ctxt.getResourcePaths(startPath);
        if (dirList != null) {
            Iterator it = dirList.iterator();
            while (it.hasNext()) {
                String path = it.next();
                if (!path.endsWith(TLD_EXT)
                        && (path.startsWith(WEB_INF_LIB)
                                || path.startsWith("/WEB-INF/classes/")
                                   && !webInfAsJar)) {
                    continue;
                }
                if (path.endsWith(TLD_EXT)) {
                    if (path.startsWith("/WEB-INF/tags/") &&
                            !path.endsWith("implicit.tld")) {
                        continue;
                    }
                    InputStream stream = ctxt.getResourceAsStream(path);
                    try {
                        XmlErrorHandler handler = tldScanStream(stream);
                        handler.logFindings(log, path);
                    } catch (IOException ioe) {
                        log.warn(sm.getString("tldConfig.webinfFail", path),
                                ioe);
                    } finally {
                        if (stream != null) {
                            try {
                                stream.close();
                            } catch (Throwable t) {
                                ExceptionUtils.handleThrowable(t);
                            }
                        }
                    }
                } else {
                    tldScanResourcePaths(path, false);
                }
            }
        }
    }

    /*
     * Scans the directory identified by startPath, along with its
     * sub-directories, for TLDs.
     *
     * Keep in sync with o.a.j.comiler.TldLocationsCache
     */
    private void tldScanDir(File start) {

        if (log.isTraceEnabled()) {
            log.trace(sm.getString("tldConfig.dirScan", start.getAbsolutePath()));
        }

        File[] fileList = start.listFiles();
        if (fileList != null) {
            for (int i = 0; i < fileList.length; i++) {
                // Scan recursively
                if (fileList[i].isDirectory()) {
                    tldScanDir(fileList[i]);
                } else if (fileList[i].getAbsolutePath().endsWith(TLD_EXT)) {
                    InputStream stream = null;
                    try {
                        stream = new FileInputStream(fileList[i]);
                        XmlErrorHandler handler = tldScanStream(stream);
                        handler.logFindings(log, fileList[i].getAbsolutePath());
                    } catch (IOException ioe) {
                        log.warn(sm.getString("tldConfig.dirFail",
                                fileList[i].getAbsolutePath()),
                                ioe);
                    } finally {
                        if (stream != null) {
                            try {
                                stream.close();
                            } catch (Throwable t) {
                                ExceptionUtils.handleThrowable(t);
                            }
                        }
                    }
                }
            }
        }
    }

    /*
     * Scans the given JarURLConnection for TLD files located in META-INF
     * (or a sub-directory of it).
     *
     * @param jarConn The JarURLConnection to the JAR file to scan
     *
     * Keep in sync with o.a.j.comiler.TldLocationsCache
     */
    private void tldScanJar(JarURLConnection jarConn) {

        Jar jar = null;
        InputStream is;

        try {
            jar = JarFactory.newInstance(jarConn.getURL());

            jar.nextEntry();
            String entryName = jar.getEntryName();
            while (entryName != null) {
                if (entryName.startsWith("META-INF/") &&
                        entryName.endsWith(".tld")) {
                    is = null;
                    try {
                        is = jar.getEntryInputStream();
                        XmlErrorHandler handler = tldScanStream(is);
                        handler.logFindings(log, jarConn.getURL() + entryName);
                    } finally {
                        if (is != null) {
                            try {
                                is.close();
                            } catch (IOException ioe) {
                                // Ignore
                            }
                        }
                    }
                }
                jar.nextEntry();
                entryName = jar.getEntryName();
            }
        } catch (IOException ioe) {
            log.warn(sm.getString("tldConfig.jarFail", jarConn.getURL()), ioe);
        } finally {
            if (jar != null) {
                jar.close();
            }
        }
    }


    /*
     * Scan the TLD contents in the specified input stream, and register
     * any application event listeners found there.  NOTE - This
     * method ensure that the InputStream is correctly closed.
     *
     * @param resourceStream InputStream containing a tag library descriptor
     *
     * @throws IOException  If the file cannot be read
     */
    private XmlErrorHandler tldScanStream(InputStream resourceStream)
            throws IOException {

        InputSource source = new InputSource(resourceStream);

        XmlErrorHandler result = new XmlErrorHandler();

        synchronized (tldDigester) {
            try {
                tldDigester.setErrorHandler(result);
                tldDigester.push(this);
                tldDigester.parse(source);
            } catch (SAXException s) {
                // Hack - makes exception handling simpler
                throw new IOException(s);
            } finally {
                tldDigester.reset();
            }
            return result;
        }
    }

    @Override
    public void lifecycleEvent(LifecycleEvent event) {
        // Identify the context we are associated with
        try {
            context = (Context) event.getLifecycle();
        } catch (ClassCastException e) {
            log.error(sm.getString("tldConfig.cce", event.getLifecycle()), e);
            return;
        }

        if (event.getType().equals(Lifecycle.AFTER_INIT_EVENT)) {
            init();
        } else if (event.getType().equals(Lifecycle.CONFIGURE_START_EVENT)) {
            try {
                execute();
            } catch (Exception e) {
                log.error(sm.getString(
                        "tldConfig.execute", context.getName()), e);
            }
        } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
            taglibUris.clear();
            webxmlTaglibUris.clear();
            listeners.clear();
        }
    }

    private void init() {
        if (tldDigester == null){
            tldDigester = createTldDigester(context.getTldNamespaceAware(),
                    context.getTldValidation());
        }
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy