Are ServletContainerInitializer implementations also discovered from the
common classloader automatically?


p



On 23/04/2010 14:34, ma...@apache.org wrote:
> Author: markt
> Date: Fri Apr 23 13:34:17 2010
> New Revision: 937288
> 
> URL: http://svn.apache.org/viewvc?rev=937288&view=rev
> Log:
> Servlet 3. Implement ServletContainerInitializer support
> 
> Modified:
>     tomcat/trunk/java/org/apache/catalina/Context.java
>     tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
>     tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java
>     tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
> 
> Modified: tomcat/trunk/java/org/apache/catalina/Context.java
> URL: 
> http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Context.java?rev=937288&r1=937287&r2=937288&view=diff
> ==============================================================================
> --- tomcat/trunk/java/org/apache/catalina/Context.java (original)
> +++ tomcat/trunk/java/org/apache/catalina/Context.java Fri Apr 23 13:34:17 
> 2010
> @@ -20,7 +20,9 @@ package org.apache.catalina;
>  
>  
>  import java.net.URL;
> +import java.util.Set;
>  
> +import javax.servlet.ServletContainerInitializer;
>  import javax.servlet.ServletContext;
>  import javax.servlet.descriptor.JspConfigDescriptor;
>  
> @@ -1183,11 +1185,23 @@ public interface Context extends Contain
>       */
>      public JspConfigDescriptor getJspConfigDescriptor();
>  
> +    
>      /**
>       * Add a URL for a JAR that contains static resources in a
>       * META-INF/resources directory that should be included in the static
>       * resources for this context.
>       */
>      public void addResourceJarUrl(URL url);
> +    
> +    
> +    /**
> +     * Add a ServletContainerInitializer instance to this web application.
> +     * 
> +     * @param sci       The instance to add
> +     * @param classes   The classes in which the initializer expressed an
> +     *                  interest
> +     */
> +    public void addServletContainerInitializer(
> +            ServletContainerInitializer sci, Set<Class<?>> classes);
>  }
>  
> 
> Modified: tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
> URL: 
> http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/StandardContext.java?rev=937288&r1=937287&r2=937288&view=diff
> ==============================================================================
> --- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original)
> +++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Fri Apr 
> 23 13:34:17 2010
> @@ -28,8 +28,10 @@ import java.util.ArrayList;
>  import java.util.HashMap;
>  import java.util.Hashtable;
>  import java.util.Iterator;
> +import java.util.LinkedHashMap;
>  import java.util.List;
>  import java.util.Map;
> +import java.util.Set;
>  import java.util.Stack;
>  import java.util.TreeMap;
>  
> @@ -48,6 +50,7 @@ import javax.management.ObjectName;
>  import javax.naming.NamingException;
>  import javax.naming.directory.DirContext;
>  import javax.servlet.FilterConfig;
> +import javax.servlet.ServletContainerInitializer;
>  import javax.servlet.ServletContext;
>  import javax.servlet.ServletContextAttributeListener;
>  import javax.servlet.ServletContextEvent;
> @@ -229,6 +232,13 @@ public class StandardContext
>  
>  
>      /**
> +     * The ordered set of ServletContainerInitializers for this web 
> application.
> +     */
> +    private Map<ServletContainerInitializer,Set<Class<?>>> initializers =
> +        new LinkedHashMap<ServletContainerInitializer,Set<Class<?>>>();
> +    
> +    
> +    /**
>       * The set of application parameters defined for this application.
>       */
>      private ApplicationParameter applicationParameters[] =
> @@ -988,6 +998,19 @@ public class StandardContext
>      
>      
>      /**
> +     * Add a ServletContainerInitializer instance to this web application.
> +     * 
> +     * @param sci       The instance to add
> +     * @param classes   The classes in which the initializer expressed an
> +     *                  interest
> +     */
> +    public void addServletContainerInitializer(
> +            ServletContainerInitializer sci, Set<Class<?>> classes) {
> +        initializers.put(sci, classes);
> +    }
> +
> +    
> +    /**
>       * Return the "follow standard delegation model" flag used to configure
>       * our ClassLoader.
>       */
> @@ -4712,6 +4735,19 @@ public class StandardContext
>                  postWelcomeFiles();
>              }
>              
> +            // Call ServletContainerInitializers
> +            for (Map.Entry<ServletContainerInitializer, Set<Class<?>>> entry 
> :
> +                initializers.entrySet()) {
> +                try {
> +                    entry.getKey().onStartup(entry.getValue(),
> +                            getServletContext());
> +                } catch (ServletException e) {
> +                    // TODO: Log error
> +                    ok = false;
> +                    break;
> +                }
> +            }
> +
>              // Configure and call application event listeners
>              if (ok) {
>                  if (!listenerStart()) {
> @@ -4971,7 +5007,7 @@ public class StandardContext
>  
>      }
>      
> -    private void resetContext() throws Exception, MBeanRegistrationException 
> {
> +    private void resetContext() throws Exception {
>          // Restore the original state ( pre reading web.xml in start )
>          // If you extend this - override this method and make sure to clean 
> up
>          children = new HashMap<String, Container>();
> @@ -4987,6 +5023,8 @@ public class StandardContext
>          applicationLifecycleListenersObjects = new Object[0];
>          jspConfigDescriptor = new ApplicationJspConfigDescriptor();
>          
> +        initializers.clear();
> +        
>          if(log.isDebugEnabled())
>              log.debug("resetContext " + oname);
>      }
> 
> Modified: tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java
> URL: 
> http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java?rev=937288&r1=937287&r2=937288&view=diff
> ==============================================================================
> --- tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java 
> (original)
> +++ tomcat/trunk/java/org/apache/catalina/startup/ContextConfig.java Fri Apr 
> 23 13:34:17 2010
> @@ -19,11 +19,14 @@
>  package org.apache.catalina.startup;
>  
>  
> +import java.io.BufferedReader;
>  import java.io.File;
>  import java.io.FileInputStream;
>  import java.io.FileNotFoundException;
>  import java.io.IOException;
>  import java.io.InputStream;
> +import java.io.InputStreamReader;
> +import java.io.UnsupportedEncodingException;
>  import java.net.JarURLConnection;
>  import java.net.MalformedURLException;
>  import java.net.URISyntaxException;
> @@ -33,6 +36,7 @@ import java.util.ArrayList;
>  import java.util.Enumeration;
>  import java.util.HashMap;
>  import java.util.HashSet;
> +import java.util.LinkedHashMap;
>  import java.util.Map;
>  import java.util.Properties;
>  import java.util.Set;
> @@ -40,7 +44,9 @@ import java.util.jar.JarEntry;
>  import java.util.jar.JarFile;
>  import java.util.zip.ZipEntry;
>  
> +import javax.servlet.ServletContainerInitializer;
>  import javax.servlet.ServletContext;
> +import javax.servlet.annotation.HandlesTypes;
>  
>  import org.apache.tomcat.util.bcel.classfile.AnnotationElementValue;
>  import org.apache.tomcat.util.bcel.classfile.AnnotationEntry;
> @@ -148,6 +154,19 @@ public class ContextConfig
>      
>  
>      /**
> +     * Map of ServletContainerInitializer to classes they expressed interest 
> in.
> +     */
> +    protected Map<ServletContainerInitializer, Set<Class<?>>> 
> initializerClassMap =
> +        new LinkedHashMap<ServletContainerInitializer, Set<Class<?>>>();
> +    
> +    /**
> +     * Map of Types to ServletContainerInitializer that are interested in 
> those
> +     * types.
> +     */
> +    protected Map<Class<?>, Set<ServletContainerInitializer>> 
> typeInitializerMap =
> +        new HashMap<Class<?>, Set<ServletContainerInitializer>>();
> +
> +    /**
>       * The string resources for this package.
>       */
>      protected static final StringManager sm =
> @@ -1199,51 +1218,204 @@ public class ContextConfig
>          }
>          
>          if (webXmlVersion >= 3 && !webXml.isMetadataComplete()) {
> -            // Process /WEB-INF/classes for annotations
> -            URL webinfClasses;
> -            try {
> -                webinfClasses =
> -                    
> context.getServletContext().getResource("/WEB-INF/classes");
> -                processAnnotationsUrl(webinfClasses, webXml);
> -            } catch (MalformedURLException e) {
> -                log.error(sm.getString("contextConfig.webinfClassesUrl"), e);
> -            }
> -            
> -            // Have to process JARs for fragments
> +            // Ordering is important here
> +
> +            // Step 1. Identify all the JARs packaged with the application
> +            // If the JARs have a web-fragment.xml it will be parsed at this
> +            // point.
>              Map<String,WebXml> fragments = processJarsForWebFragments();
> -            
> -            // Order the fragments
> +
> +            // Step 2. Order the fragments.
>              Set<WebXml> orderedFragments =
>                  WebXml.orderWebFragments(webXml, fragments);
>  
> -            // Process JARs for annotations - only need to process those
> +            // Step 3. Look for ServletContainerInitializer implementations
> +            ok = processServletContainerInitializers(orderedFragments);
> +
> +            // Step 4. Process /WEB-INF/classes for annotations
> +            // This will add any matching classes to the typeInitializerMap
> +            if (ok) {
> +                URL webinfClasses;
> +                try {
> +                    webinfClasses =
> +                        
> context.getServletContext().getResource("/WEB-INF/classes");
> +                    processAnnotationsUrl(webinfClasses, webXml);
> +                } catch (MalformedURLException e) {
> +                    
> log.error(sm.getString("contextConfig.webinfClassesUrl"), e);
> +                }
> +            }
> +
> +            // Step 5. Process JARs for annotations - only need to process 
> those
>              // fragments we are going to use
> -            processAnnotations(orderedFragments);
> +            // This will add any matching classes to the typeInitializerMap
> +            if (ok) {
> +                processAnnotations(orderedFragments);
> +            }
>  
> -            // Merge fragment into application
> +            // Step 6. Merge web-fragment.xml files into the main web.xml 
> file.
>              if (ok) {
>                  ok = webXml.merge(orderedFragments);
>              }
>  
> -            // Apply merged web.xml to Context
> -            webXml.configureContext(context);
> +            // Step 7. Apply merged web.xml to Context
> +            if (ok) {
> +                webXml.configureContext(context);
> +
> +                // Step 7a. Make the merged web.xml available to other
> +                // components, specifically Jasper, to save those components
> +                // from having to re-generate it.
> +                // TODO Use a ServletContainerInitializer for Jasper
> +                String mergedWebXml = webXml.toXml();
> +                context.getServletContext().setAttribute(
> +                       org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
> +                        mergedWebXml);
> +                if (context.getLogEffectiveWebXml()) {
> +                    log.info("web.xml:\n" + mergedWebXml);
> +                }
> +            }
>              
> -            // Make the merged web.xml available to other components,
> -            // specifically Jasper, to save those components from having to
> -            // re-generate it.
> -            String mergedWebXml = webXml.toXml();
> -            context.getServletContext().setAttribute(
> -                   org.apache.tomcat.util.scan.Constants.MERGED_WEB_XML,
> -                    mergedWebXml);
> -            if (context.getLogEffectiveWebXml()) {
> -                log.info("web.xml:\n" + mergedWebXml);
> +            // Step 8. Look for static resources packaged in JARs
> +            if (ok) {
> +                processResourceJARs(orderedFragments);
>              }
>              
> -            processResourceJARs(orderedFragments);
> +            // Step 9. Apply the ServletContainerInitializer config to the
> +            // context
> +            if (ok) {
> +                for (Map.Entry<ServletContainerInitializer,Set<Class<?>>> 
> entry :
> +                        initializerClassMap.entrySet()) {
> +                    if (entry.getValue().isEmpty()) {
> +                        
> context.addServletContainerInitializer(entry.getKey(),
> +                                null);
> +                    } else {
> +                        
> context.addServletContainerInitializer(entry.getKey(),
> +                                entry.getValue());
> +                    }
> +                }
> +            }
>          } else {
>              // Apply unmerged web.xml to Context
>              webXml.configureContext(context);
> -        }        
> +        }
> +    }
> +
> +    
> +    /**
> +     * Scan JARs for ServletContainerInitializer implementations.
> +     * Implementations will be added in web-fragment.xml priority order.
> +     */
> +    protected boolean processServletContainerInitializers(
> +            Set<WebXml> fragments) {
> +        
> +        for (WebXml fragment : fragments) {
> +            URL jarUrl = fragment.getURL();
> +            JarFile jarFile = null;
> +            InputStream is = null;
> +            ServletContainerInitializer sci = null;
> +            try {
> +                JarURLConnection conn =
> +                    (JarURLConnection) jarUrl.openConnection();
> +                jarFile = conn.getJarFile();   
> +                ZipEntry entry = jarFile.getEntry(
> +                        
> "META-INF/services/javax.servlet.ServletContainerInitializer");
> +                if (entry != null) {
> +                    is = jarFile.getInputStream(entry);
> +                    sci = getServletContainerInitializer(is);
> +                }
> +            } catch (IOException ioe) {
> +                log.error(sm.getString(
> +                        "contextConfig.servletContainerInitializerFail", 
> jarUrl,
> +                        context.getPath()));
> +                return false;
> +            } finally {
> +                if (is != null) {
> +                    try {
> +                        is.close();
> +                    } catch (IOException e) {
> +                        // Ignore
> +                    }
> +                }
> +                if (jarFile != null) {
> +                    try {
> +                        jarFile.close();
> +                    } catch (IOException e) {
> +                        // Ignore
> +                    }
> +                }
> +            }
> +            
> +            if (sci == null) {
> +                break;
> +            }
> +
> +            initializerClassMap.put(sci, new HashSet<Class<?>>());
> +            
> +            HandlesTypes ht =
> +                sci.getClass().getAnnotation(HandlesTypes.class);
> +            if (ht != null) {
> +                Class<?>[] types = ht.value();
> +                if (types != null) {
> +                    for (Class<?> type : types) {
> +                        Set<ServletContainerInitializer> scis =
> +                            typeInitializerMap.get(type.getCanonicalName());
> +                        if (scis == null) {
> +                            scis = new 
> HashSet<ServletContainerInitializer>();
> +                            typeInitializerMap.put(type, scis);
> +                        }
> +                        scis.add(sci);
> +                    }
> +                }
> +            }
> +
> +        }
> +        return true;
> +    }
> +    
> +    
> +    /**
> +     * Extract the name of the ServletContainerInitializer.
> +     * 
> +     * @param is    The resource where the name is defined 
> +     * @return      The class name
> +     * @throws IOException
> +     */
> +    protected ServletContainerInitializer getServletContainerInitializer(
> +            InputStream is) throws IOException {
> +
> +        String className = null;
> +        
> +        if (is != null) {
> +            String line = null;
> +            try {
> +                BufferedReader br =
> +                    new BufferedReader(new InputStreamReader(is, "UTF-8"));
> +                line = br.readLine();
> +                if (line != null && line.trim().length() > 0) {
> +                    className = line.trim();
> +                }
> +            } catch (UnsupportedEncodingException e) {
> +                // Should never happen with UTF-8
> +                // If it does - ignore & return null
> +            }
> +        }
> +        
> +        ServletContainerInitializer sci = null;
> +        try {
> +            Class<?> clazz = Class.forName(className,true,
> +                    context.getLoader().getClassLoader());
> +             sci = (ServletContainerInitializer) clazz.newInstance();
> +        } catch (ClassNotFoundException e) {
> +            log.error(sm.getString("contextConfig.invalidSci", className), 
> e);
> +            throw new IOException(e);
> +        } catch (InstantiationException e) {
> +            log.error(sm.getString("contextConfig.invalidSci", className), 
> e);
> +            throw new IOException(e);
> +        } catch (IllegalAccessException e) {
> +            log.error(sm.getString("contextConfig.invalidSci", className), 
> e);
> +            throw new IOException(e);
> +        }
> +        
> +        return sci;
>      }
>  
>      
> @@ -1661,9 +1833,13 @@ public class ContextConfig
>          
>          ClassParser parser = new ClassParser(is, null);
>          JavaClass clazz = parser.parse();
> +        
> +        checkHandlesTypes(clazz);
> +        
>          String className = clazz.getClassName();
> -        AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
>          
> +        AnnotationEntry[] annotationsEntries = clazz.getAnnotationEntries();
> +
>          for (AnnotationEntry ae : annotationsEntries) {
>              String type = ae.getAnnotationType();
>              if ("Ljavax/servlet/annotation/WebServlet;".equals(type)) {
> @@ -1678,6 +1854,39 @@ public class ContextConfig
>          }
>      }
>  
> +    /**
> +     * For classes packaged with the web application, the class and each
> +     * super class needs to be checked for a match with {...@link 
> HandlesTypes}.
> +     * @param javaClass
> +     */
> +    protected void checkHandlesTypes(JavaClass javaClass) {
> +        
> +        // Skip this if we can
> +        if (typeInitializerMap.size() == 0)
> +            return;
> +        
> +        // No choice but to load the class
> +        String className = javaClass.getClassName();
> +        
> +        Class<?> clazz = null;
> +        try {
> +            clazz = Class.forName(className, true,
> +                    context.getLoader().getClassLoader());
> +        } catch (ClassNotFoundException e) {
> +            log.warn(sm.getString("contextConfig.invalidSciHandlesTypes",
> +                    className), e);
> +        }
> +
> +        for (Map.Entry<Class<?>, Set<ServletContainerInitializer>> entry :
> +                typeInitializerMap.entrySet()) {
> +            if (entry.getKey().isAssignableFrom(clazz)) {
> +                for (ServletContainerInitializer sci : entry.getValue()) {
> +                    initializerClassMap.get(sci).add(clazz);
> +                }
> +            }
> +        }
> +    }
> +
>      protected void processAnnotationWebServlet(String className,
>              AnnotationEntry ae, WebXml fragment) {
>          if (fragment.getServlets().containsKey(className)) {
> 
> Modified: 
> tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
> URL: 
> http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties?rev=937288&r1=937287&r2=937288&view=diff
> ==============================================================================
> --- tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties 
> (original)
> +++ tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Fri 
> Apr 23 13:34:17 2010
> @@ -41,6 +41,8 @@ contextConfig.init=ContextConfig: Initia
>  contextConfig.inputStreamFile=Unable to process file [{0}] for annotations
>  contextConfig.inputStreamJar=Unable to process Jar entry [{0}] from Jar 
> [{1}] for annotations
>  contextConfig.inputStreamJndi=Unable to process resource element [{0}] for 
> annotations
> +contextConfig.invalidSci=The ServletContentInitializer [{0}] could not be 
> created
> +contextConfig.invalidSciHandlesTypes=Unable to load class [{0}] to check 
> against the @HandlesTypes annotation of one or more 
> ServletContentInitializers. 
>  contextConfig.jarUrl=The connection created for URL [{0}] was not a 
> JarUrlConnection
>  contextConfig.jar=Unable to process resource [{0}] for annotations
>  contextConfig.jndiUrl=Unable to process JNDI URL [{0}] for annotations
> 
> 
> 
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
> For additional commands, e-mail: dev-h...@tomcat.apache.org
> 


Attachment: signature.asc
Description: OpenPGP digital signature

Reply via email to