Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » catalina » startup » [javadoc | source]
    1   /*
    2    * Licensed to the Apache Software Foundation (ASF) under one or more
    3    * contributor license agreements.  See the NOTICE file distributed with
    4    * this work for additional information regarding copyright ownership.
    5    * The ASF licenses this file to You under the Apache License, Version 2.0
    6    * (the "License"); you may not use this file except in compliance with
    7    * the License.  You may obtain a copy of the License at
    8    * 
    9    *      http://www.apache.org/licenses/LICENSE-2.0
   10    * 
   11    * Unless required by applicable law or agreed to in writing, software
   12    * distributed under the License is distributed on an "AS IS" BASIS,
   13    * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   14    * See the License for the specific language governing permissions and
   15    * limitations under the License.
   16    */
   17   
   18   
   19   package org.apache.catalina.startup;
   20   
   21   
   22   import java.io.File;
   23   import java.io.IOException;
   24   import java.io.InputStream;
   25   import java.net.URISyntaxException;
   26   import java.net.URL;
   27   import java.net.URLClassLoader;
   28   import java.util.ArrayList;
   29   import java.util.Enumeration;
   30   import java.util.HashMap;
   31   import java.util.HashSet;
   32   import java.util.Iterator;
   33   import java.util.Map;
   34   import java.util.Set;
   35   import java.util.StringTokenizer;
   36   import java.util.jar.JarEntry;
   37   import java.util.jar.JarFile;
   38   
   39   import javax.naming.NameClassPair;
   40   import javax.naming.NamingEnumeration;
   41   import javax.naming.NamingException;
   42   import javax.naming.directory.DirContext;
   43   import javax.servlet.ServletException;
   44   
   45   import org.apache.catalina.Context;
   46   import org.apache.catalina.Lifecycle;
   47   import org.apache.catalina.LifecycleEvent;
   48   import org.apache.catalina.LifecycleListener;
   49   import org.apache.catalina.core.StandardContext;
   50   import org.apache.catalina.core.StandardHost;
   51   import org.apache.catalina.util.StringManager;
   52   import org.apache.tomcat.util.digester.Digester;
   53   import org.xml.sax.InputSource;
   54   
   55   /**
   56    * Startup event listener for a <b>Context</b> that configures application
   57    * listeners configured in any TLD files.
   58    *
   59    * @author Craig R. McClanahan
   60    * @author Jean-Francois Arcand
   61    * @author Costin Manolache
   62    */
   63   public final class TldConfig  implements LifecycleListener {
   64   
   65       // Names of JARs that are known not to contain any TLDs
   66       private static HashSet<String> noTldJars;
   67   
   68       private static org.apache.juli.logging.Log log=
   69           org.apache.juli.logging.LogFactory.getLog( TldConfig.class );
   70   
   71       /*
   72        * Initializes the set of JARs that are known not to contain any TLDs
   73        */
   74       static {
   75           noTldJars = new HashSet<String>();
   76           // Bootstrap JARs
   77           noTldJars.add("bootstrap.jar");
   78           noTldJars.add("commons-daemon.jar");
   79           noTldJars.add("tomcat-juli.jar");
   80           // Main JARs
   81           noTldJars.add("annotations-api.jar");
   82           noTldJars.add("catalina.jar");
   83           noTldJars.add("catalina-ant.jar");
   84           noTldJars.add("catalina-ha.jar");
   85           noTldJars.add("catalina-tribes.jar");
   86           noTldJars.add("el-api.jar");
   87           noTldJars.add("jasper.jar");
   88           noTldJars.add("jasper-el.jar");
   89           noTldJars.add("jasper-jdt.jar");
   90           noTldJars.add("jsp-api.jar");
   91           noTldJars.add("servlet-api.jar");
   92           noTldJars.add("tomcat-coyote.jar");
   93           noTldJars.add("tomcat-dbcp.jar");
   94           // i18n JARs
   95           noTldJars.add("tomcat-i18n-en.jar");
   96           noTldJars.add("tomcat-i18n-es.jar");
   97           noTldJars.add("tomcat-i18n-fr.jar");
   98           noTldJars.add("tomcat-i18n-ja.jar");
   99           // Misc JARs not included with Tomcat
  100           noTldJars.add("ant.jar");
  101           noTldJars.add("commons-dbcp.jar");
  102           noTldJars.add("commons-beanutils.jar");
  103           noTldJars.add("commons-fileupload-1.0.jar");
  104           noTldJars.add("commons-pool.jar");
  105           noTldJars.add("commons-digester.jar");
  106           noTldJars.add("commons-logging.jar");
  107           noTldJars.add("commons-collections.jar");
  108           noTldJars.add("jmx.jar");
  109           noTldJars.add("jmx-tools.jar");
  110           noTldJars.add("xercesImpl.jar");
  111           noTldJars.add("xmlParserAPIs.jar");
  112           noTldJars.add("xml-apis.jar");
  113           // JARs from J2SE runtime
  114           noTldJars.add("sunjce_provider.jar");
  115           noTldJars.add("ldapsec.jar");
  116           noTldJars.add("localedata.jar");
  117           noTldJars.add("dnsns.jar");
  118           noTldJars.add("tools.jar");
  119           noTldJars.add("sunpkcs11.jar");
  120       }
  121   
  122   
  123       // ----------------------------------------------------- Instance Variables
  124   
  125       /**
  126        * The Context we are associated with.
  127        */
  128       private Context context = null;
  129   
  130   
  131       /**
  132        * The string resources for this package.
  133        */
  134       private static final StringManager sm =
  135           StringManager.getManager(Constants.Package);
  136   
  137       /**
  138        * The <code>Digester</code> we will use to process tag library
  139        * descriptor files.
  140        */
  141       private static Digester tldDigester = null;
  142   
  143   
  144       /**
  145        * Attribute value used to turn on/off TLD validation
  146        */
  147        private static boolean tldValidation = false;
  148   
  149   
  150       /**
  151        * Attribute value used to turn on/off TLD  namespace awarenes.
  152        */
  153       private static boolean tldNamespaceAware = false;
  154   
  155       private boolean rescan=true;
  156   
  157       private ArrayList<String> listeners = new ArrayList<String>();
  158   
  159       // --------------------------------------------------------- Public Methods
  160   
  161       /**
  162        * Sets the list of JARs that are known not to contain any TLDs.
  163        *
  164        * @param jarNames List of comma-separated names of JAR files that are 
  165        * known not to contain any TLDs 
  166        */
  167       public static void setNoTldJars(String jarNames) {
  168           if (jarNames != null) {
  169               noTldJars.clear();
  170               StringTokenizer tokenizer = new StringTokenizer(jarNames, ",");
  171               while (tokenizer.hasMoreElements()) {
  172                   noTldJars.add(tokenizer.nextToken());
  173               }
  174           }
  175       }
  176   
  177       /**
  178        * Set the validation feature of the XML parser used when
  179        * parsing xml instances.
  180        * @param tldValidation true to enable xml instance validation
  181        */
  182       public void setTldValidation(boolean tldValidation){
  183           TldConfig.tldValidation = tldValidation;
  184       }
  185   
  186       /**
  187        * Get the server.xml &lt;host&gt; attribute's xmlValidation.
  188        * @return true if validation is enabled.
  189        *
  190        */
  191       public boolean getTldValidation(){
  192           return tldValidation;
  193       }
  194   
  195       /**
  196        * Get the server.xml &lt;host&gt; attribute's xmlNamespaceAware.
  197        * @return true if namespace awarenes is enabled.
  198        *
  199        */
  200       public boolean getTldNamespaceAware(){
  201           return tldNamespaceAware;
  202       }
  203   
  204   
  205       /**
  206        * Set the namespace aware feature of the XML parser used when
  207        * parsing xml instances.
  208        * @param tldNamespaceAware true to enable namespace awareness
  209        */
  210       public void setTldNamespaceAware(boolean tldNamespaceAware){
  211           TldConfig.tldNamespaceAware = tldNamespaceAware;
  212       }    
  213   
  214   
  215       public boolean isRescan() {
  216           return rescan;
  217       }
  218   
  219       public void setRescan(boolean rescan) {
  220           this.rescan = rescan;
  221       }
  222   
  223       public Context getContext() {
  224           return context;
  225       }
  226   
  227       public void setContext(Context context) {
  228           this.context = context;
  229       }
  230   
  231       public void addApplicationListener( String s ) {
  232           //if(log.isDebugEnabled())
  233               log.debug( "Add tld listener " + s);
  234           listeners.add(s);
  235       }
  236   
  237       public String[] getTldListeners() {
  238           String result[]=new String[listeners.size()];
  239           listeners.toArray(result);
  240           return result;
  241       }
  242   
  243   
  244       /**
  245        * Scan for and configure all tag library descriptors found in this
  246        * web application.
  247        *
  248        * @exception Exception if a fatal input/output or parsing error occurs
  249        */
  250       public void execute() throws Exception {
  251           long t1=System.currentTimeMillis();
  252   
  253           /*
  254            * Acquire the list of TLD resource paths, possibly embedded in JAR
  255            * files, to be processed
  256            */
  257           Set resourcePaths = tldScanResourcePaths();
  258           Map jarPaths = getJarPaths();
  259   
  260           // Scan each accumulated resource path for TLDs to be processed
  261           Iterator paths = resourcePaths.iterator();
  262           while (paths.hasNext()) {
  263               String path = (String) paths.next();
  264               if (path.endsWith(".jar")) {
  265                   tldScanJar(path);
  266               } else {
  267                   tldScanTld(path);
  268               }
  269           }
  270           if (jarPaths != null) {
  271               paths = jarPaths.values().iterator();
  272               while (paths.hasNext()) {
  273                   tldScanJar((File) paths.next());
  274               }
  275           }
  276   
  277           String list[] = getTldListeners();
  278   
  279           if( log.isDebugEnabled() )
  280               log.debug( "Adding tld listeners:" + list.length);
  281           for( int i=0; list!=null && i<list.length; i++ ) {
  282               context.addApplicationListener(list[i]);
  283           }
  284   
  285           long t2=System.currentTimeMillis();
  286           if( context instanceof StandardContext ) {
  287               ((StandardContext)context).setTldScanTime(t2-t1);
  288           }
  289   
  290       }
  291   
  292       // -------------------------------------------------------- Private Methods
  293   
  294       /**
  295        * Scan the JAR file at the specified resource path for TLDs in the
  296        * <code>META-INF</code> subdirectory, and scan each TLD for application
  297        * event listeners that need to be registered.
  298        *
  299        * @param resourcePath Resource path of the JAR file to scan
  300        *
  301        * @exception Exception if an exception occurs while scanning this JAR
  302        */
  303       private void tldScanJar(String resourcePath) throws Exception {
  304   
  305           if (log.isDebugEnabled()) {
  306               log.debug(" Scanning JAR at resource path '" + resourcePath + "'");
  307           }
  308   
  309           URL url = context.getServletContext().getResource(resourcePath);
  310           if (url == null) {
  311               throw new IllegalArgumentException
  312                                   (sm.getString("contextConfig.tldResourcePath",
  313                                                 resourcePath));
  314           }
  315   
  316           File file = null;
  317           try {
  318               file = new File(url.toURI());
  319           } catch (URISyntaxException e) {
  320               // Ignore, probably an unencoded char
  321               file = new File(url.getFile());
  322           }
  323           try {
  324               file = file.getCanonicalFile();
  325           } catch (IOException e) {
  326               // Ignore
  327           }
  328           tldScanJar(file);
  329   
  330       }
  331   
  332       /**
  333        * Scans all TLD entries in the given JAR for application listeners.
  334        *
  335        * @param file JAR file whose TLD entries are scanned for application
  336        * listeners
  337        */
  338       private void tldScanJar(File file) throws Exception {
  339   
  340           JarFile jarFile = null;
  341           String name = null;
  342   
  343           String jarPath = file.getAbsolutePath();
  344   
  345           try {
  346               jarFile = new JarFile(file);
  347               Enumeration entries = jarFile.entries();
  348               while (entries.hasMoreElements()) {
  349                   JarEntry entry = (JarEntry) entries.nextElement();
  350                   name = entry.getName();
  351                   if (!name.startsWith("META-INF/")) {
  352                       continue;
  353                   }
  354                   if (!name.endsWith(".tld")) {
  355                       continue;
  356                   }
  357                   if (log.isTraceEnabled()) {
  358                       log.trace("  Processing TLD at '" + name + "'");
  359                   }
  360                   try {
  361                       tldScanStream(new InputSource(jarFile.getInputStream(entry)));
  362                   } catch (Exception e) {
  363                       log.error(sm.getString("contextConfig.tldEntryException",
  364                                              name, jarPath, context.getPath()),
  365                                 e);
  366                   }
  367               }
  368           } catch (Exception e) {
  369               log.error(sm.getString("contextConfig.tldJarException",
  370                                      jarPath, context.getPath()),
  371                         e);
  372           } finally {
  373               if (jarFile != null) {
  374                   try {
  375                       jarFile.close();
  376                   } catch (Throwable t) {
  377                       // Ignore
  378                   }
  379               }
  380           }
  381       }
  382   
  383       /**
  384        * Scan the TLD contents in the specified input stream, and register
  385        * any application event listeners found there.  <b>NOTE</b> - It is
  386        * the responsibility of the caller to close the InputStream after this
  387        * method returns.
  388        *
  389        * @param resourceStream InputStream containing a tag library descriptor
  390        *
  391        * @exception Exception if an exception occurs while scanning this TLD
  392        */
  393       private void tldScanStream(InputSource resourceStream)
  394           throws Exception {
  395   
  396           synchronized (tldDigester) {
  397               try {
  398                   tldDigester.push(this);
  399                   tldDigester.parse(resourceStream);
  400               } finally {
  401                   tldDigester.reset();
  402               }
  403           }
  404   
  405       }
  406   
  407       /**
  408        * Scan the TLD contents at the specified resource path, and register
  409        * any application event listeners found there.
  410        *
  411        * @param resourcePath Resource path being scanned
  412        *
  413        * @exception Exception if an exception occurs while scanning this TLD
  414        */
  415       private void tldScanTld(String resourcePath) throws Exception {
  416   
  417           if (log.isDebugEnabled()) {
  418               log.debug(" Scanning TLD at resource path '" + resourcePath + "'");
  419           }
  420   
  421           InputSource inputSource = null;
  422           try {
  423               InputStream stream =
  424                   context.getServletContext().getResourceAsStream(resourcePath);
  425               if (stream == null) {
  426                   throw new IllegalArgumentException
  427                   (sm.getString("contextConfig.tldResourcePath",
  428                           resourcePath));
  429               }
  430               inputSource = new InputSource(stream);
  431               if (inputSource == null) {
  432                   throw new IllegalArgumentException
  433                       (sm.getString("contextConfig.tldResourcePath",
  434                                     resourcePath));
  435               }
  436               tldScanStream(inputSource);
  437           } catch (Exception e) {
  438                throw new ServletException
  439                    (sm.getString("contextConfig.tldFileException", resourcePath,
  440                                  context.getPath()),
  441                     e);
  442           } 
  443   
  444       }
  445   
  446       /**
  447        * Accumulate and return a Set of resource paths to be analyzed for
  448        * tag library descriptors.  Each element of the returned set will be
  449        * the context-relative path to either a tag library descriptor file,
  450        * or to a JAR file that may contain tag library descriptors in its
  451        * <code>META-INF</code> subdirectory.
  452        *
  453        * @exception IOException if an input/output error occurs while
  454        *  accumulating the list of resource paths
  455        */
  456       private Set tldScanResourcePaths() throws IOException {
  457           if (log.isDebugEnabled()) {
  458               log.debug(" Accumulating TLD resource paths");
  459           }
  460           Set resourcePaths = new HashSet();
  461   
  462           // Accumulate resource paths explicitly listed in the web application
  463           // deployment descriptor
  464           if (log.isTraceEnabled()) {
  465               log.trace("  Scanning <taglib> elements in web.xml");
  466           }
  467           String taglibs[] = context.findTaglibs();
  468           for (int i = 0; i < taglibs.length; i++) {
  469               String resourcePath = context.findTaglib(taglibs[i]);
  470               // FIXME - Servlet 2.4 DTD implies that the location MUST be
  471               // a context-relative path starting with '/'?
  472               if (!resourcePath.startsWith("/")) {
  473                   resourcePath = "/WEB-INF/" + resourcePath;
  474               }
  475               if (log.isTraceEnabled()) {
  476                   log.trace("   Adding path '" + resourcePath +
  477                       "' for URI '" + taglibs[i] + "'");
  478               }
  479               resourcePaths.add(resourcePath);
  480           }
  481   
  482           DirContext resources = context.getResources();
  483           if (resources != null) {
  484               tldScanResourcePathsWebInf(resources, "/WEB-INF", resourcePaths);
  485           }
  486   
  487           // Return the completed set
  488           return (resourcePaths);
  489   
  490       }
  491   
  492       /*
  493        * Scans the web application's subdirectory identified by rootPath,
  494        * along with its subdirectories, for TLDs.
  495        *
  496        * Initially, rootPath equals /WEB-INF. The /WEB-INF/classes and
  497        * /WEB-INF/lib subdirectories are excluded from the search, as per the
  498        * JSP 2.0 spec.
  499        *
  500        * @param resources The web application's resources
  501        * @param rootPath The path whose subdirectories are to be searched for
  502        * TLDs
  503        * @param tldPaths The set of TLD resource paths to add to
  504        */
  505       private void tldScanResourcePathsWebInf(DirContext resources,
  506                                               String rootPath,
  507                                               Set tldPaths) 
  508               throws IOException {
  509   
  510           if (log.isTraceEnabled()) {
  511               log.trace("  Scanning TLDs in " + rootPath + " subdirectory");
  512           }
  513   
  514           try {
  515               NamingEnumeration items = resources.list(rootPath);
  516               while (items.hasMoreElements()) {
  517                   NameClassPair item = (NameClassPair) items.nextElement();
  518                   String resourcePath = rootPath + "/" + item.getName();
  519                   if (!resourcePath.endsWith(".tld")
  520                           && (resourcePath.startsWith("/WEB-INF/classes")
  521                               || resourcePath.startsWith("/WEB-INF/lib"))) {
  522                       continue;
  523                   }
  524                   if (resourcePath.endsWith(".tld")) {
  525                       if (log.isTraceEnabled()) {
  526                           log.trace("   Adding path '" + resourcePath + "'");
  527                       }
  528                       tldPaths.add(resourcePath);
  529                   } else {
  530                       tldScanResourcePathsWebInf(resources, resourcePath,
  531                                                  tldPaths);
  532                   }
  533               }
  534           } catch (NamingException e) {
  535               ; // Silent catch: it's valid that no /WEB-INF directory exists
  536           }
  537       }
  538   
  539       /**
  540        * Returns a map of the paths to all JAR files that are accessible to the
  541        * webapp and will be scanned for TLDs.
  542        *
  543        * The map always includes all the JARs under WEB-INF/lib, as well as
  544        * shared JARs in the classloader delegation chain of the webapp's
  545        * classloader.
  546        *
  547        * The latter constitutes a Tomcat-specific extension to the TLD search
  548        * order defined in the JSP spec. It allows tag libraries packaged as JAR
  549        * files to be shared by web applications by simply dropping them in a 
  550        * location that all web applications have access to (e.g.,
  551        * <CATALINA_HOME>/common/lib).
  552        *
  553        * The set of shared JARs to be scanned for TLDs is narrowed down by
  554        * the <tt>noTldJars</tt> class variable, which contains the names of JARs
  555        * that are known not to contain any TLDs.
  556        *
  557        * @return Map of JAR file paths
  558        */
  559       private Map getJarPaths() {
  560   
  561           HashMap jarPathMap = null;
  562   
  563           ClassLoader webappLoader = Thread.currentThread().getContextClassLoader();
  564           ClassLoader loader = webappLoader;
  565           while (loader != null) {
  566               if (loader instanceof URLClassLoader) {
  567                   URL[] urls = ((URLClassLoader) loader).getURLs();
  568                   for (int i=0; i<urls.length; i++) {
  569                       // Expect file URLs, these are %xx encoded or not depending
  570                       // on the class loader
  571                       // This is definitely not as clean as using JAR URLs either
  572                       // over file or the custom jndi handler, but a lot less
  573                       // buggy overall
  574                       
  575                       // Check that the URL is using file protocol, else ignore it
  576                       if (!"file".equals(urls[i].getProtocol())) {
  577                           continue;
  578                       }
  579                       
  580                       File file = null;
  581                       try {
  582                           file = new File(urls[i].toURI());
  583                       } catch (URISyntaxException e) {
  584                           // Ignore, probably an unencoded char
  585                           file = new File(urls[i].getFile());
  586                       }
  587                       try {
  588                           file = file.getCanonicalFile();
  589                       } catch (IOException e) {
  590                           // Ignore
  591                       }
  592                       if (!file.exists()) {
  593                           continue;
  594                       }
  595                       String path = file.getAbsolutePath();
  596                       if (!path.endsWith(".jar")) {
  597                           continue;
  598                       }
  599                       /*
  600                        * Scan all JARs from WEB-INF/lib, plus any shared JARs
  601                        * that are not known not to contain any TLDs
  602                        */
  603                       if (loader == webappLoader
  604                               || noTldJars == null
  605                               || !noTldJars.contains(file.getName())) {
  606                           if (jarPathMap == null) {
  607                               jarPathMap = new HashMap();
  608                               jarPathMap.put(path, file);
  609                           } else if (!jarPathMap.containsKey(path)) {
  610                               jarPathMap.put(path, file);
  611                           }
  612                       }
  613                   }
  614               }
  615               loader = loader.getParent();
  616           }
  617   
  618           return jarPathMap;
  619       }
  620   
  621       public void lifecycleEvent(LifecycleEvent event) {
  622           // Identify the context we are associated with
  623           try {
  624               context = (Context) event.getLifecycle();
  625           } catch (ClassCastException e) {
  626               log.error(sm.getString("tldConfig.cce", event.getLifecycle()), e);
  627               return;
  628           }
  629           
  630           if (event.getType().equals(Lifecycle.INIT_EVENT)) {
  631               init();
  632           } else if (event.getType().equals(Lifecycle.START_EVENT)) {
  633               try {
  634                   execute();
  635               } catch (Exception e) {
  636                   log.error(sm.getString(
  637                           "tldConfig.execute", context.getPath()), e);
  638               }
  639           } else if (event.getType().equals(Lifecycle.STOP_EVENT)) {
  640               listeners.clear();
  641           }
  642       }
  643       
  644       private void init() {
  645           if (tldDigester == null){
  646               // (1)  check if the attribute has been defined
  647               //      on the context element.
  648               setTldValidation(context.getTldValidation());
  649               setTldNamespaceAware(context.getTldNamespaceAware());
  650       
  651               // (2) if the attribute wasn't defined on the context
  652               //     try the host.
  653               if (!tldValidation) {
  654                 setTldValidation(
  655                         ((StandardHost) context.getParent()).getXmlValidation());
  656               }
  657       
  658               if (!tldNamespaceAware) {
  659                 setTldNamespaceAware(
  660                         ((StandardHost) context.getParent()).getXmlNamespaceAware());
  661               }
  662   
  663               tldDigester = DigesterFactory.newDigester(tldValidation, 
  664                       tldNamespaceAware, 
  665                       new TldRuleSet());
  666               tldDigester.getParser();
  667           }
  668       }
  669   }

Save This Page
Home » apache-tomcat-6.0.26-src » org.apache » catalina » startup » [javadoc | source]