Skip to content

Commit dfd36cb

Browse files
authored
Merge pull request #34008 from jimblye/TS021313188-missing-servlet-def-defined-in-webfragment
Fix WAR deployment failure when servlet class defined in web-fragment
2 parents 066115b + 39d3481 commit dfd36cb

File tree

6 files changed

+122
-14
lines changed

6 files changed

+122
-14
lines changed

dev/com.ibm.websphere.appserver.features/visibility/protected/com.ibm.websphere.appserver.containerServices-1.0.feature

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ IBM-Process-Types: server, \
2020
com.ibm.ws.javaee.version, \
2121
com.ibm.ws.serialization
2222
-jars=com.ibm.websphere.appserver.spi.containerServices; location:=dev/spi/ibm/
23-
-files=dev/spi/ibm/javadoc/com.ibm.websphere.appserver.spi.containerServices_4.1-javadoc.zip
23+
-files=dev/spi/ibm/javadoc/com.ibm.websphere.appserver.spi.containerServices_4.2-javadoc.zip
2424
kind=ga
2525
edition=core
2626
WLP-Activation-Type: parallel

dev/com.ibm.websphere.appserver.spi.containerServices/bnd.bnd

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#*******************************************************************************
2-
# Copyright (c) 2017, 2025 IBM Corporation and others.
2+
# Copyright (c) 2017, 2026 IBM Corporation and others.
33
# All rights reserved. This program and the accompanying materials
44
# are made available under the terms of the Eclipse Public License 2.0
55
# which accompanies this distribution, and is available at
@@ -11,7 +11,7 @@
1111
# IBM Corporation - initial API and implementation
1212
#*******************************************************************************
1313
-include= ~../cnf/resources/bnd/bundle.props
14-
bVersion: 4.1
14+
bVersion: 4.2
1515

1616
Bundle-Name: WebSphere Container Services SPI
1717
Bundle-Description: WebSphere Container Services SPI, version ${bVersion}

dev/com.ibm.ws.container.service/src/com/ibm/ws/container/service/config/ServletConfigurator.java

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2012, 2020 IBM Corporation and others.
2+
* Copyright (c) 2012, 2026 IBM Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License 2.0
55
* which accompanies this distribution, and is available at
@@ -105,8 +105,12 @@ public interface MergeComparator<T> {
105105

106106
public ConfigSource getConfigSource();
107107

108+
public void setConfigSource(ConfigSource source);
109+
108110
public String getLibraryURI();
109111

112+
public void setLibraryURI(String libraryURI);
113+
110114
boolean getMetadataCompleted();
111115

112116
public WebAnnotations getWebAnnotations();
Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011, 2015 IBM Corporation and others.
2+
* Copyright (c) 2011, 2026 IBM Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License 2.0
55
* which accompanies this distribution, and is available at
@@ -10,6 +10,6 @@
1010
* Contributors:
1111
* IBM Corporation - initial API and implementation
1212
*******************************************************************************/
13-
/** @version 2.1 */
14-
@org.osgi.annotation.versioning.Version("2.1")
13+
/** @version 2.2 */
14+
@org.osgi.annotation.versioning.Version("2.2")
1515
package com.ibm.ws.container.service.config;

dev/com.ibm.ws.webcontainer/src/com/ibm/ws/webcontainer/osgi/container/config/WebAppConfigurator.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011, 2021 IBM Corporation and others.
2+
* Copyright (c) 2011, 2026 IBM Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License 2.0
55
* which accompanies this distribution, and is available at
@@ -356,11 +356,19 @@ public ConfigSource getConfigSource() {
356356
return this.currentSource;
357357
}
358358

359+
public void setConfigSource(ConfigSource source) {
360+
this.currentSource = source;
361+
}
362+
359363
@Override
360364
public String getLibraryURI() {
361365
return this.currentLibraryURI;
362366
}
363367

368+
public void setLibraryURI(String libraryURI) {
369+
this.currentLibraryURI = libraryURI;
370+
}
371+
364372
@Override
365373
public boolean getMetadataCompleted() {
366374
return this.currentMetadataComplete;

dev/com.ibm.ws.webcontainer/src/com/ibm/ws/webcontainer/osgi/container/config/WebAppConfiguratorHelper.java

Lines changed: 102 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*******************************************************************************
2-
* Copyright (c) 2011, 2023 IBM Corporation and others.
2+
* Copyright (c) 2011, 2026 IBM Corporation and others.
33
* All rights reserved. This program and the accompanying materials
44
* are made available under the terms of the Eclipse Public License 2.0
55
* which accompanies this distribution, and is available at
@@ -16,6 +16,7 @@
1616
// 148426 10/06/14 bitonti Give extension default error page precedence in Servlet 3.0
1717
// PI67942 10/21/16 zaroman encode URI after dispatch
1818
// 11909 12/11/20 jimblye Allow context-root to be overridden in server.xml
19+
// 02/06/26. jimblye Defer servlet mappings until webfragment servlet defs processed
1920

2021
package com.ibm.ws.webcontainer.osgi.container.config;
2122

@@ -156,6 +157,24 @@
156157
* from web.xml, web-fragment.xml and annotations. Configure them into the WebAppConfiguration.
157158
*/
158159
public class WebAppConfiguratorHelper implements ServletConfiguratorHelper {
160+
161+
/**
162+
* Helper class to store servlet mappings for deferred processing.
163+
* This allows all servlet definitions to be collected before processing mappings.
164+
*/
165+
private static class DeferredServletMapping {
166+
final ServletMapping servletMapping;
167+
final ConfigSource source;
168+
final String libraryURI;
169+
170+
DeferredServletMapping(ServletMapping servletMapping, ConfigSource source, String libraryURI) {
171+
this.servletMapping = servletMapping;
172+
this.source = source;
173+
this.libraryURI = libraryURI;
174+
}
175+
}
176+
177+
private final List<DeferredServletMapping> deferredServletMappings = new ArrayList<>();
159178
private static final String CLASS_NAME = WebAppConfiguratorHelper.class.getSimpleName();
160179

161180
private static final TraceComponent tc = Tr.register(WebAppConfiguratorHelper.class, WebContainerConstants.TR_GROUP, WebContainerConstants.NLS_PROPS);
@@ -818,7 +837,9 @@ public void configureFromWebApp(WebApp webApp) throws UnableToAdaptException {
818837

819838
configureSessionConfig(webApp.getSessionConfig());
820839
configureServlets(webApp, webApp.getServlets());
821-
configureServletMappings(webApp.getServletMappings());
840+
841+
// Defer servlet mapping processing until all servlet definitions are collected
842+
storeDeferredServletMappings(webApp.getServletMappings());
822843
configureLocaleEncodingMap(webApp.getLocaleEncodingMappingList());
823844
configureListener(webApp.getListeners());
824845
configureEnvEntries(webApp.getEnvEntries());
@@ -856,7 +877,9 @@ public void configureFromWebFragment(WebFragmentInfo webFragmentItem) throws Una
856877
configureDistributableFromFragment(webFragment.isSetDistributable());
857878
configureSessionConfig(webFragment.getSessionConfig());
858879
configureServlets(webFragment, webFragment.getServlets());
859-
configureServletMappings(webFragment.getServletMappings());
880+
881+
// Defer servlet mapping processing until all servlet definitions are collected
882+
storeDeferredServletMappings(webFragment.getServletMappings());
860883
configureLocaleEncodingMap(webFragment.getLocaleEncodingMappingList());
861884
configureListener(webFragment.getListeners());
862885
configureEnvEntries(webFragment.getEnvEntries());
@@ -913,7 +936,11 @@ public void configureFromAnnotations(WebFragmentInfo webFragmentItem) throws Una
913936
@Override
914937
public void configureDefaults() throws UnableToAdaptException {
915938

916-
//I think we need to process specifiedClasses first to find what servlets were defined
939+
// Process all deferred servlet mappings now that all servlet definitions have been collected
940+
// from web.xml and all web-fragment.xml files
941+
processDeferredServletMappings();
942+
943+
//I think we need to process specifiedClasses first to find what servlets were defined
917944
//in case a filter is mapped to * (all servlets)
918945
configureSpecifiedClasses();
919946

@@ -2564,9 +2591,78 @@ private void configureServlets(DeploymentDescriptor webDD, List<Servlet> servlet
25642591
}
25652592
}
25662593

2567-
private void configureServletMappings(List<ServletMapping> servletMappings) throws UnableToAdaptException {
2594+
/**
2595+
* Process all deferred servlet mappings after all servlet definitions have been collected.
2596+
* The deferred mappings retain their original source context for proper merging rules and error messages.
2597+
*
2598+
* @throws UnableToAdaptException if servlet mapping processing fails
2599+
*/
2600+
private void processDeferredServletMappings() throws UnableToAdaptException {
2601+
2602+
// Save the current context
2603+
ConfigSource savedSource = configurator.getConfigSource();
2604+
String savedLibraryURI = configurator.getLibraryURI();
2605+
2606+
for (DeferredServletMapping deferred : deferredServletMappings) {
2607+
try {
2608+
// Retrieve the context from for this servlet mapping
2609+
// Needed for proper merging rules in processServletMappingConfig(...)
2610+
configurator.setConfigSource(deferred.source);
2611+
configurator.setLibraryURI(deferred.libraryURI);
2612+
processServletMappingConfig(deferred.servletMapping);
2613+
} finally {
2614+
// Restore the saved context
2615+
configurator.setConfigSource(savedSource);
2616+
configurator.setLibraryURI(savedLibraryURI);
2617+
}
2618+
}
2619+
deferredServletMappings.clear();
2620+
}
2621+
2622+
/**
2623+
* Store servlet mappings for deferred processing.
2624+
* This allows all servlet definitions to be collected from web.xml and web-fragment.xml
2625+
* files before processing the mappings, which is necessary when a servlet-mapping in
2626+
* web.xml references a servlet whose definition is provided in a web-fragment.xml.
2627+
*
2628+
* <p>We only defer mappings for servlets that don't have a definition yet.
2629+
* If the servlet is already defined (e.g., from an earlier source like annotations annotations or web.xml),
2630+
* we process the mapping immediately. This
2631+
* <ul>
2632+
* <li>allows processServletMappingConfig() to apply the correct merge rules based on ConfigSource
2633+
* (web.xml > web-fragment.xml > annotations)</li>
2634+
* <li>prevents duplicate mapping attempts that would occur if we deferred all mappings and then
2635+
* tried to add them again after annotations have already added them</li>
2636+
* <li>maintains the existing precedence behavior where earlier sources override later ones</li>
2637+
* </ul>
2638+
*
2639+
* @param servletMappings the list of servlet mappings to process or defer
2640+
* @throws UnableToAdaptException if servlet mapping processing fails
2641+
*/
2642+
private void storeDeferredServletMappings(List<ServletMapping> servletMappings) throws UnableToAdaptException {
2643+
if (servletMappings == null || servletMappings.isEmpty()) {
2644+
return;
2645+
}
2646+
2647+
ConfigSource currentSource = configurator.getConfigSource();
2648+
String currentLibraryURI = configurator.getLibraryURI();
2649+
25682650
for (ServletMapping servletMapping : servletMappings) {
2569-
processServletMappingConfig(servletMapping);
2651+
String servletName = servletMapping.getServletName();
2652+
Map<String, ConfigItem<ServletConfig>> servletMap = configurator.getConfigItemMap("servlet");
2653+
2654+
// Check if servlet definition exists
2655+
if (servletMap.get(servletName) == null) {
2656+
// Servlet not defined yet - defer mapping until all servlet definitions are collected
2657+
// This handles the case where web.xml has a servlet-mapping but the servlet definition
2658+
// is in a web-fragment.xml that hasn't been processed yet
2659+
deferredServletMappings.add(new DeferredServletMapping(servletMapping, currentSource, currentLibraryURI));
2660+
} else {
2661+
// Servlet already defined - process mapping immediately to maintain proper precedence
2662+
// This ensures processServletMappingConfig() can apply merge rules correctly and
2663+
// prevents duplicate mappings when annotations have already added mappings
2664+
processServletMappingConfig(servletMapping);
2665+
}
25702666
}
25712667
}
25722668

0 commit comments

Comments
 (0)