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

Reply via email to