View | Details | Raw Unified | Return to bug 54379
Collapse All | Expand All

(-)java/org/apache/catalina/Context.java (+98 lines)
Lines 21-26 Link Here
21
21
22
import java.net.URL;
22
import java.net.URL;
23
import java.util.Locale;
23
import java.util.Locale;
24
import java.util.Map;
24
import java.util.Set;
25
import java.util.Set;
25
26
26
import javax.servlet.ServletContainerInitializer;
27
import javax.servlet.ServletContainerInitializer;
Lines 1424-1428 Link Here
1424
     * part of a redirect response.
1425
     * part of a redirect response.
1425
     */
1426
     */
1426
    public boolean getSendRedirectBody();
1427
    public boolean getSendRedirectBody();
1428
1429
    /**
1430
     * Add a post construct method definition for the given class, if there is
1431
     * an existing definition for the specified class - IllegalArgumentException
1432
     * will be thrown.
1433
     * 
1434
     * @param class Fully qualified class name
1435
     * @param method
1436
     *            Post construct method name
1437
     * @throws IllegalArgumentException
1438
     *             if the fully qualified class name or method name are
1439
     *             <code>NULL</code>; if there is already post construct method
1440
     *             definition for the given class
1441
     */
1442
    public void addPostConstructMethod(String clazz, String method);
1443
1444
    /**
1445
     * Add a pre destroy method definition for the given class, if there is an
1446
     * existing definition for the specified class - IllegalArgumentException
1447
     * will be thrown.
1448
     * 
1449
     * @param class Fully qualified class name
1450
     * @param method
1451
     *            Post construct method name
1452
     * @throws IllegalArgumentException
1453
     *             if the fully qualified class name or method name are
1454
     *             <code>NULL</code>; if there is already pre destroy method
1455
     *             definition for the given class
1456
     */
1457
    public void addPreDestroyMethod(String clazz, String method);
1458
1459
    /**
1460
     * Removes the post construct method definition for the given class, if it
1461
     * exists; otherwise, no action is taken.
1462
     * 
1463
     * @param clazz
1464
     *            Fully qualified class name
1465
     */
1466
    public void removePostConstructMethod(String clazz);
1467
1468
    /**
1469
     * Removes the pre destroy method definition for the given class, if it
1470
     * exists; otherwise, no action is taken.
1471
     * 
1472
     * @param clazz
1473
     *            Fully qualified class name
1474
     */
1475
    public void removePreDestroyMethod(String clazz);
1476
1477
    /**
1478
     * Returns the method name that is specified as post construct method for
1479
     * the given class, if it exists; otherwise <code>NULL</code> will be
1480
     * returned.
1481
     * 
1482
     * @param clazz
1483
     *            Fully qualified class name
1484
     * 
1485
     * @return the method name that is specified as post construct method for
1486
     *         the given class, if it exists; otherwise <code>NULL</code> will
1487
     *         be returned.
1488
     */
1489
    public String findPostConstructMethod(String clazz);
1490
1491
    /**
1492
     * Returns the method name that is specified as pre destroy method for the
1493
     * given class, if it exists; otherwise <code>NULL</code> will be returned.
1494
     * 
1495
     * @param clazz
1496
     *            Fully qualified class name
1497
     * 
1498
     * @return the method name that is specified as pre destroy method for the
1499
     *         given class, if it exists; otherwise <code>NULL</code> will be
1500
     *         returned.
1501
     */
1502
    public String findPreDestroyMethod(String clazz);
1503
1504
    /**
1505
     * Returns a map with keys - fully qualified class names of the classes that
1506
     * have post construct methods and the values are the corresponding method
1507
     * names. If there are no such classes an empty map will be returned.
1508
     * 
1509
     * @return a map with keys - fully qualified class names of the classes that
1510
     *         have post construct methods and the values are the corresponding
1511
     *         method names.
1512
     */
1513
    public Map<String, String> findPostConstructMethods();
1514
1515
    /**
1516
     * Returns a map with keys - fully qualified class names of the classes that
1517
     * have pre destroy methods and the values are the corresponding method
1518
     * names. If there are no such classes an empty map will be returned.
1519
     * 
1520
     * @return a map with keys - fully qualified class names of the classes that
1521
     *         have pre destroy methods and the values are the corresponding
1522
     *         method names.
1523
     */
1524
    public Map<String, String> findPreDestroyMethods();
1427
}
1525
}
1428
1526
(-)java/org/apache/catalina/core/LocalStrings.properties (+4 lines)
Lines 145-150 Link Here
145
standardContext.parameter.duplicate=Duplicate context initialization parameter {0}
145
standardContext.parameter.duplicate=Duplicate context initialization parameter {0}
146
standardContext.parameter.required=Both parameter name and parameter value are required
146
standardContext.parameter.required=Both parameter name and parameter value are required
147
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}]
147
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}]
148
standardContext.postconstruct.duplicate=Duplicate post construct method definition for class {0}
149
standardContext.postconstruct.required=Both fully qualified class name and method name are required
150
standardContext.predestroy.duplicate=Duplicate pre destroy method definition for class {0}
151
standardContext.predestroy.required=Both fully qualified class name and method name are required
148
standardContext.reloadingCompleted=Reloading Context with name [{0}] is completed
152
standardContext.reloadingCompleted=Reloading Context with name [{0}] is completed
149
standardContext.reloadingFailed=Reloading this Context failed due to previous errors
153
standardContext.reloadingFailed=Reloading this Context failed due to previous errors
150
standardContext.reloadingStarted=Reloading Context with name [{0}] has started
154
standardContext.reloadingStarted=Reloading Context with name [{0}] has started
(-)java/org/apache/catalina/core/DefaultInstanceManager.java (-23 / +54 lines)
Lines 19-28 Link Here
19
import java.beans.Introspector;
19
import java.beans.Introspector;
20
import java.io.IOException;
20
import java.io.IOException;
21
import java.io.InputStream;
21
import java.io.InputStream;
22
import java.lang.annotation.Annotation;
22
import java.lang.reflect.Field;
23
import java.lang.reflect.Field;
23
import java.lang.reflect.InvocationTargetException;
24
import java.lang.reflect.InvocationTargetException;
24
import java.lang.reflect.Method;
25
import java.lang.reflect.Method;
25
import java.lang.reflect.Modifier;
26
import java.security.AccessController;
26
import java.security.AccessController;
27
import java.security.PrivilegedAction;
27
import java.security.PrivilegedAction;
28
import java.security.PrivilegedActionException;
28
import java.security.PrivilegedActionException;
Lines 80-85 Link Here
80
    private final Properties restrictedServlets = new Properties();
80
    private final Properties restrictedServlets = new Properties();
81
    private final Map<Class<?>, AnnotationCacheEntry[]> annotationCache =
81
    private final Map<Class<?>, AnnotationCacheEntry[]> annotationCache =
82
        new WeakHashMap<Class<?>, AnnotationCacheEntry[]>();
82
        new WeakHashMap<Class<?>, AnnotationCacheEntry[]>();
83
    private final Map<String, String> postConstructMethods;
84
    private final Map<String, String> preDestroyMethods;
83
85
84
    public DefaultInstanceManager(Context context, Map<String, Map<String, String>> injectionMap, org.apache.catalina.Context catalinaContext, ClassLoader containerClassLoader) {
86
    public DefaultInstanceManager(Context context, Map<String, Map<String, String>> injectionMap, org.apache.catalina.Context catalinaContext, ClassLoader containerClassLoader) {
85
        classLoader = catalinaContext.getLoader().getClassLoader();
87
        classLoader = catalinaContext.getLoader().getClassLoader();
Lines 126-131 Link Here
126
        }
128
        }
127
        this.context = context;
129
        this.context = context;
128
        this.injectionMap = injectionMap;
130
        this.injectionMap = injectionMap;
131
        this.postConstructMethods = catalinaContext.findPostConstructMethods();
132
        this.preDestroyMethods = catalinaContext.findPreDestroyMethods();
129
    }
133
    }
130
134
131
    @Override
135
    @Override
Lines 332-338 Link Here
332
                // Initialize methods annotations
336
                // Initialize methods annotations
333
                Method[] methods = Introspection.getDeclaredMethods(clazz);
337
                Method[] methods = Introspection.getDeclaredMethods(clazz);
334
                Method postConstruct = null;
338
                Method postConstruct = null;
339
                String postConstructFromXml = this.postConstructMethods.get(clazz.getName());
335
                Method preDestroy = null;
340
                Method preDestroy = null;
341
                String preDestroyFromXml = this.preDestroyMethods.get(clazz.getName());
336
                for (Method method : methods) {
342
                for (Method method : methods) {
337
                    if (context != null) {
343
                    if (context != null) {
338
                        // Resource injection only if JNDI is enabled
344
                        // Resource injection only if JNDI is enabled
Lines 389-429 Link Here
389
                        }
395
                        }
390
                    }
396
                    }
391
397
392
                    if (method.isAnnotationPresent(PostConstruct.class)) {
398
                    postConstruct = findPostConstruct(postConstruct, postConstructFromXml, method);
393
                        if ((postConstruct != null) ||
394
                                (method.getParameterTypes().length != 0) ||
395
                                (Modifier.isStatic(method.getModifiers())) ||
396
                                (method.getExceptionTypes().length > 0) ||
397
                                (!method.getReturnType().getName().equals("void"))) {
398
                            throw new IllegalArgumentException(
399
                                    "Invalid PostConstruct annotation");
400
                        }
401
                        postConstruct = method;
402
                    }
403
399
404
                    if (method.isAnnotationPresent(PreDestroy.class)) {
400
                    preDestroy = findPreDestroy(preDestroy, preDestroyFromXml, method);
405
                        if ((preDestroy != null ||
406
                                method.getParameterTypes().length != 0) ||
407
                                (Modifier.isStatic(method.getModifiers())) ||
408
                                (method.getExceptionTypes().length > 0) ||
409
                                (!method.getReturnType().getName().equals("void"))) {
410
                            throw new IllegalArgumentException(
411
                                    "Invalid PreDestroy annotation");
412
                        }
413
                        preDestroy = method;
414
                    }
415
                }
401
                }
402
416
                if (postConstruct != null) {
403
                if (postConstruct != null) {
417
                    annotations.add(new AnnotationCacheEntry(
404
                    annotations.add(new AnnotationCacheEntry(
418
                            postConstruct.getName(),
405
                            postConstruct.getName(),
419
                            postConstruct.getParameterTypes(), null,
406
                            postConstruct.getParameterTypes(), null,
420
                            AnnotationCacheEntryType.POST_CONSTRUCT));
407
                            AnnotationCacheEntryType.POST_CONSTRUCT));
408
                } else if (postConstructFromXml != null) {
409
                    throw new IllegalArgumentException("Post construct method " 
410
                        + postConstructFromXml + " for class " + clazz.getName()
411
                        + " is declared in deployment descriptor but cannot be found.");
421
                }
412
                }
422
                if (preDestroy != null) {
413
                if (preDestroy != null) {
423
                    annotations.add(new AnnotationCacheEntry(
414
                    annotations.add(new AnnotationCacheEntry(
424
                            preDestroy.getName(),
415
                            preDestroy.getName(),
425
                            preDestroy.getParameterTypes(), null,
416
                            preDestroy.getParameterTypes(), null,
426
                            AnnotationCacheEntryType.PRE_DESTROY));
417
                            AnnotationCacheEntryType.PRE_DESTROY));
418
                } else if (preDestroyFromXml != null) {
419
                    throw new IllegalArgumentException("Pre destroy method " 
420
                        + preDestroyFromXml + " for class " + clazz.getName()
421
                        + " is declared in deployment descriptor but cannot be found.");
427
                }
422
                }
428
                if (annotations.isEmpty()) {
423
                if (annotations.isEmpty()) {
429
                    // Use common object to save memory
424
                    // Use common object to save memory
Lines 745-748 Link Here
745
    private static enum AnnotationCacheEntryType {
740
    private static enum AnnotationCacheEntryType {
746
        FIELD, SETTER, POST_CONSTRUCT, PRE_DESTROY
741
        FIELD, SETTER, POST_CONSTRUCT, PRE_DESTROY
747
    }
742
    }
743
744
    private Method findPostConstruct(Method currentPostConstruct, 
745
        String postConstructFromXml, Method method) {
746
        return findLifecycleCallback(currentPostConstruct, 
747
            postConstructFromXml, method, PostConstruct.class);
748
    }
749
750
    private Method findPreDestroy(Method currentPreDestroy, 
751
        String preDestroyFromXml, Method method) {
752
        return findLifecycleCallback(currentPreDestroy, 
753
            preDestroyFromXml, method, PreDestroy.class);
754
    }
755
756
    private Method findLifecycleCallback(Method currentMethod, String methodNameFromXml, 
757
        Method method, Class<? extends Annotation> annotation) {
758
        Method result = currentMethod;
759
        if (methodNameFromXml != null) {
760
            if (method.getName().equals(methodNameFromXml)) {
761
                if (!Introspection.isValidLifecycleCallback(method)) {
762
                    throw new IllegalArgumentException(
763
                        "Invalid " + annotation.getName() + " annotation");
764
                }
765
                result = method;
766
            }
767
        } else {
768
            if (method.isAnnotationPresent(annotation)) {
769
                if (currentMethod != null || 
770
                    !Introspection.isValidLifecycleCallback(method)) {
771
                    throw new IllegalArgumentException(
772
                        "Invalid " + annotation.getName() + " annotation");
773
                }
774
                result = method;
775
            }
776
        }
777
        return result;
778
    }
748
}
779
}
(-)java/org/apache/catalina/core/StandardContext.java (+74 lines)
Lines 6520-6523 Link Here
6520
    public boolean isStatisticsProvider() {
6520
    public boolean isStatisticsProvider() {
6521
        return false;
6521
        return false;
6522
    }
6522
    }
6523
6524
    private Map<String, String> postConstructMethods = new HashMap<String, String>();
6525
6526
    @Override
6527
    public void addPostConstructMethod(String clazz, String method) {
6528
        if (clazz == null || method == null)
6529
            throw new IllegalArgumentException(
6530
                    sm.getString("standardContext.postconstruct.required"));
6531
        if (this.postConstructMethods.get(clazz) != null)
6532
            throw new IllegalArgumentException(sm.getString(
6533
                    "standardContext.postconstruct.duplicate", clazz));
6534
6535
        synchronized (this.postConstructMethods) {
6536
            this.postConstructMethods.put(clazz, method);
6537
        }
6538
        fireContainerEvent("addPostConstructMethod", clazz);
6539
    }
6540
6541
    @Override
6542
    public void removePostConstructMethod(String clazz) {
6543
        synchronized (this.postConstructMethods) {
6544
            this.postConstructMethods.remove(clazz);
6545
        }
6546
        fireContainerEvent("removePostConstructMethod", clazz);
6547
    }
6548
6549
    private Map<String, String> preDestroyMethods = new HashMap<String, String>();
6550
6551
    @Override
6552
    public void addPreDestroyMethod(String clazz, String method) {
6553
        if (clazz == null || method == null)
6554
            throw new IllegalArgumentException(
6555
                    sm.getString("standardContext.predestroy.required"));
6556
        if (this.preDestroyMethods.get(clazz) != null)
6557
            throw new IllegalArgumentException(sm.getString(
6558
                    "standardContext.predestroy.duplicate", clazz));
6559
6560
        synchronized (this.preDestroyMethods) {
6561
            this.preDestroyMethods.put(clazz, method);
6562
        }
6563
        fireContainerEvent("addPreDestroyMethod", clazz);
6564
    }
6565
6566
    @Override
6567
    public void removePreDestroyMethod(String clazz) {
6568
        synchronized (this.preDestroyMethods) {
6569
            this.preDestroyMethods.remove(clazz);
6570
        }
6571
        fireContainerEvent("removePreDestroyMethod", clazz);
6572
    }
6573
6574
    @Override
6575
    public String findPostConstructMethod(String clazz) {
6576
        synchronized (this.postConstructMethods) {
6577
            return (this.postConstructMethods.get(clazz));
6578
        }
6579
    }
6580
6581
    @Override
6582
    public String findPreDestroyMethod(String clazz) {
6583
        synchronized (this.preDestroyMethods) {
6584
            return (this.preDestroyMethods.get(clazz));
6585
        }
6586
    }
6587
6588
    @Override
6589
    public Map<String, String> findPostConstructMethods() {
6590
        return this.postConstructMethods;
6591
    }
6592
6593
    @Override
6594
    public Map<String, String> findPreDestroyMethods() {
6595
        return this.preDestroyMethods;
6596
    }
6523
}
6597
}
(-)java/org/apache/catalina/deploy/WebXml.java (+97 lines)
Lines 568-573 Link Here
568
        return localeEncodingMappings;
568
        return localeEncodingMappings;
569
    }
569
    }
570
570
571
    // post-construct elements
572
    private Map<String, String> postConstructMethods = new HashMap<String, String>();
573
    public void addPostConstructMethods(String clazz, String method) {
574
        if (!this.postConstructMethods.containsKey(clazz)) {
575
            this.postConstructMethods.put(clazz, method);
576
        }
577
    }
578
    public Map<String, String> getPostConstructMethods() {
579
        return this.postConstructMethods;
580
    }
581
582
    // pre-destroy elements
583
    private Map<String, String> preDestroyMethods = new HashMap<String, String>();
584
    public void addPreDestroyMethods(String clazz, String method) {
585
        if (!this.preDestroyMethods.containsKey(clazz)) {
586
            this.preDestroyMethods.put(clazz, method);
587
        }
588
    }
589
    public Map<String, String> getPreDestroyMethods() {
590
        return this.preDestroyMethods;
591
    }
571
592
572
    // Attributes not defined in web.xml or web-fragment.xml
593
    // Attributes not defined in web.xml or web-fragment.xml
573
594
Lines 1075-1080 Link Here
1075
        }
1096
        }
1076
        sb.append('\n');
1097
        sb.append('\n');
1077
1098
1099
        if (!this.postConstructMethods.isEmpty()) {
1100
            for (Entry<String, String> entry : this.postConstructMethods
1101
                    .entrySet()) {
1102
                sb.append("  <post-construct>\n");
1103
                appendElement(sb, INDENT4, "lifecycle-callback-class",
1104
                        entry.getKey());
1105
                appendElement(sb, INDENT4, "lifecycle-callback-method",
1106
                        entry.getValue());
1107
                sb.append("  </post-construct>\n");
1108
            }
1109
            sb.append('\n');
1110
        }
1111
1112
        if (!this.preDestroyMethods.isEmpty()) {
1113
            for (Entry<String, String> entry : this.preDestroyMethods
1114
                    .entrySet()) {
1115
                sb.append("  <pre-destroy>\n");
1116
                appendElement(sb, INDENT4, "lifecycle-callback-class",
1117
                        entry.getKey());
1118
                appendElement(sb, INDENT4, "lifecycle-callback-method",
1119
                        entry.getValue());
1120
                sb.append("  </pre-destroy>\n");
1121
            }
1122
            sb.append('\n');
1123
        }
1124
1078
        for (MessageDestinationRef mdr : messageDestinationRefs.values()) {
1125
        for (MessageDestinationRef mdr : messageDestinationRefs.values()) {
1079
            sb.append("  <message-destination-ref>\n");
1126
            sb.append("  <message-destination-ref>\n");
1080
            appendElement(sb, INDENT4, "description", mdr.getDescription());
1127
            appendElement(sb, INDENT4, "description", mdr.getDescription());
Lines 1374-1379 Link Here
1374
                }
1421
                }
1375
            }
1422
            }
1376
        }
1423
        }
1424
1425
        for (Entry<String, String> entry : this.postConstructMethods.entrySet()) {
1426
            context.addPostConstructMethod(entry.getKey(), entry.getValue());
1427
        }
1428
1429
        for (Entry<String, String> entry : this.preDestroyMethods.entrySet()) {
1430
            context.addPreDestroyMethod(entry.getKey(), entry.getValue());
1431
        }
1377
    }
1432
    }
1378
1433
1379
    /**
1434
    /**
Lines 1870-1875 Link Here
1870
            }
1925
            }
1871
        }
1926
        }
1872
1927
1928
        if (this.postConstructMethods.isEmpty()) {
1929
            for (WebXml fragment : fragments) {
1930
                if (!mergeLifecycleCallback(fragment.getPostConstructMethods(),
1931
                        temp.getPostConstructMethods(), fragment,
1932
                        "Post Construct Methods")) {
1933
                    return false;
1934
                }
1935
            }
1936
            this.postConstructMethods.putAll(temp.getPostConstructMethods());
1937
        }
1938
1939
        if (this.preDestroyMethods.isEmpty()) {
1940
            for (WebXml fragment : fragments) {
1941
                if (!mergeLifecycleCallback(fragment.getPreDestroyMethods(),
1942
                        temp.getPreDestroyMethods(), fragment,
1943
                        "Pre Destroy Methods")) {
1944
                    return false;
1945
                }
1946
            }
1947
            this.preDestroyMethods.putAll(temp.getPreDestroyMethods());
1948
        }
1949
1873
        return true;
1950
        return true;
1874
    }
1951
    }
1875
1952
Lines 2094-2099 Link Here
2094
    }
2171
    }
2095
2172
2096
2173
2174
    private static <T> boolean mergeLifecycleCallback(
2175
            Map<String, String> fragmentMap, Map<String, String> tempMap,
2176
            WebXml fragment, String mapName) {
2177
        for (Entry<String, String> entry : fragmentMap.entrySet()) {
2178
            final String key = entry.getKey();
2179
            final String value = entry.getValue();
2180
            if (tempMap.containsKey(key)) {
2181
                if (value != null && !value.equals(tempMap.get(key))) {
2182
                    log.error(sm.getString("webXml.mergeConflictString",
2183
                            mapName, key, fragment.getName(), fragment.getURL()));
2184
                    return false;
2185
                }
2186
            } else {
2187
                tempMap.put(key, value);
2188
            }
2189
        }
2190
        return true;
2191
    }
2192
2193
2097
    /**
2194
    /**
2098
     * Generates the sub-set of the web-fragment.xml files to be processed in
2195
     * Generates the sub-set of the web-fragment.xml files to be processed in
2099
     * the order that the fragments must be processed as per the rules in the
2196
     * the order that the fragments must be processed as per the rules in the
(-)java/org/apache/catalina/startup/LocalStrings.properties (+2 lines)
Lines 136-141 Link Here
136
webRuleSet.absoluteOrdering=<absolute-ordering> element not valid in web-fragment.xml and will be ignored
136
webRuleSet.absoluteOrdering=<absolute-ordering> element not valid in web-fragment.xml and will be ignored
137
webRuleSet.absoluteOrderingCount=<absolute-ordering> element is limited to 1 occurrence
137
webRuleSet.absoluteOrderingCount=<absolute-ordering> element is limited to 1 occurrence
138
webRuleSet.nameCount=<name> element is limited to 1 occurrence
138
webRuleSet.nameCount=<name> element is limited to 1 occurrence
139
webRuleSet.postconstruct.duplicate=Duplicate post construct method definition for class {0}
140
webRuleSet.predestroy.duplicate=Duplicate pre destroy method definition for class {0}
139
webRuleSet.relativeOrdering=<ordering> element not valid in web.xml and will be ignored
141
webRuleSet.relativeOrdering=<ordering> element not valid in web.xml and will be ignored
140
webRuleSet.relativeOrderingCount=<ordering> element is limited to 1 occurrence
142
webRuleSet.relativeOrderingCount=<ordering> element is limited to 1 occurrence
141
xmlErrorHandler.error=Non-fatal error [{0}] reported processing [{1}].
143
xmlErrorHandler.error=Non-fatal error [{0}] reported processing [{1}].
(-)java/org/apache/catalina/startup/WebRuleSet.java (-1 / +44 lines)
Lines 473-478 Link Here
473
        digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0);
473
        digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/locale", 0);
474
        digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1);
474
        digester.addCallParam(fullPrefix + "/locale-encoding-mapping-list/locale-encoding-mapping/encoding", 1);
475
475
476
        digester.addRule(fullPrefix + "/post-construct",
477
                new LifecycleCallbackRule("addPostConstructMethods", 2, true));
478
        digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-class", 0);
479
        digester.addCallParam(fullPrefix + "/post-construct/lifecycle-callback-method", 1);
480
481
        digester.addRule(fullPrefix + "/pre-destroy",
482
                new LifecycleCallbackRule("addPreDestroyMethods", 2, false));
483
        digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-class", 0);
484
        digester.addCallParam(fullPrefix + "/pre-destroy/lifecycle-callback-method", 1);
476
    }
485
    }
477
486
478
    protected void configureNamingRules(Digester digester) {
487
    protected void configureNamingRules(Digester digester) {
Lines 1293-1296 Link Here
1293
        ResourceBase resourceBase = (ResourceBase) digester.peek();
1302
        ResourceBase resourceBase = (ResourceBase) digester.peek();
1294
        resourceBase.setProperty("mappedName", text.trim());
1303
        resourceBase.setProperty("mappedName", text.trim());
1295
    }
1304
    }
1296
}
1305
}
1306
1307
/**
1308
 * A rule that fails if more than one post construct or pre destroy methods 
1309
 * are configured per class.
1310
 */
1311
final class LifecycleCallbackRule extends CallMethodRule {
1312
1313
    private final boolean postConstruct;
1314
1315
    public LifecycleCallbackRule(String methodName, int paramCount, boolean postConstruct) {
1316
        super(methodName, paramCount);
1317
        this.postConstruct = postConstruct;
1318
    }
1319
1320
    @Override
1321
    public void end(String namespace, String name) throws Exception {
1322
        Object[] params = (Object[]) digester.peekParams();
1323
        if (params != null && params.length == 2) {
1324
            WebXml webXml = (WebXml) digester.peek();
1325
            if (postConstruct) {
1326
                if (webXml.getPostConstructMethods().containsKey(params[0])) {
1327
                    throw new IllegalArgumentException(WebRuleSet.sm.getString(
1328
                        "webRuleSet.postconstruct.duplicate", params[0]));
1329
                }
1330
            } else {
1331
                if (webXml.getPreDestroyMethods().containsKey(params[0])) {
1332
                    throw new IllegalArgumentException(WebRuleSet.sm.getString(
1333
                        "webRuleSet.predestroy.duplicate", params[0]));
1334
                }
1335
            }
1336
        }
1337
        super.end(namespace, name);
1338
    }
1339
}
(-)java/org/apache/catalina/startup/FailedContext.java (+25 lines)
Lines 20-25 Link Here
20
import java.io.IOException;
20
import java.io.IOException;
21
import java.net.URL;
21
import java.net.URL;
22
import java.util.Locale;
22
import java.util.Locale;
23
import java.util.Map;
23
import java.util.Set;
24
import java.util.Set;
24
25
25
import javax.naming.directory.DirContext;
26
import javax.naming.directory.DirContext;
Lines 656-659 Link Here
656
657
657
    @Override
658
    @Override
658
    public Object getMappingObject() { return null; }
659
    public Object getMappingObject() { return null; }
660
661
    @Override
662
    public void addPostConstructMethod(String clazz, String method) { /* NO-OP */ }
663
664
    @Override
665
    public void addPreDestroyMethod(String clazz, String method) { /* NO-OP */ }
666
667
    @Override
668
    public void removePostConstructMethod(String clazz) { /* NO-OP */ }
669
670
    @Override
671
    public void removePreDestroyMethod(String clazz) { /* NO-OP */ }
672
673
    @Override
674
    public String findPostConstructMethod(String clazz) { return null; }
675
676
    @Override
677
    public String findPreDestroyMethod(String clazz) { return null; }
678
679
    @Override
680
    public Map<String, String> findPostConstructMethods() { return null; }
681
682
    @Override
683
    public Map<String, String> findPreDestroyMethods() { return null; }
659
}
684
}
(-)java/org/apache/catalina/util/Introspection.java (+19 lines)
Lines 19-24 Link Here
19
import java.beans.Introspector;
19
import java.beans.Introspector;
20
import java.lang.reflect.Field;
20
import java.lang.reflect.Field;
21
import java.lang.reflect.Method;
21
import java.lang.reflect.Method;
22
import java.lang.reflect.Modifier;
22
import java.security.AccessController;
23
import java.security.AccessController;
23
import java.security.PrivilegedAction;
24
import java.security.PrivilegedAction;
24
25
Lines 68-73 Link Here
68
        return false;
69
        return false;
69
    }
70
    }
70
71
72
    /**
73
     * Determines if a method is a valid lifecycle callback method.
74
     * 
75
     * @param method
76
     *            The method to test
77
     * 
78
     * @return <code>true</code> if the method is a valid lifecycle callback
79
     *         method, else <code>false</code>
80
     */
81
    public static boolean isValidLifecycleCallback(Method method) {
82
        if (method.getParameterTypes().length != 0
83
                || Modifier.isStatic(method.getModifiers())
84
                || method.getExceptionTypes().length > 0
85
                || !method.getReturnType().getName().equals("void")) {
86
            return false;
87
        }
88
        return true;
89
    }
71
90
72
    /**
91
    /**
73
     * Obtain the declared fields for a class taking account of any security
92
     * Obtain the declared fields for a class taking account of any security
(-)test/org/apache/catalina/core/TestStandardContext.java (+34 lines)
Lines 754-757 Link Here
754
            return false; // Don't care
754
            return false; // Don't care
755
        }
755
        }
756
    }
756
    }
757
758
    @Test(expected = IllegalArgumentException.class)
759
    public void testAddPostConstructMethodNullClassName() {
760
        new StandardContext().addPostConstructMethod(null, "");
761
    }
762
763
    @Test(expected = IllegalArgumentException.class)
764
    public void testAddPostConstructMethodNullMethodName() {
765
        new StandardContext().addPostConstructMethod("", null);
766
    }
767
768
    @Test(expected = IllegalArgumentException.class)
769
    public void testAddPostConstructMethodConflicts() {
770
        StandardContext standardContext = new StandardContext();
771
        standardContext.addPostConstructMethod("a", "a");
772
        standardContext.addPostConstructMethod("a", "b");
773
    }
774
775
    @Test(expected = IllegalArgumentException.class)
776
    public void testAddPreDestroyMethodNullClassName() {
777
        new StandardContext().addPreDestroyMethod(null, "");
778
    }
779
780
    @Test(expected = IllegalArgumentException.class)
781
    public void testAddPreDestroyMethodNullMethodName() {
782
        new StandardContext().addPreDestroyMethod("", null);
783
    }
784
785
    @Test(expected = IllegalArgumentException.class)
786
    public void testAddPreDestroyMethodConflicts() {
787
        StandardContext standardContext = new StandardContext();
788
        standardContext.addPreDestroyMethod("a", "a");
789
        standardContext.addPreDestroyMethod("a", "b");
790
    }
757
}
791
}
(-)test/org/apache/catalina/deploy/TestWebXml.java (+88 lines)
Lines 18-23 Link Here
18
package org.apache.catalina.deploy;
18
package org.apache.catalina.deploy;
19
19
20
import static org.junit.Assert.assertEquals;
20
import static org.junit.Assert.assertEquals;
21
import static org.junit.Assert.assertFalse;
22
23
import java.util.HashSet;
24
import java.util.Map;
25
import java.util.Set;
21
26
22
import org.junit.Test;
27
import org.junit.Test;
23
28
Lines 133-136 Link Here
133
        assertEquals(0, webxml.getMinorVersion());
138
        assertEquals(0, webxml.getMinorVersion());
134
        assertEquals("3.0", webxml.getVersion());
139
        assertEquals("3.0", webxml.getVersion());
135
    }
140
    }
141
142
    @Test
143
    public void testLifecycleMethodsWebXml() {
144
        WebXml webxml = new WebXml();
145
        webxml.addPostConstructMethods("a", "a");
146
        webxml.addPreDestroyMethods("b", "b");
147
148
        WebXml fragment = new WebXml();
149
        fragment.addPostConstructMethods("c", "c");
150
        fragment.addPreDestroyMethods("d", "d");
151
152
        Set<WebXml> fragments = new HashSet<WebXml>();
153
        fragments.add(fragment);
154
155
        webxml.merge(fragments);
156
157
        Map<String, String> postConstructMethods = webxml.getPostConstructMethods();
158
        Map<String, String> preDestroyMethods = webxml.getPreDestroyMethods();
159
        assertEquals(1, postConstructMethods.size());
160
        assertEquals(1, preDestroyMethods.size());
161
162
        assertEquals("a", postConstructMethods.get("a"));
163
        assertEquals("b", preDestroyMethods.get("b"));
164
    }
165
166
    @Test
167
    public void testLifecycleMethodsWebFragments() {
168
        WebXml webxml = new WebXml();
169
170
        WebXml fragment1 = new WebXml();
171
        fragment1.addPostConstructMethods("a", "a");
172
        fragment1.addPreDestroyMethods("b", "b");
173
174
        WebXml fragment2 = new WebXml();
175
        fragment2.addPostConstructMethods("c", "c");
176
        fragment2.addPreDestroyMethods("d", "d");
177
178
        Set<WebXml> fragments = new HashSet<WebXml>();
179
        fragments.add(fragment1);
180
        fragments.add(fragment2);
181
182
        webxml.merge(fragments);
183
184
        Map<String, String> postConstructMethods = webxml.getPostConstructMethods();
185
        Map<String, String> preDestroyMethods = webxml.getPreDestroyMethods();
186
        assertEquals(2, postConstructMethods.size());
187
        assertEquals(2, preDestroyMethods.size());
188
189
        assertEquals("a", postConstructMethods.get("a"));
190
        assertEquals("c", postConstructMethods.get("c"));
191
        assertEquals("b", preDestroyMethods.get("b"));
192
        assertEquals("d", preDestroyMethods.get("d"));
193
    }
194
195
    @Test
196
    public void testLifecycleMethodsWebFragmentsWithConflicts() {
197
        WebXml webxml = new WebXml();
198
199
        WebXml fragment1 = new WebXml();
200
        fragment1.addPostConstructMethods("a", "a");
201
        fragment1.addPreDestroyMethods("b", "a");
202
203
        WebXml fragment2 = new WebXml();
204
        fragment2.addPostConstructMethods("a", "b");
205
206
        Set<WebXml> fragments = new HashSet<WebXml>();
207
        fragments.add(fragment1);
208
        fragments.add(fragment2);
209
210
        assertFalse(webxml.merge(fragments));
211
212
        assertEquals(0, webxml.getPostConstructMethods().size());
213
214
        WebXml fragment3 = new WebXml();
215
        fragment3.addPreDestroyMethods("b", "b");
216
217
        fragments.remove(fragment2);
218
        fragments.add(fragment3);
219
220
        assertFalse(webxml.merge(fragments));
221
222
        assertEquals(0, webxml.getPreDestroyMethods().size());
223
    }
136
}
224
}
(-)test/org/apache/catalina/startup/TestWebRuleSet.java (+7 lines)
Lines 115-120 Link Here
115
        parse(new WebXml(), "web-1ordering.xml", false, true);
115
        parse(new WebXml(), "web-1ordering.xml", false, true);
116
}
116
}
117
117
118
    @Test
119
    public void testLifecycleMethodsDefinitions() throws Exception {
120
        // post-construct and pre-destroy
121
        parse(new WebXml(), "web-1lifecyclecallback.xml", false, true);
122
        // conflicting post-construct definitions
123
        parse(new WebXml(), "web-2lifecyclecallback.xml", false, false);
124
    }
118
125
119
    private synchronized void parse(WebXml webXml, String target,
126
    private synchronized void parse(WebXml webXml, String target,
120
            boolean fragment, boolean expected) throws FileNotFoundException {
127
            boolean fragment, boolean expected) throws FileNotFoundException {
(-)test/org/apache/catalina/startup/TestContextConfig.java (+20 lines)
Lines 32-37 Link Here
32
import org.junit.Assert;
32
import org.junit.Assert;
33
import org.junit.Test;
33
import org.junit.Test;
34
34
35
import org.apache.catalina.Context;
35
import org.apache.catalina.core.StandardContext;
36
import org.apache.catalina.core.StandardContext;
36
import org.apache.tomcat.util.buf.ByteChunk;
37
import org.apache.tomcat.util.buf.ByteChunk;
37
38
Lines 106-111 Link Here
106
                null, HttpServletResponse.SC_NOT_FOUND);
107
                null, HttpServletResponse.SC_NOT_FOUND);
107
    }
108
    }
108
109
110
    @Test
111
    public void testBug54379() throws Exception {
112
        Tomcat tomcat = getTomcatInstance();
113
114
        File appDir = new File("test/webapp-3.0-fragments");
115
        String oldValue = System.setProperty("catalina.useNaming", "true");
116
        Context context = tomcat.addWebapp(null, "/test", appDir.getAbsolutePath());
117
118
        Tomcat.addServlet(context, "TestServlet", 
119
            "org.apache.catalina.startup.ServletWithLifeCycleMethods");
120
        context.addServletMapping("/testServlet", "TestServlet");
121
122
        tomcat.start();
123
124
        assertPageContains("/test/testServlet", "postConstruct1()");
125
126
        System.setProperty("catalina.useNaming", oldValue);
127
    }
128
109
    private static class CustomDefaultServletSCI
129
    private static class CustomDefaultServletSCI
110
            implements ServletContainerInitializer {
130
            implements ServletContainerInitializer {
111
131
(-)test/webapp-3.0-fragments/WEB-INF/web.xml (+8 lines)
Lines 49-52 Link Here
49
    <jsp-file>/bug5nnnn/bug51396.jsp</jsp-file>
49
    <jsp-file>/bug5nnnn/bug51396.jsp</jsp-file>
50
  </servlet>
50
  </servlet>
51
51
52
  <post-construct>
53
  	<lifecycle-callback-class>org.apache.catalina.startup.ServletWithLifeCycleMethods</lifecycle-callback-class>
54
  	<lifecycle-callback-method>postConstruct1</lifecycle-callback-method>
55
  </post-construct>
56
  <pre-destroy>
57
  	<lifecycle-callback-class>org.apache.catalina.startup.ServletWithLifeCycleMethods</lifecycle-callback-class>
58
  	<lifecycle-callback-method>preDestroy1</lifecycle-callback-method>
59
  </pre-destroy>
52
</web-app>
60
</web-app>
(-)test/org/apache/catalina/startup/ServletWithLifeCycleMethods.java (+60 lines)
Line 0 Link Here
1
/*
2
 * Licensed to the Apache Software Foundation (ASF) under one or more
3
 * contributor license agreements.  See the NOTICE file distributed with
4
 * this work for additional information regarding copyright ownership.
5
 * The ASF licenses this file to You under the Apache License, Version 2.0
6
 * (the "License"); you may not use this file except in compliance with
7
 * the License.  You may obtain a copy of the License at
8
 *
9
 *      http://www.apache.org/licenses/LICENSE-2.0
10
 *
11
 * Unless required by applicable law or agreed to in writing, software
12
 * distributed under the License is distributed on an "AS IS" BASIS,
13
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14
 * See the License for the specific language governing permissions and
15
 * limitations under the License.
16
 */
17
18
package org.apache.catalina.startup;
19
20
import java.io.IOException;
21
22
import javax.annotation.PostConstruct;
23
import javax.annotation.PreDestroy;
24
import javax.servlet.ServletException;
25
import javax.servlet.http.HttpServlet;
26
import javax.servlet.http.HttpServletRequest;
27
import javax.servlet.http.HttpServletResponse;
28
29
public class ServletWithLifeCycleMethods extends HttpServlet {
30
31
    private static final long serialVersionUID = 1L;
32
33
    private String result;
34
35
    @Override
36
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) 
37
        throws ServletException, IOException {
38
        resp.setContentType("text/plain");
39
        resp.getWriter().print(this.result);
40
    }
41
42
    @PostConstruct
43
    protected void postConstruct() {
44
        this.result = "postConstruct()";
45
    }
46
47
    @PreDestroy
48
    protected void preDestroy() {
49
        this.result = "preDestroy()";
50
    }
51
52
    protected void postConstruct1() {
53
        this.result = "postConstruct1()";
54
    }
55
56
    protected void preDestroy1() {
57
        this.result = "preDestroy1()";
58
    }
59
}
60
native
(-)test/org/apache/catalina/startup/web-1lifecyclecallback.xml (+33 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="ISO-8859-1"?>
2
<!--
3
  Licensed to the Apache Software Foundation (ASF) under one or more
4
  contributor license agreements.  See the NOTICE file distributed with
5
  this work for additional information regarding copyright ownership.
6
  The ASF licenses this file to You under the Apache License, Version 2.0
7
  (the "License"); you may not use this file except in compliance with
8
  the License.  You may obtain a copy of the License at
9
10
      http://www.apache.org/licenses/LICENSE-2.0
11
12
  Unless required by applicable law or agreed to in writing, software
13
  distributed under the License is distributed on an "AS IS" BASIS,
14
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
  See the License for the specific language governing permissions and
16
  limitations under the License.
17
-->
18
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
19
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
21
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
22
  version="3.0"
23
  metadata-complete="true">
24
  <post-construct>
25
  	<lifecycle-callback-class>test.TestServlet</lifecycle-callback-class>
26
  	<lifecycle-callback-method>postConstruct</lifecycle-callback-method>
27
  </post-construct>
28
  <pre-destroy>
29
  	<lifecycle-callback-class>test.TestServlet</lifecycle-callback-class>
30
  	<lifecycle-callback-method>preDestroy</lifecycle-callback-method>
31
  </pre-destroy>
32
</web-app>
33
native
(-)test/org/apache/catalina/startup/web-2lifecyclecallback.xml (+33 lines)
Line 0 Link Here
1
<?xml version="1.0" encoding="ISO-8859-1"?>
2
<!--
3
  Licensed to the Apache Software Foundation (ASF) under one or more
4
  contributor license agreements.  See the NOTICE file distributed with
5
  this work for additional information regarding copyright ownership.
6
  The ASF licenses this file to You under the Apache License, Version 2.0
7
  (the "License"); you may not use this file except in compliance with
8
  the License.  You may obtain a copy of the License at
9
10
      http://www.apache.org/licenses/LICENSE-2.0
11
12
  Unless required by applicable law or agreed to in writing, software
13
  distributed under the License is distributed on an "AS IS" BASIS,
14
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
  See the License for the specific language governing permissions and
16
  limitations under the License.
17
-->
18
<web-app xmlns="http://java.sun.com/xml/ns/javaee"
19
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
20
  xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
21
                      http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
22
  version="3.0"
23
  metadata-complete="true">
24
  <post-construct>
25
  	<lifecycle-callback-class>test.TestServlet</lifecycle-callback-class>
26
  	<lifecycle-callback-method>postConstruct1</lifecycle-callback-method>
27
  </post-construct>
28
  <post-construct>
29
  	<lifecycle-callback-class>test.TestServlet</lifecycle-callback-class>
30
  	<lifecycle-callback-method>postConstruct2</lifecycle-callback-method>
31
  </post-construct>
32
</web-app>
33
native

Return to bug 54379