Author: markt
Date: Thu Jan 10 11:39:46 2013
New Revision: 1431293

URL: http://svn.apache.org/viewvc?rev=1431293&view=rev
Log:
Fix https://issues.apache.org/bugzilla/show_bug.cgi?id=54379
Implement support for post-construct and pre-destroy elements in web.xml
Patch by  Violeta Georgieva.

Added:
    
tomcat/trunk/test/org/apache/catalina/startup/TesterServletWithLifeCycleMethods.java
   (with props)
    tomcat/trunk/test/org/apache/catalina/startup/web-1lifecyclecallback.xml   
(with props)
    tomcat/trunk/test/org/apache/catalina/startup/web-2lifecyclecallback.xml   
(with props)
Modified:
    tomcat/trunk/java/org/apache/catalina/Context.java
    tomcat/trunk/java/org/apache/catalina/core/DefaultInstanceManager.java
    tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/core/StandardContext.java
    tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java
    tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java
    tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties
    tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java
    tomcat/trunk/java/org/apache/catalina/util/Introspection.java
    tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java
    tomcat/trunk/test/org/apache/catalina/core/TesterContext.java
    tomcat/trunk/test/org/apache/catalina/deploy/TestWebXml.java
    tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java
    tomcat/trunk/test/org/apache/catalina/startup/TestWebRuleSet.java
    tomcat/trunk/test/webapp-3.0-fragments/WEB-INF/web.xml

Modified: tomcat/trunk/java/org/apache/catalina/Context.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/Context.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/Context.java (original)
+++ tomcat/trunk/java/org/apache/catalina/Context.java Thu Jan 10 11:39:46 2013
@@ -18,6 +18,7 @@ package org.apache.catalina;
 
 import java.net.URL;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
 
 import javax.servlet.ServletContainerInitializer;
@@ -1420,5 +1421,101 @@ public interface Context extends Contain
      * JAR.
      */
     public boolean getAddWebinfClassesResources();
-}
 
+    /**
+     * Add a post construct method definition for the given class, if there is
+     * an existing definition for the specified class - 
IllegalArgumentException
+     * will be thrown.
+     *
+     * @param clazz Fully qualified class name
+     * @param method
+     *            Post construct method name
+     * @throws IllegalArgumentException
+     *             if the fully qualified class name or method name are
+     *             <code>NULL</code>; if there is already post construct method
+     *             definition for the given class
+     */
+    public void addPostConstructMethod(String clazz, String method);
+
+    /**
+     * Add a pre destroy method definition for the given class, if there is an
+     * existing definition for the specified class - IllegalArgumentException
+     * will be thrown.
+     *
+     * @param clazz Fully qualified class name
+     * @param method
+     *            Post construct method name
+     * @throws IllegalArgumentException
+     *             if the fully qualified class name or method name are
+     *             <code>NULL</code>; if there is already pre destroy method
+     *             definition for the given class
+     */
+    public void addPreDestroyMethod(String clazz, String method);
+
+    /**
+     * Removes the post construct method definition for the given class, if it
+     * exists; otherwise, no action is taken.
+     *
+     * @param clazz
+     *            Fully qualified class name
+     */
+    public void removePostConstructMethod(String clazz);
+
+    /**
+     * Removes the pre destroy method definition for the given class, if it
+     * exists; otherwise, no action is taken.
+     *
+     * @param clazz
+     *            Fully qualified class name
+     */
+    public void removePreDestroyMethod(String clazz);
+
+    /**
+     * Returns the method name that is specified as post construct method for
+     * the given class, if it exists; otherwise <code>NULL</code> will be
+     * returned.
+     *
+     * @param clazz
+     *            Fully qualified class name
+     *
+     * @return the method name that is specified as post construct method for
+     *         the given class, if it exists; otherwise <code>NULL</code> will
+     *         be returned.
+     */
+    public String findPostConstructMethod(String clazz);
+
+    /**
+     * Returns the method name that is specified as pre destroy method for the
+     * given class, if it exists; otherwise <code>NULL</code> will be returned.
+     *
+     * @param clazz
+     *            Fully qualified class name
+     *
+     * @return the method name that is specified as pre destroy method for the
+     *         given class, if it exists; otherwise <code>NULL</code> will be
+     *         returned.
+     */
+    public String findPreDestroyMethod(String clazz);
+
+    /**
+     * Returns a map with keys - fully qualified class names of the classes 
that
+     * have post construct methods and the values are the corresponding method
+     * names. If there are no such classes an empty map will be returned.
+     *
+     * @return a map with keys - fully qualified class names of the classes 
that
+     *         have post construct methods and the values are the corresponding
+     *         method names.
+     */
+    public Map<String, String> findPostConstructMethods();
+
+    /**
+     * Returns a map with keys - fully qualified class names of the classes 
that
+     * have pre destroy methods and the values are the corresponding method
+     * names. If there are no such classes an empty map will be returned.
+     *
+     * @return a map with keys - fully qualified class names of the classes 
that
+     *         have pre destroy methods and the values are the corresponding
+     *         method names.
+     */
+    public Map<String, String> findPreDestroyMethods();
+}

Modified: tomcat/trunk/java/org/apache/catalina/core/DefaultInstanceManager.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/DefaultInstanceManager.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/DefaultInstanceManager.java 
(original)
+++ tomcat/trunk/java/org/apache/catalina/core/DefaultInstanceManager.java Thu 
Jan 10 11:39:46 2013
@@ -18,10 +18,10 @@ package org.apache.catalina.core;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.lang.annotation.Annotation;
 import java.lang.reflect.Field;
 import java.lang.reflect.InvocationTargetException;
 import java.lang.reflect.Method;
-import java.lang.reflect.Modifier;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 import java.security.PrivilegedActionException;
@@ -79,6 +79,8 @@ public class DefaultInstanceManager impl
     private final Properties restrictedServlets = new Properties();
     private final Map<Class<?>, AnnotationCacheEntry[]> annotationCache =
         new WeakHashMap<>();
+    private final Map<String, String> postConstructMethods;
+    private final Map<String, String> preDestroyMethods;
 
     public DefaultInstanceManager(Context context, Map<String, Map<String, 
String>> injectionMap, org.apache.catalina.Context catalinaContext, ClassLoader 
containerClassLoader) {
         classLoader = catalinaContext.getLoader().getClassLoader();
@@ -125,6 +127,8 @@ public class DefaultInstanceManager impl
         }
         this.context = context;
         this.injectionMap = injectionMap;
+        this.postConstructMethods = catalinaContext.findPostConstructMethods();
+        this.preDestroyMethods = catalinaContext.findPreDestroyMethods();
     }
 
     @Override
@@ -331,7 +335,9 @@ public class DefaultInstanceManager impl
                 // Initialize methods annotations
                 Method[] methods = Introspection.getDeclaredMethods(clazz);
                 Method postConstruct = null;
+                String postConstructFromXml = 
postConstructMethods.get(clazz.getName());
                 Method preDestroy = null;
+                String preDestroyFromXml = 
preDestroyMethods.get(clazz.getName());
                 for (Method method : methods) {
                     if (context != null) {
                         // Resource injection only if JNDI is enabled
@@ -388,41 +394,30 @@ public class DefaultInstanceManager impl
                         }
                     }
 
-                    if (method.isAnnotationPresent(PostConstruct.class)) {
-                        if ((postConstruct != null) ||
-                                (method.getParameterTypes().length != 0) ||
-                                (Modifier.isStatic(method.getModifiers())) ||
-                                (method.getExceptionTypes().length > 0) ||
-                                
(!method.getReturnType().getName().equals("void"))) {
-                            throw new IllegalArgumentException(
-                                    "Invalid PostConstruct annotation");
-                        }
-                        postConstruct = method;
-                    }
+                    postConstruct = findPostConstruct(postConstruct, 
postConstructFromXml, method);
 
-                    if (method.isAnnotationPresent(PreDestroy.class)) {
-                        if ((preDestroy != null ||
-                                method.getParameterTypes().length != 0) ||
-                                (Modifier.isStatic(method.getModifiers())) ||
-                                (method.getExceptionTypes().length > 0) ||
-                                
(!method.getReturnType().getName().equals("void"))) {
-                            throw new IllegalArgumentException(
-                                    "Invalid PreDestroy annotation");
-                        }
-                        preDestroy = method;
-                    }
+                    preDestroy = findPreDestroy(preDestroy, preDestroyFromXml, 
method);
                 }
+
                 if (postConstruct != null) {
                     annotations.add(new AnnotationCacheEntry(
                             postConstruct.getName(),
                             postConstruct.getParameterTypes(), null,
                             AnnotationCacheEntryType.POST_CONSTRUCT));
+                } else if (postConstructFromXml != null) {
+                    throw new IllegalArgumentException("Post construct method "
+                        + postConstructFromXml + " for class " + 
clazz.getName()
+                        + " is declared in deployment descriptor but cannot be 
found.");
                 }
                 if (preDestroy != null) {
                     annotations.add(new AnnotationCacheEntry(
                             preDestroy.getName(),
                             preDestroy.getParameterTypes(), null,
                             AnnotationCacheEntryType.PRE_DESTROY));
+                } else if (preDestroyFromXml != null) {
+                    throw new IllegalArgumentException("Pre destroy method "
+                        + preDestroyFromXml + " for class " + clazz.getName()
+                        + " is declared in deployment descriptor but cannot be 
found.");
                 }
                 if (annotations.isEmpty()) {
                     // Use common object to save memory
@@ -703,6 +698,44 @@ public class DefaultInstanceManager impl
         return result;
     }
 
+
+    private static Method findPostConstruct(Method currentPostConstruct,
+            String postConstructFromXml, Method method) {
+        return findLifecycleCallback(currentPostConstruct,
+            postConstructFromXml, method, PostConstruct.class);
+    }
+
+    private static Method findPreDestroy(Method currentPreDestroy,
+        String preDestroyFromXml, Method method) {
+        return findLifecycleCallback(currentPreDestroy,
+            preDestroyFromXml, method, PreDestroy.class);
+    }
+
+    private static Method findLifecycleCallback(Method currentMethod,
+            String methodNameFromXml, Method method,
+            Class<? extends Annotation> annotation) {
+        Method result = currentMethod;
+        if (methodNameFromXml != null) {
+            if (method.getName().equals(methodNameFromXml)) {
+                if (!Introspection.isValidLifecycleCallback(method)) {
+                    throw new IllegalArgumentException(
+                        "Invalid " + annotation.getName() + " annotation");
+                }
+                result = method;
+            }
+        } else {
+            if (method.isAnnotationPresent(annotation)) {
+                if (currentMethod != null ||
+                    !Introspection.isValidLifecycleCallback(method)) {
+                    throw new IllegalArgumentException(
+                        "Invalid " + annotation.getName() + " annotation");
+                }
+                result = method;
+            }
+        }
+        return result;
+    }
+
     private static final class AnnotationCacheEntry {
         private final String accessibleObjectName;
         private final Class<?>[] paramTypes;

Modified: tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/core/LocalStrings.properties Thu Jan 
10 11:39:46 2013
@@ -119,6 +119,10 @@ standardContext.notWrapper=Child of a Co
 standardContext.parameter.duplicate=Duplicate context initialization parameter 
{0}
 standardContext.parameter.required=Both parameter name and parameter value are 
required
 standardContext.pathInvalid=A context path must either be an empty string or 
start with a ''/''. The path [{0}] does not meet these criteria and has been 
changed to [{1}]
+standardContext.postconstruct.duplicate=Duplicate post construct method 
definition for class {0}
+standardContext.postconstruct.required=Both fully qualified class name and 
method name are required
+standardContext.predestroy.duplicate=Duplicate pre destroy method definition 
for class {0}
+standardContext.predestroy.required=Both fully qualified class name and method 
name are required
 standardContext.reloadingCompleted=Reloading Context with name [{0}] is 
completed
 standardContext.reloadingStarted=Reloading Context with name [{0}] has started
 standardContext.resourcesStart=Error starting static Resources

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=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/core/StandardContext.java (original)
+++ tomcat/trunk/java/org/apache/catalina/core/StandardContext.java Thu Jan 10 
11:39:46 2013
@@ -823,6 +823,10 @@ public class StandardContext extends Con
 
     private boolean jndiExceptionOnFailedWrite = true;
 
+    private Map<String, String> postConstructMethods = new HashMap<>();
+    private Map<String, String> preDestroyMethods = new HashMap<>();
+
+
     // ----------------------------------------------------- Context Properties
 
     @Override
@@ -5844,6 +5848,72 @@ public class StandardContext extends Con
     }
 
 
+    @Override
+    public void addPostConstructMethod(String clazz, String method) {
+        if (clazz == null || method == null)
+            throw new IllegalArgumentException(
+                    sm.getString("standardContext.postconstruct.required"));
+        if (postConstructMethods.get(clazz) != null)
+            throw new IllegalArgumentException(sm.getString(
+                    "standardContext.postconstruct.duplicate", clazz));
+
+        postConstructMethods.put(clazz, method);
+        fireContainerEvent("addPostConstructMethod", clazz);
+    }
+
+
+    @Override
+    public void removePostConstructMethod(String clazz) {
+        postConstructMethods.remove(clazz);
+        fireContainerEvent("removePostConstructMethod", clazz);
+    }
+
+
+    @Override
+    public void addPreDestroyMethod(String clazz, String method) {
+        if (clazz == null || method == null)
+            throw new IllegalArgumentException(
+                    sm.getString("standardContext.predestroy.required"));
+        if (preDestroyMethods.get(clazz) != null)
+            throw new IllegalArgumentException(sm.getString(
+                    "standardContext.predestroy.duplicate", clazz));
+
+        preDestroyMethods.put(clazz, method);
+        fireContainerEvent("addPreDestroyMethod", clazz);
+    }
+
+
+    @Override
+    public void removePreDestroyMethod(String clazz) {
+        preDestroyMethods.remove(clazz);
+        fireContainerEvent("removePreDestroyMethod", clazz);
+    }
+
+
+    @Override
+    public String findPostConstructMethod(String clazz) {
+        return postConstructMethods.get(clazz);
+    }
+
+
+    @Override
+    public String findPreDestroyMethod(String clazz) {
+        return preDestroyMethods.get(clazz);
+    }
+
+
+    @Override
+    public Map<String, String> findPostConstructMethods() {
+        return postConstructMethods;
+    }
+
+
+    @Override
+    public Map<String, String> findPreDestroyMethods() {
+        return preDestroyMethods;
+    }
+
+
     /**
      * Set the appropriate context attribute for our work directory.
      */

Modified: tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java (original)
+++ tomcat/trunk/java/org/apache/catalina/deploy/WebXml.java Thu Jan 10 
11:39:46 2013
@@ -559,6 +559,27 @@ public class WebXml {
         return localeEncodingMappings;
     }
 
+    // post-construct elements
+    private Map<String, String> postConstructMethods = new HashMap<>();
+    public void addPostConstructMethods(String clazz, String method) {
+        if (!postConstructMethods.containsKey(clazz)) {
+            postConstructMethods.put(clazz, method);
+        }
+    }
+    public Map<String, String> getPostConstructMethods() {
+        return postConstructMethods;
+    }
+
+    // pre-destroy elements
+    private Map<String, String> preDestroyMethods = new HashMap<>();
+    public void addPreDestroyMethods(String clazz, String method) {
+        if (!preDestroyMethods.containsKey(clazz)) {
+            preDestroyMethods.put(clazz, method);
+        }
+    }
+    public Map<String, String> getPreDestroyMethods() {
+        return preDestroyMethods;
+    }
 
     // Attributes not defined in web.xml or web-fragment.xml
 
@@ -1066,6 +1087,32 @@ public class WebXml {
         }
         sb.append('\n');
 
+        if (!postConstructMethods.isEmpty()) {
+            for (Entry<String, String> entry : postConstructMethods
+                    .entrySet()) {
+                sb.append("  <post-construct>\n");
+                appendElement(sb, INDENT4, "lifecycle-callback-class",
+                        entry.getKey());
+                appendElement(sb, INDENT4, "lifecycle-callback-method",
+                        entry.getValue());
+                sb.append("  </post-construct>\n");
+            }
+            sb.append('\n');
+        }
+
+        if (!preDestroyMethods.isEmpty()) {
+            for (Entry<String, String> entry : preDestroyMethods
+                    .entrySet()) {
+                sb.append("  <pre-destroy>\n");
+                appendElement(sb, INDENT4, "lifecycle-callback-class",
+                        entry.getKey());
+                appendElement(sb, INDENT4, "lifecycle-callback-method",
+                        entry.getValue());
+                sb.append("  </pre-destroy>\n");
+            }
+            sb.append('\n');
+        }
+
         for (MessageDestinationRef mdr : messageDestinationRefs.values()) {
             sb.append("  <message-destination-ref>\n");
             appendElement(sb, INDENT4, "description", mdr.getDescription());
@@ -1365,6 +1412,14 @@ public class WebXml {
                 }
             }
         }
+
+        for (Entry<String, String> entry : postConstructMethods.entrySet()) {
+            context.addPostConstructMethod(entry.getKey(), entry.getValue());
+        }
+
+        for (Entry<String, String> entry : preDestroyMethods.entrySet()) {
+            context.addPreDestroyMethod(entry.getKey(), entry.getValue());
+        }
     }
 
     /**
@@ -1860,6 +1915,28 @@ public class WebXml {
             }
         }
 
+        if (postConstructMethods.isEmpty()) {
+            for (WebXml fragment : fragments) {
+                if (!mergeLifecycleCallback(fragment.getPostConstructMethods(),
+                        temp.getPostConstructMethods(), fragment,
+                        "Post Construct Methods")) {
+                    return false;
+                }
+            }
+            postConstructMethods.putAll(temp.getPostConstructMethods());
+        }
+
+        if (preDestroyMethods.isEmpty()) {
+            for (WebXml fragment : fragments) {
+                if (!mergeLifecycleCallback(fragment.getPreDestroyMethods(),
+                        temp.getPreDestroyMethods(), fragment,
+                        "Pre Destroy Methods")) {
+                    return false;
+                }
+            }
+            preDestroyMethods.putAll(temp.getPreDestroyMethods());
+        }
+
         return true;
     }
 
@@ -2084,6 +2161,26 @@ public class WebXml {
     }
 
 
+    private static <T> boolean mergeLifecycleCallback(
+            Map<String, String> fragmentMap, Map<String, String> tempMap,
+            WebXml fragment, String mapName) {
+        for (Entry<String, String> entry : fragmentMap.entrySet()) {
+            final String key = entry.getKey();
+            final String value = entry.getValue();
+            if (tempMap.containsKey(key)) {
+                if (value != null && !value.equals(tempMap.get(key))) {
+                    log.error(sm.getString("webXml.mergeConflictString",
+                            mapName, key, fragment.getName(), 
fragment.getURL()));
+                    return false;
+                }
+            } else {
+                tempMap.put(key, value);
+            }
+        }
+        return true;
+    }
+
+
     /**
      * Generates the sub-set of the web-fragment.xml files to be processed in
      * the order that the fragments must be processed as per the rules in the

Modified: tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/FailedContext.java Thu Jan 10 
11:39:46 2013
@@ -20,6 +20,7 @@ import java.beans.PropertyChangeListener
 import java.io.File;
 import java.net.URL;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
 
 import javax.servlet.ServletContainerInitializer;
@@ -681,4 +682,28 @@ public class FailedContext extends Lifec
     }
     @Override
     public boolean getAddWebinfClassesResources() { return false; }
+
+    @Override
+    public void addPostConstructMethod(String clazz, String method) { /* NO-OP 
*/ }
+
+    @Override
+    public void addPreDestroyMethod(String clazz, String method) { /* NO-OP */ 
}
+
+    @Override
+    public void removePostConstructMethod(String clazz) { /* NO-OP */ }
+
+    @Override
+    public void removePreDestroyMethod(String clazz) { /* NO-OP */ }
+
+    @Override
+    public String findPostConstructMethod(String clazz) { return null; }
+
+    @Override
+    public String findPreDestroyMethod(String clazz) { return null; }
+
+    @Override
+    public Map<String, String> findPostConstructMethods() { return null; }
+
+    @Override
+    public Map<String, String> findPreDestroyMethods() { return null; }
 }
\ No newline at end of file

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=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties 
(original)
+++ tomcat/trunk/java/org/apache/catalina/startup/LocalStrings.properties Thu 
Jan 10 11:39:46 2013
@@ -130,6 +130,8 @@ webAnnotationSet.invalidInjection=Invali
 webRuleSet.absoluteOrdering=<absolute-ordering> element not valid in 
web-fragment.xml and will be ignored
 webRuleSet.absoluteOrderingCount=<absolute-ordering> element is limited to 1 
occurrence
 webRuleSet.nameCount=<name> element is limited to 1 occurrence
+webRuleSet.postconstruct.duplicate=Duplicate post construct method definition 
for class {0}
+webRuleSet.predestroy.duplicate=Duplicate pre destroy method definition for 
class {0}
 webRuleSet.relativeOrdering=<ordering> element not valid in web.xml and will 
be ignored
 webRuleSet.relativeOrderingCount=<ordering> element is limited to 1 occurrence
 xmlErrorHandler.error=Non-fatal error [{0}] reported processing [{1}].

Modified: tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java (original)
+++ tomcat/trunk/java/org/apache/catalina/startup/WebRuleSet.java Thu Jan 10 
11:39:46 2013
@@ -473,6 +473,15 @@ public class WebRuleSet extends RuleSetB
         digester.addCallParam(fullPrefix + 
"/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0);
         digester.addCallParam(fullPrefix + 
"/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1);
 
+        digester.addRule(fullPrefix + "/post-construct",
+                new LifecycleCallbackRule("addPostConstructMethods", 2, true));
+        digester.addCallParam(fullPrefix + 
"/post-construct/lifecycle-callback-class", 0);
+        digester.addCallParam(fullPrefix + 
"/post-construct/lifecycle-callback-method", 1);
+
+        digester.addRule(fullPrefix + "/pre-destroy",
+                new LifecycleCallbackRule("addPreDestroyMethods", 2, false));
+        digester.addCallParam(fullPrefix + 
"/pre-destroy/lifecycle-callback-class", 0);
+        digester.addCallParam(fullPrefix + 
"/pre-destroy/lifecycle-callback-method", 1);
     }
 
     protected void configureNamingRules(Digester digester) {
@@ -1293,4 +1302,39 @@ final class MappedNameRule extends Rule 
         ResourceBase resourceBase = (ResourceBase) digester.peek();
         resourceBase.setProperty("mappedName", text.trim());
     }
-}
\ No newline at end of file
+}
+
+/**
+ * A rule that fails if more than one post construct or pre destroy methods
+ * are configured per class.
+ */
+final class LifecycleCallbackRule extends CallMethodRule {
+
+    private final boolean postConstruct;
+
+    public LifecycleCallbackRule(String methodName, int paramCount,
+            boolean postConstruct) {
+        super(methodName, paramCount);
+        this.postConstruct = postConstruct;
+    }
+
+    @Override
+    public void end(String namespace, String name) throws Exception {
+        Object[] params = (Object[]) digester.peekParams();
+        if (params != null && params.length == 2) {
+            WebXml webXml = (WebXml) digester.peek();
+            if (postConstruct) {
+                if (webXml.getPostConstructMethods().containsKey(params[0])) {
+                    throw new IllegalArgumentException(WebRuleSet.sm.getString(
+                            "webRuleSet.postconstruct.duplicate", params[0]));
+                }
+            } else {
+                if (webXml.getPreDestroyMethods().containsKey(params[0])) {
+                    throw new IllegalArgumentException(WebRuleSet.sm.getString(
+                            "webRuleSet.predestroy.duplicate", params[0]));
+                }
+            }
+        }
+        super.end(namespace, name);
+    }
+}

Modified: tomcat/trunk/java/org/apache/catalina/util/Introspection.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/catalina/util/Introspection.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/catalina/util/Introspection.java (original)
+++ tomcat/trunk/java/org/apache/catalina/util/Introspection.java Thu Jan 10 
11:39:46 2013
@@ -19,6 +19,7 @@ package org.apache.catalina.util;
 import java.beans.Introspector;
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
 import java.security.AccessController;
 import java.security.PrivilegedAction;
 
@@ -68,6 +69,24 @@ public class Introspection {
         return false;
     }
 
+    /**
+     * Determines if a method is a valid lifecycle callback method.
+     *
+     * @param method
+     *            The method to test
+     *
+     * @return <code>true</code> if the method is a valid lifecycle callback
+     *         method, else <code>false</code>
+     */
+    public static boolean isValidLifecycleCallback(Method method) {
+        if (method.getParameterTypes().length != 0
+                || Modifier.isStatic(method.getModifiers())
+                || method.getExceptionTypes().length > 0
+                || !method.getReturnType().getName().equals("void")) {
+            return false;
+        }
+        return true;
+    }
 
     /**
      * Obtain the declared fields for a class taking account of any security

Modified: tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/core/TestStandardContext.java Thu Jan 
10 11:39:46 2013
@@ -754,4 +754,38 @@ public class TestStandardContext extends
             return false; // Don't care
         }
     }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddPostConstructMethodNullClassName() {
+        new StandardContext().addPostConstructMethod(null, "");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddPostConstructMethodNullMethodName() {
+        new StandardContext().addPostConstructMethod("", null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddPostConstructMethodConflicts() {
+        StandardContext standardContext = new StandardContext();
+        standardContext.addPostConstructMethod("a", "a");
+        standardContext.addPostConstructMethod("a", "b");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddPreDestroyMethodNullClassName() {
+        new StandardContext().addPreDestroyMethod(null, "");
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddPreDestroyMethodNullMethodName() {
+        new StandardContext().addPreDestroyMethod("", null);
+    }
+
+    @Test(expected = IllegalArgumentException.class)
+    public void testAddPreDestroyMethodConflicts() {
+        StandardContext standardContext = new StandardContext();
+        standardContext.addPreDestroyMethod("a", "a");
+        standardContext.addPreDestroyMethod("a", "b");
+    }
 }

Modified: tomcat/trunk/test/org/apache/catalina/core/TesterContext.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/core/TesterContext.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/core/TesterContext.java (original)
+++ tomcat/trunk/test/org/apache/catalina/core/TesterContext.java Thu Jan 10 
11:39:46 2013
@@ -20,6 +20,7 @@ import java.beans.PropertyChangeListener
 import java.io.File;
 import java.net.URL;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Set;
 
 import javax.management.ObjectName;
@@ -1112,4 +1113,44 @@ public class TesterContext implements Co
     public boolean getAddWebinfClassesResources() {
         return false;
     }
+
+    @Override
+    public void addPostConstructMethod(String clazz, String method) {
+        // NO-OP
+    }
+
+    @Override
+    public void addPreDestroyMethod(String clazz, String method) {
+        // NO-OP
+    }
+
+    @Override
+    public void removePostConstructMethod(String clazz) {
+        // NO-OP
+    }
+
+    @Override
+    public void removePreDestroyMethod(String clazz) {
+        // NO-OP
+    }
+
+    @Override
+    public String findPostConstructMethod(String clazz) {
+        return null;
+    }
+
+    @Override
+    public String findPreDestroyMethod(String clazz) {
+        return null;
+    }
+
+    @Override
+    public Map<String,String> findPostConstructMethods() {
+        return null;
+    }
+
+    @Override
+    public Map<String,String> findPreDestroyMethods() {
+        return null;
+    }
 }

Modified: tomcat/trunk/test/org/apache/catalina/deploy/TestWebXml.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/deploy/TestWebXml.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/deploy/TestWebXml.java (original)
+++ tomcat/trunk/test/org/apache/catalina/deploy/TestWebXml.java Thu Jan 10 
11:39:46 2013
@@ -17,8 +17,11 @@
 
 package org.apache.catalina.deploy;
 
-import static org.junit.Assert.assertEquals;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
 
+import org.junit.Assert;
 import org.junit.Test;
 
 /**
@@ -32,46 +35,46 @@ public class TestWebXml {
         WebXml webxml = new WebXml();
 
         // Defaults
-        assertEquals(3, webxml.getMajorVersion());
-        assertEquals(0, webxml.getMinorVersion());
+        Assert.assertEquals(3, webxml.getMajorVersion());
+        Assert.assertEquals(0, webxml.getMinorVersion());
 
         // Both get changed
         webxml.setVersion("2.5");
-        assertEquals(2, webxml.getMajorVersion());
-        assertEquals(5, webxml.getMinorVersion());
+        Assert.assertEquals(2, webxml.getMajorVersion());
+        Assert.assertEquals(5, webxml.getMinorVersion());
 
         // Reset
         webxml.setVersion("0.0");
-        assertEquals(0, webxml.getMajorVersion());
-        assertEquals(0, webxml.getMinorVersion());
+        Assert.assertEquals(0, webxml.getMajorVersion());
+        Assert.assertEquals(0, webxml.getMinorVersion());
 
         // null input should be ignored
         webxml.setVersion(null);
-        assertEquals(0, webxml.getMajorVersion());
-        assertEquals(0, webxml.getMinorVersion());
+        Assert.assertEquals(0, webxml.getMajorVersion());
+        Assert.assertEquals(0, webxml.getMinorVersion());
 
         // major only
         webxml.setVersion("3");
-        assertEquals(3, webxml.getMajorVersion());
-        assertEquals(0, webxml.getMinorVersion());
+        Assert.assertEquals(3, webxml.getMajorVersion());
+        Assert.assertEquals(0, webxml.getMinorVersion());
 
         // no minor digit
         webxml.setVersion("0.0");   // reset
         webxml.setVersion("3.");
-        assertEquals(3, webxml.getMajorVersion());
-        assertEquals(0, webxml.getMinorVersion());
+        Assert.assertEquals(3, webxml.getMajorVersion());
+        Assert.assertEquals(0, webxml.getMinorVersion());
 
         // minor only
         webxml.setVersion("0.0");   // reset
         webxml.setVersion(".5");
-        assertEquals(0, webxml.getMajorVersion());
-        assertEquals(5, webxml.getMinorVersion());
+        Assert.assertEquals(0, webxml.getMajorVersion());
+        Assert.assertEquals(5, webxml.getMinorVersion());
 
         // leading & training zeros
         webxml.setVersion("0.0");   // reset
         webxml.setVersion("002.500");
-        assertEquals(2, webxml.getMajorVersion());
-        assertEquals(500, webxml.getMinorVersion());
+        Assert.assertEquals(2, webxml.getMajorVersion());
+        Assert.assertEquals(500, webxml.getMinorVersion());
     }
 
     @Test
@@ -81,9 +84,9 @@ public class TestWebXml {
 
         webxml.setPublicId(
                 org.apache.catalina.startup.Constants.WebDtdPublicId_22);
-        assertEquals(2, webxml.getMajorVersion());
-        assertEquals(2, webxml.getMinorVersion());
-        assertEquals("2.2", webxml.getVersion());
+        Assert.assertEquals(2, webxml.getMajorVersion());
+        Assert.assertEquals(2, webxml.getMinorVersion());
+        Assert.assertEquals("2.2", webxml.getVersion());
     }
 
     @Test
@@ -93,9 +96,9 @@ public class TestWebXml {
 
         webxml.setPublicId(
                 org.apache.catalina.startup.Constants.WebDtdPublicId_23);
-        assertEquals(2, webxml.getMajorVersion());
-        assertEquals(3, webxml.getMinorVersion());
-        assertEquals("2.3", webxml.getVersion());
+        Assert.assertEquals(2, webxml.getMajorVersion());
+        Assert.assertEquals(3, webxml.getMinorVersion());
+        Assert.assertEquals("2.3", webxml.getVersion());
     }
 
     @Test
@@ -105,9 +108,9 @@ public class TestWebXml {
 
         webxml.setPublicId(
                 org.apache.catalina.startup.Constants.WebSchemaPublicId_24);
-        assertEquals(2, webxml.getMajorVersion());
-        assertEquals(4, webxml.getMinorVersion());
-        assertEquals("2.4", webxml.getVersion());
+        Assert.assertEquals(2, webxml.getMajorVersion());
+        Assert.assertEquals(4, webxml.getMinorVersion());
+        Assert.assertEquals("2.4", webxml.getVersion());
     }
 
     @Test
@@ -117,9 +120,9 @@ public class TestWebXml {
 
         webxml.setPublicId(
                 org.apache.catalina.startup.Constants.WebSchemaPublicId_25);
-        assertEquals(2, webxml.getMajorVersion());
-        assertEquals(5, webxml.getMinorVersion());
-        assertEquals("2.5", webxml.getVersion());
+        Assert.assertEquals(2, webxml.getMajorVersion());
+        Assert.assertEquals(5, webxml.getMinorVersion());
+        Assert.assertEquals("2.5", webxml.getVersion());
     }
 
     @Test
@@ -129,8 +132,91 @@ public class TestWebXml {
 
         webxml.setPublicId(
                 org.apache.catalina.startup.Constants.WebSchemaPublicId_30);
-        assertEquals(3, webxml.getMajorVersion());
-        assertEquals(0, webxml.getMinorVersion());
-        assertEquals("3.0", webxml.getVersion());
+        Assert.assertEquals(3, webxml.getMajorVersion());
+        Assert.assertEquals(0, webxml.getMinorVersion());
+        Assert.assertEquals("3.0", webxml.getVersion());
+    }
+
+    @Test
+    public void testLifecycleMethodsWebXml() {
+        WebXml webxml = new WebXml();
+        webxml.addPostConstructMethods("a", "a");
+        webxml.addPreDestroyMethods("b", "b");
+
+        WebXml fragment = new WebXml();
+        fragment.addPostConstructMethods("c", "c");
+        fragment.addPreDestroyMethods("d", "d");
+
+        Set<WebXml> fragments = new HashSet<>();
+        fragments.add(fragment);
+
+        webxml.merge(fragments);
+
+        Map<String, String> postConstructMethods = 
webxml.getPostConstructMethods();
+        Map<String, String> preDestroyMethods = webxml.getPreDestroyMethods();
+        Assert.assertEquals(1, postConstructMethods.size());
+        Assert.assertEquals(1, preDestroyMethods.size());
+
+        Assert.assertEquals("a", postConstructMethods.get("a"));
+        Assert.assertEquals("b", preDestroyMethods.get("b"));
+    }
+
+    @Test
+    public void testLifecycleMethodsWebFragments() {
+        WebXml webxml = new WebXml();
+
+        WebXml fragment1 = new WebXml();
+        fragment1.addPostConstructMethods("a", "a");
+        fragment1.addPreDestroyMethods("b", "b");
+
+        WebXml fragment2 = new WebXml();
+        fragment2.addPostConstructMethods("c", "c");
+        fragment2.addPreDestroyMethods("d", "d");
+
+        Set<WebXml> fragments = new HashSet<>();
+        fragments.add(fragment1);
+        fragments.add(fragment2);
+
+        webxml.merge(fragments);
+
+        Map<String, String> postConstructMethods = 
webxml.getPostConstructMethods();
+        Map<String, String> preDestroyMethods = webxml.getPreDestroyMethods();
+        Assert.assertEquals(2, postConstructMethods.size());
+        Assert.assertEquals(2, preDestroyMethods.size());
+
+        Assert.assertEquals("a", postConstructMethods.get("a"));
+        Assert.assertEquals("c", postConstructMethods.get("c"));
+        Assert.assertEquals("b", preDestroyMethods.get("b"));
+        Assert.assertEquals("d", preDestroyMethods.get("d"));
+    }
+
+    @Test
+    public void testLifecycleMethodsWebFragmentsWithConflicts() {
+        WebXml webxml = new WebXml();
+
+        WebXml fragment1 = new WebXml();
+        fragment1.addPostConstructMethods("a", "a");
+        fragment1.addPreDestroyMethods("b", "a");
+
+        WebXml fragment2 = new WebXml();
+        fragment2.addPostConstructMethods("a", "b");
+
+        Set<WebXml> fragments = new HashSet<>();
+        fragments.add(fragment1);
+        fragments.add(fragment2);
+
+        Assert.assertFalse(webxml.merge(fragments));
+
+        Assert.assertEquals(0, webxml.getPostConstructMethods().size());
+
+        WebXml fragment3 = new WebXml();
+        fragment3.addPreDestroyMethods("b", "b");
+
+        fragments.remove(fragment2);
+        fragments.add(fragment3);
+
+        Assert.assertFalse(webxml.merge(fragments));
+
+        Assert.assertEquals(0, webxml.getPreDestroyMethods().size());
     }
 }

Modified: tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java 
(original)
+++ tomcat/trunk/test/org/apache/catalina/startup/TestContextConfig.java Thu 
Jan 10 11:39:46 2013
@@ -32,6 +32,7 @@ import javax.servlet.http.HttpServletRes
 import org.junit.Assert;
 import org.junit.Test;
 
+import org.apache.catalina.Context;
 import org.apache.catalina.core.StandardContext;
 import org.apache.tomcat.util.buf.ByteChunk;
 
@@ -106,6 +107,25 @@ public class TestContextConfig extends T
                 null, HttpServletResponse.SC_NOT_FOUND);
     }
 
+    @Test
+    public void testBug54379() throws Exception {
+        Tomcat tomcat = getTomcatInstance();
+
+        File appDir = new File("test/webapp-3.0-fragments");
+        Context context =
+                tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
+
+        Tomcat.addServlet(context, "TestServlet",
+                
"org.apache.catalina.startup.TesterServletWithLifeCycleMethods");
+        context.addServletMapping("/testServlet", "TestServlet");
+
+        tomcat.enableNaming();
+
+        tomcat.start();
+
+        assertPageContains("/test/testServlet", "postConstruct1()");
+    }
+
     private static class CustomDefaultServletSCI
             implements ServletContainerInitializer {
 

Modified: tomcat/trunk/test/org/apache/catalina/startup/TestWebRuleSet.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TestWebRuleSet.java?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/TestWebRuleSet.java (original)
+++ tomcat/trunk/test/org/apache/catalina/startup/TestWebRuleSet.java Thu Jan 
10 11:39:46 2013
@@ -115,6 +115,13 @@ public class TestWebRuleSet {
         parse(new WebXml(), "web-1ordering.xml", false, true);
 }
 
+    @Test
+    public void testLifecycleMethodsDefinitions() throws Exception {
+        // post-construct and pre-destroy
+        parse(new WebXml(), "web-1lifecyclecallback.xml", false, true);
+        // conflicting post-construct definitions
+        parse(new WebXml(), "web-2lifecyclecallback.xml", false, false);
+    }
 
     private synchronized void parse(WebXml webXml, String target,
             boolean fragment, boolean expected) throws FileNotFoundException {

Added: 
tomcat/trunk/test/org/apache/catalina/startup/TesterServletWithLifeCycleMethods.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/TesterServletWithLifeCycleMethods.java?rev=1431293&view=auto
==============================================================================
--- 
tomcat/trunk/test/org/apache/catalina/startup/TesterServletWithLifeCycleMethods.java
 (added)
+++ 
tomcat/trunk/test/org/apache/catalina/startup/TesterServletWithLifeCycleMethods.java
 Thu Jan 10 11:39:46 2013
@@ -0,0 +1,59 @@
+/*
+ * 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.IOException;
+
+import javax.annotation.PostConstruct;
+import javax.annotation.PreDestroy;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServlet;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+public class TesterServletWithLifeCycleMethods extends HttpServlet {
+
+    private static final long serialVersionUID = 1L;
+
+    private String result;
+
+    @Override
+    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
+            throws ServletException, IOException {
+        resp.setContentType("text/plain");
+        resp.getWriter().print(result);
+    }
+
+    @PostConstruct
+    protected void postConstruct() {
+        result = "postConstruct()";
+    }
+
+    @PreDestroy
+    protected void preDestroy() {
+        result = "preDestroy()";
+    }
+
+    protected void postConstruct1() {
+        result = "postConstruct1()";
+    }
+
+    protected void preDestroy1() {
+        result = "preDestroy1()";
+    }
+}

Propchange: 
tomcat/trunk/test/org/apache/catalina/startup/TesterServletWithLifeCycleMethods.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/test/org/apache/catalina/startup/web-1lifecyclecallback.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/web-1lifecyclecallback.xml?rev=1431293&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/web-1lifecyclecallback.xml 
(added)
+++ tomcat/trunk/test/org/apache/catalina/startup/web-1lifecyclecallback.xml 
Thu Jan 10 11:39:46 2013
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee";
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
+                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd";
+  version="3.0"
+  metadata-complete="true">
+  <post-construct>
+    <lifecycle-callback-class>test.TestServlet</lifecycle-callback-class>
+    <lifecycle-callback-method>postConstruct</lifecycle-callback-method>
+  </post-construct>
+  <pre-destroy>
+    <lifecycle-callback-class>test.TestServlet</lifecycle-callback-class>
+    <lifecycle-callback-method>preDestroy</lifecycle-callback-method>
+  </pre-destroy>
+</web-app>
\ No newline at end of file

Propchange: 
tomcat/trunk/test/org/apache/catalina/startup/web-1lifecyclecallback.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Added: tomcat/trunk/test/org/apache/catalina/startup/web-2lifecyclecallback.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/catalina/startup/web-2lifecyclecallback.xml?rev=1431293&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/catalina/startup/web-2lifecyclecallback.xml 
(added)
+++ tomcat/trunk/test/org/apache/catalina/startup/web-2lifecyclecallback.xml 
Thu Jan 10 11:39:46 2013
@@ -0,0 +1,32 @@
+<?xml version="1.0" encoding="ISO-8859-1"?>
+<!--
+  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.
+-->
+<web-app xmlns="http://java.sun.com/xml/ns/javaee";
+  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
+                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd";
+  version="3.0"
+  metadata-complete="true">
+  <post-construct>
+    <lifecycle-callback-class>test.TestServlet</lifecycle-callback-class>
+    <lifecycle-callback-method>postConstruct1</lifecycle-callback-method>
+  </post-construct>
+  <post-construct>
+    <lifecycle-callback-class>test.TestServlet</lifecycle-callback-class>
+    <lifecycle-callback-method>postConstruct2</lifecycle-callback-method>
+  </post-construct>
+</web-app>
\ No newline at end of file

Propchange: 
tomcat/trunk/test/org/apache/catalina/startup/web-2lifecyclecallback.xml
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/test/webapp-3.0-fragments/WEB-INF/web.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/webapp-3.0-fragments/WEB-INF/web.xml?rev=1431293&r1=1431292&r2=1431293&view=diff
==============================================================================
--- tomcat/trunk/test/webapp-3.0-fragments/WEB-INF/web.xml (original)
+++ tomcat/trunk/test/webapp-3.0-fragments/WEB-INF/web.xml Thu Jan 10 11:39:46 
2013
@@ -49,4 +49,12 @@
     <jsp-file>/bug5nnnn/bug51396.jsp</jsp-file>
   </servlet>
 
+  <post-construct>
+    
<lifecycle-callback-class>org.apache.catalina.startup.TesterServletWithLifeCycleMethods</lifecycle-callback-class>
+    <lifecycle-callback-method>postConstruct1</lifecycle-callback-method>
+  </post-construct>
+  <pre-destroy>
+    
<lifecycle-callback-class>org.apache.catalina.startup.TesterServletWithLifeCycleMethods</lifecycle-callback-class>
+    <lifecycle-callback-method>preDestroy1</lifecycle-callback-method>
+  </pre-destroy>
 </web-app>
\ No newline at end of file



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to