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: [email protected]
For additional commands, e-mail: [email protected]