This is an automated email from the ASF dual-hosted git repository.

rmaucher pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tomcat.git

commit 46c7235e8dd3ae66fc74ed1367bfb37f6b03643c
Author: opencode <[email protected]>
AuthorDate: Fri Nov 6 14:55:21 2026 +0100

    Expand OWB module test coverage
    
    Add CDI 4.1 method invoker, event matching and context tests, plus coverage 
for both listener modes, startWithoutBeansXml, security valve configuration, 
lifecycle callbacks and BASIC authentication.
---
 modules/owb/pom.xml                                |   2 +-
 .../webbeans/web/tomcat/OpenWebBeansCdi41IT.java   | 145 ++----------------
 .../web/tomcat/OpenWebBeansSecurityIT.java         |  58 +++++++
 .../tomcat/OpenWebBeansTomcatIntegrationIT.java    | 148 ++++++++++++++++++
 .../apache/webbeans/web/tomcat/OwbTestServer.java  | 128 ++++++++++++++++
 .../apache/webbeans/web/tomcat/OwbTestSupport.java |  84 +++++++++++
 .../apache/webbeans/web/tomcat/WebAppBuilder.java  | 168 +++++++++++++++++++++
 .../src/test/webapp/{ => cdi41}/WEB-INF/beans.xml  |   0
 .../jakarta.enterprise.inject.spi.Extension        |  15 ++
 .../webbeans/web/tomcat/testapp/Cdi41Event.java}   |   5 +-
 .../web/tomcat/testapp/Cdi41EventObserver.java}    |   7 +-
 .../webbeans/web/tomcat/testapp/Cdi41Features.java |  15 ++
 .../webbeans/web/tomcat/testapp/Cdi41Servlet.java  |  12 +-
 .../webbeans/web/tomcat/testapp/Greeting.java      |   0
 .../web/tomcat/testapp/InvokerExtension.java       |  48 ++++++
 .../web/tomcat/testapp/InvokerTarget.java}         |   6 +-
 .../web/tomcat/testapp/MatchingTarget.java         |   0
 .../web/tomcat/testapp/RequestScopedBean.java      |   0
 .../apache/webbeans/web/tomcat/testapp/Traced.java |   0
 .../webbeans/web/tomcat/testapp/TracedService.java |   0
 .../web/tomcat/testapp/TracingInterceptor.java     |   6 +-
 .../test/webapp/{ => lifecycle}/WEB-INF/beans.xml  |   0
 .../tomcat/testapp/LifecycleContextListener.java}  |  21 +--
 .../web/tomcat/testapp/LifecycleFilter.java        |  45 ++++++
 .../web/tomcat/testapp/LifecycleGreeting.java}     |   4 +-
 .../web/tomcat/testapp/LifecycleServlet.java}      |  58 +++----
 .../test/webapp/{ => security}/WEB-INF/beans.xml   |   0
 .../owb/src/test/webapp/security/WEB-INF/web.xml   |  44 ++++++
 .../web/tomcat/testapp/WhoAmIServlet.java}         |  41 ++---
 29 files changed, 847 insertions(+), 213 deletions(-)

diff --git a/modules/owb/pom.xml b/modules/owb/pom.xml
index e27ec6e930..c15db4fffe 100644
--- a/modules/owb/pom.xml
+++ b/modules/owb/pom.xml
@@ -209,7 +209,7 @@
                         
<classpathDependencyExclude>jakarta.annotation:jakarta.annotation-api</classpathDependencyExclude>
                     </classpathDependencyExcludes>
                     <systemPropertyVariables>
-                        
<owb.test.webapp>${project.basedir}/src/test/webapp</owb.test.webapp>
+                        
<owb.test.webapp.root>${project.basedir}/src/test/webapp</owb.test.webapp.root>
                         
<owb.test.jar>${project.build.directory}/${project.build.finalName}.jar</owb.test.jar>
                     </systemPropertyVariables>
                 </configuration>
diff --git 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansCdi41IT.java
 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansCdi41IT.java
index 53638d9d88..fda921cab8 100644
--- 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansCdi41IT.java
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansCdi41IT.java
@@ -19,160 +19,45 @@ package org.apache.webbeans.web.tomcat;
 import static org.junit.jupiter.api.Assertions.assertEquals;
 import static org.junit.jupiter.api.Assertions.assertFalse;
 import static org.junit.jupiter.api.Assertions.assertNotEquals;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
 
-import java.io.File;
-import java.io.IOException;
 import java.net.URI;
 import java.net.http.HttpClient;
-import java.net.http.HttpRequest;
-import java.net.http.HttpResponse;
-import java.nio.charset.StandardCharsets;
-import java.nio.file.Files;
 import java.nio.file.Path;
-import java.nio.file.StandardCopyOption;
-import java.time.Duration;
-import java.util.List;
-import java.util.Locale;
-import java.util.stream.Stream;
 
-import javax.tools.Diagnostic;
-import javax.tools.DiagnosticCollector;
-import javax.tools.JavaCompiler;
-import javax.tools.JavaFileObject;
-import javax.tools.StandardJavaFileManager;
-import javax.tools.ToolProvider;
-
-import org.apache.catalina.startup.Tomcat;
 import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.web.tomcat.OwbTestServer.ListenerMode;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
 class OpenWebBeansCdi41IT {
 
-    private static final String CONTEXT_PATH = "/owb-cdi41";
-
     @TempDir
     Path tempDir;
 
     @Test
     void cdi41Webapp() throws Exception {
-        Path webappSource = Path.of(System.getProperty("owb.test.webapp"));
-        Path shadedJar = Path.of(System.getProperty("owb.test.jar"));
-
-        assertEquals(shadedJar.toRealPath(),
-                
Path.of(OpenWebBeansListener.class.getProtectionDomain().getCodeSource().getLocation().toURI())
-                        .toRealPath(),
-                "The test must run against the shaded module JAR");
-        assertEquals(shadedJar.toRealPath(),
-                
Path.of(WebBeansContext.class.getProtectionDomain().getCodeSource().getLocation().toURI())
-                        .toRealPath(),
-                "The test must use the OpenWebBeans classes from the shaded 
module JAR");
-
-        Path webapp = tempDir.resolve("webapp");
-        copyTree(webappSource.resolve("WEB-INF"), webapp.resolve("WEB-INF"));
-        Path classes = webapp.resolve("WEB-INF/classes");
-        Files.createDirectories(classes);
-        compile(webappSource.resolve("src"), classes, shadedJar);
-
-        Path base = tempDir.resolve("tomcat");
-        Tomcat tomcat = new Tomcat();
-        tomcat.setBaseDir(base.toString());
-        tomcat.setPort(0);
-        tomcat.getConnector();
-        tomcat.getServer().setPort(-1);
+        Path shadedJar = OwbTestSupport.shadedJar();
+        OwbTestSupport.assertShadedJar(shadedJar, OpenWebBeansListener.class, 
WebBeansContext.class);
 
-        OpenWebBeansListener listener = new OpenWebBeansListener();
-        listener.setStartWithoutBeansXml(false);
-        tomcat.getServer().addLifecycleListener(listener);
+        Path webapp = new WebAppBuilder(tempDir.resolve("webapp"), 
"cdi41").build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, false, true)) {
+            HttpClient client = OwbTestSupport.newClient();
+            URI baseUri = server.baseUri();
 
-        // The test webapp does not use JSPs and tomcat-jasper is not required.
-        tomcat.setAddDefaultWebXmlToWebapp(false);
-        tomcat.addWebapp(CONTEXT_PATH, webapp.toString());
-        tomcat.start();
-        try {
-            int port = tomcat.getConnector().getLocalPort();
-            assertTrue(port > 0, "Tomcat did not start an HTTP connector");
+            assertEquals("hello-owb-4.1.1", OwbTestSupport.get(client, 
baseUri.resolve("hello")));
 
-            HttpClient client = 
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
-            URI baseUri = URI.create("http://127.0.0.1:"; + port + CONTEXT_PATH 
+ "/");
-
-            assertEquals("hello-owb-4.1.1", get(client, 
baseUri.resolve("hello")));
-
-            String firstRequest = get(client, baseUri.resolve("request"));
-            String secondRequest = get(client, baseUri.resolve("request"));
+            String firstRequest = OwbTestSupport.get(client, 
baseUri.resolve("request"));
+            String secondRequest = OwbTestSupport.get(client, 
baseUri.resolve("request"));
             assertNotEquals(firstRequest, secondRequest,
                     "The request scoped bean was not recreated for each 
request");
             assertFalse(firstRequest.isEmpty());
             assertFalse(secondRequest.isEmpty());
 
-            assertEquals("true", get(client, baseUri.resolve("matching")));
-            assertEquals("bound:ok", get(client, 
baseUri.resolve("interceptor")));
-        } finally {
-            try {
-                tomcat.stop();
-            } finally {
-                tomcat.destroy();
-            }
-        }
-    }
-
-    private static String get(HttpClient client, URI uri) throws IOException, 
InterruptedException {
-        HttpRequest request = 
HttpRequest.newBuilder(uri).timeout(Duration.ofSeconds(10)).GET().build();
-        HttpResponse<String> response = client.send(request, 
HttpResponse.BodyHandlers.ofString());
-        assertEquals(200, response.statusCode(), "Unexpected response for " + 
uri + ": " + response.body());
-        return response.body().strip();
-    }
-
-    private static void compile(Path sourceDir, Path classesDir, Path 
shadedJar) throws IOException {
-        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
-        assertNotNull(compiler, "A JDK is required to run this test");
-
-        List<Path> sources;
-        try (Stream<Path> stream = Files.walk(sourceDir)) {
-            sources = stream.filter(Files::isRegularFile)
-                    .filter(path -> path.toString().endsWith(".java"))
-                    .toList();
-        }
-
-        String compileClasspath = 
System.getProperty("surefire.test.class.path");
-        if (compileClasspath == null || compileClasspath.isBlank()) {
-            compileClasspath = System.getProperty("java.class.path");
-        }
-        compileClasspath = compileClasspath + File.pathSeparator + shadedJar;
-
-        DiagnosticCollector<JavaFileObject> diagnostics = new 
DiagnosticCollector<>();
-        try (StandardJavaFileManager fileManager = 
compiler.getStandardFileManager(diagnostics, Locale.ROOT,
-                StandardCharsets.UTF_8)) {
-            Iterable<? extends JavaFileObject> units = 
fileManager.getJavaFileObjectsFromPaths(sources);
-            List<String> options = List.of(
-                    "--release", "21",
-                    "-proc:none",
-                    "-classpath", compileClasspath,
-                    "-d", classesDir.toString());
-            boolean success = compiler.getTask(null, fileManager, diagnostics, 
options, null, units).call();
-            if (!success) {
-                StringBuilder message = new StringBuilder("Failed to compile 
the test webapp:");
-                for (Diagnostic<? extends JavaFileObject> diagnostic : 
diagnostics.getDiagnostics()) {
-                    message.append(System.lineSeparator()).append(diagnostic);
-                }
-                throw new IllegalStateException(message.toString());
-            }
-        }
-    }
-
-    private static void copyTree(Path source, Path target) throws IOException {
-        try (Stream<Path> stream = Files.walk(source)) {
-            for (Path path : stream.toList()) {
-                Path destination = target.resolve(source.relativize(path));
-                if (Files.isDirectory(path)) {
-                    Files.createDirectories(destination);
-                } else {
-                    Files.createDirectories(destination.getParent());
-                    Files.copy(path, destination, 
StandardCopyOption.REPLACE_EXISTING);
-                }
-            }
+            assertEquals("true", OwbTestSupport.get(client, 
baseUri.resolve("matching")));
+            assertEquals("true", OwbTestSupport.get(client, 
baseUri.resolve("matching-event")));
+            assertEquals("true", OwbTestSupport.get(client, 
baseUri.resolve("contexts")));
+            assertEquals("bound:ok", OwbTestSupport.get(client, 
baseUri.resolve("interceptor")));
+            assertEquals("invoked:hello-owb-4.1.1", OwbTestSupport.get(client, 
baseUri.resolve("invoker")));
         }
     }
 }
diff --git 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityIT.java
 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityIT.java
new file mode 100644
index 0000000000..a19d5fad5b
--- /dev/null
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityIT.java
@@ -0,0 +1,58 @@
+/*
+ * 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.webbeans.web.tomcat;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpResponse;
+import java.nio.file.Path;
+
+import org.apache.webbeans.web.tomcat.OwbTestServer.ListenerMode;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class OpenWebBeansSecurityIT {
+
+    @TempDir
+    Path tempDir;
+
+    @Test
+    void basicAuthentication() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path webapp = new WebAppBuilder(tempDir.resolve("security"), 
"security").build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, false, true, "/owb-security",
+                tomcat -> {
+                    tomcat.addUser("testuser", "testpass");
+                    tomcat.addRole("testuser", "test");
+                })) {
+            HttpClient client = OwbTestSupport.newClient();
+            URI uri = server.baseUri().resolve("whoami");
+
+            HttpResponse<String> unauthenticated = 
OwbTestSupport.getResponse(client, uri);
+            assertEquals(401, unauthenticated.statusCode(), 
unauthenticated.body());
+            
assertTrue(unauthenticated.headers().firstValue("WWW-Authenticate").orElse("").startsWith("Basic"));
+
+            HttpResponse<String> authenticated = 
OwbTestSupport.getResponse(client, uri,
+                    OwbTestSupport.basicAuth("testuser", "testpass"));
+            assertEquals(200, authenticated.statusCode(), 
authenticated.body());
+            assertEquals("testuser|testuser", authenticated.body().strip());
+        }
+    }
+}
diff --git 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansTomcatIntegrationIT.java
 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansTomcatIntegrationIT.java
new file mode 100644
index 0000000000..632297d09e
--- /dev/null
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansTomcatIntegrationIT.java
@@ -0,0 +1,148 @@
+/*
+ * 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.webbeans.web.tomcat;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+
+import java.net.http.HttpClient;
+import java.net.http.HttpResponse;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.Arrays;
+
+import org.apache.webbeans.servlet.WebBeansConfigurationListener;
+import org.apache.webbeans.web.tomcat.OwbTestServer.ListenerMode;
+import org.junit.jupiter.api.Test;
+import org.junit.jupiter.api.io.TempDir;
+
+class OpenWebBeansTomcatIntegrationIT {
+
+    @TempDir
+    Path tempDir;
+
+    @Test
+    void serverListener() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path webapp = new WebAppBuilder(tempDir.resolve("server"), 
"cdi41").build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, false, true)) {
+            assertHello(server);
+            assertSecurityValve(server, true);
+            assertWebBeansListener(server, true);
+        }
+    }
+
+    @Test
+    void contextListener() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path webapp = new WebAppBuilder(tempDir.resolve("context"), 
"cdi41").build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.CONTEXT, false, true)) {
+            assertHello(server);
+            assertSecurityValve(server, true);
+            assertWebBeansListener(server, true);
+        }
+    }
+
+    @Test
+    void startWithoutBeansXml() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path webapp = new WebAppBuilder(tempDir.resolve("no-beans"), "cdi41")
+                .includeBeansXml(false)
+                .build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, true, true)) {
+            assertHello(server);
+            assertWebBeansListener(server, true);
+        }
+    }
+
+    @Test
+    void doNotStartWithoutBeansXml() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path webapp = new WebAppBuilder(tempDir.resolve("no-beans-disabled"), 
"cdi41")
+                .includeBeansXml(false)
+                .build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, false, true)) {
+            assertWebBeansListener(server, false);
+        }
+    }
+
+    @Test
+    void beansXmlInClasses() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path webapp = new WebAppBuilder(tempDir.resolve("beans-in-classes"), 
"cdi41")
+                .includeBeansXml(false)
+                .beansXmlInClasses()
+                .build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, false, true)) {
+            assertHello(server);
+        }
+    }
+
+    @Test
+    void securityValveDisabled() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path webapp = new WebAppBuilder(tempDir.resolve("no-security-valve"), 
"cdi41").build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, false, false)) {
+            assertHello(server);
+            assertSecurityValve(server, false);
+        }
+    }
+
+    @Test
+    void filterListenerAndServletInjection() throws Exception {
+        Path markerDirectory = tempDir.resolve("markers");
+        Files.createDirectories(markerDirectory);
+        String previousMarkerDirectory = 
System.getProperty("owb.test.marker.dir");
+        System.setProperty("owb.test.marker.dir", markerDirectory.toString());
+        try {
+            Path shadedJar = OwbTestSupport.shadedJar();
+            Path webapp = new WebAppBuilder(tempDir.resolve("lifecycle"), 
"lifecycle").build(shadedJar);
+            try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, false, true)) {
+                HttpClient client = OwbTestSupport.newClient();
+                HttpResponse<String> response = 
OwbTestSupport.getResponse(client, server.baseUri().resolve("lifecycle"));
+                assertEquals(200, response.statusCode(), response.body());
+                
assertEquals("lifecycle-owb-4.1.1|lifecycle-owb-4.1.1|lifecycle-owb-4.1.1",
+                        response.body().strip());
+                assertEquals("lifecycle-owb-4.1.1", 
response.headers().firstValue("X-Owb-Greeting").orElse(null));
+                OwbTestSupport.assertMarker(markerDirectory, 
"postConstruct.marker");
+            }
+            OwbTestSupport.assertMarker(markerDirectory, "preDestroy.marker");
+        } finally {
+            if (previousMarkerDirectory == null) {
+                System.clearProperty("owb.test.marker.dir");
+            } else {
+                System.setProperty("owb.test.marker.dir", 
previousMarkerDirectory);
+            }
+        }
+    }
+
+    private static void assertHello(OwbTestServer server) throws Exception {
+        HttpClient client = OwbTestSupport.newClient();
+        assertEquals("hello-owb-4.1.1", OwbTestSupport.get(client, 
server.baseUri().resolve("hello")));
+    }
+
+    private static void assertSecurityValve(OwbTestServer server, boolean 
expected) {
+        boolean found = 
Arrays.stream(server.context().getPipeline().getValves())
+                .anyMatch(valve -> valve instanceof OpenWebBeansSecurityValve);
+        assertEquals(expected, found, "Unexpected OpenWebBeansSecurityValve 
presence");
+    }
+
+    private static void assertWebBeansListener(OwbTestServer server, boolean 
expected) {
+        boolean found = 
Arrays.asList(server.context().findApplicationListeners())
+                .contains(WebBeansConfigurationListener.class.getName());
+        assertEquals(expected, found, "Unexpected 
WebBeansConfigurationListener presence");
+    }
+}
diff --git 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestServer.java 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestServer.java
new file mode 100644
index 0000000000..47ac599bae
--- /dev/null
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestServer.java
@@ -0,0 +1,128 @@
+/*
+ * 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.webbeans.web.tomcat;
+
+import java.net.URI;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.util.function.Consumer;
+
+import org.apache.catalina.Context;
+import org.apache.catalina.startup.Tomcat;
+
+/**
+ * Embedded Tomcat server configured for OWB integration tests.
+ */
+final class OwbTestServer implements AutoCloseable {
+
+    enum ListenerMode {
+        SERVER,
+        CONTEXT
+    }
+
+    private final Tomcat tomcat;
+    private final Context context;
+    private final URI baseUri;
+
+    private OwbTestServer(Tomcat tomcat, Context context, URI baseUri) {
+        this.tomcat = tomcat;
+        this.context = context;
+        this.baseUri = baseUri;
+    }
+
+    static OwbTestServer start(Path webapp, ListenerMode mode, boolean 
startWithoutBeansXml,
+            boolean addSecurityValve, String contextPath, Consumer<Tomcat> 
configurer) throws Exception {
+        Path base = Files.createTempDirectory(webapp.getParent(), "tomcat-");
+
+        Tomcat tomcat = new Tomcat();
+        tomcat.setBaseDir(base.toString());
+        tomcat.setPort(0);
+        tomcat.getConnector();
+        tomcat.getServer().setPort(-1);
+        tomcat.setAddDefaultWebXmlToWebapp(false);
+        // Enable naming so that Tomcat's DefaultInstanceManager invokes
+        // @PostConstruct and @PreDestroy callbacks.
+        tomcat.enableNaming();
+
+        if (configurer != null) {
+            configurer.accept(tomcat);
+        }
+
+        Context context = tomcat.addWebapp(contextPath, webapp.toString());
+        if (mode == ListenerMode.SERVER) {
+            OpenWebBeansListener listener = new OpenWebBeansListener();
+            listener.setStartWithoutBeansXml(startWithoutBeansXml);
+            listener.setAddSecurityValve(addSecurityValve);
+            tomcat.getServer().addLifecycleListener(listener);
+        } else {
+            OpenWebBeansContextLifecycleListener listener = new 
OpenWebBeansContextLifecycleListener();
+            listener.setStartWithoutBeansXml(startWithoutBeansXml);
+            listener.setAddSecurityValve(addSecurityValve);
+            context.addLifecycleListener(listener);
+        }
+
+        try {
+            tomcat.start();
+        } catch (Exception e) {
+            closeQuietly(tomcat);
+            throw e;
+        }
+
+        int port = tomcat.getConnector().getLocalPort();
+        URI baseUri = URI.create("http://127.0.0.1:"; + port + contextPath + 
"/");
+        return new OwbTestServer(tomcat, context, baseUri);
+    }
+
+    static OwbTestServer start(Path webapp, ListenerMode mode, boolean 
startWithoutBeansXml,
+            boolean addSecurityValve) throws Exception {
+        return start(webapp, mode, startWithoutBeansXml, addSecurityValve, 
"/owb-test", null);
+    }
+
+    Context context() {
+        return context;
+    }
+
+    URI baseUri() {
+        return baseUri;
+    }
+
+    Tomcat tomcat() {
+        return tomcat;
+    }
+
+    @Override
+    public void close() throws Exception {
+        try {
+            tomcat.stop();
+        } finally {
+            tomcat.destroy();
+        }
+    }
+
+    private static void closeQuietly(Tomcat tomcat) {
+        try {
+            tomcat.stop();
+        } catch (Exception ignored) {
+            // Ignore cleanup failures while reporting the startup failure.
+        }
+        try {
+            tomcat.destroy();
+        } catch (Exception ignored) {
+            // Ignore cleanup failures while reporting the startup failure.
+        }
+    }
+}
diff --git 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestSupport.java 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestSupport.java
new file mode 100644
index 0000000000..7c4f4c38e7
--- /dev/null
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestSupport.java
@@ -0,0 +1,84 @@
+/*
+ * 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.webbeans.web.tomcat;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.net.URI;
+import java.net.http.HttpClient;
+import java.net.http.HttpRequest;
+import java.net.http.HttpResponse;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.time.Duration;
+import java.util.Base64;
+import java.util.Objects;
+
+/**
+ * Shared helpers for the OWB integration tests.
+ */
+final class OwbTestSupport {
+
+    private OwbTestSupport() {
+    }
+
+    static Path shadedJar() {
+        return 
Path.of(Objects.requireNonNull(System.getProperty("owb.test.jar"),
+                "owb.test.jar system property is not set"));
+    }
+
+    static void assertShadedJar(Path shadedJar, Class<?>... classes) throws 
Exception {
+        Path expected = shadedJar.toRealPath();
+        for (Class<?> clazz : classes) {
+            Path actual = 
Path.of(clazz.getProtectionDomain().getCodeSource().getLocation().toURI()).toRealPath();
+            assertEquals(expected, actual, clazz.getName() + " must be loaded 
from the shaded module JAR");
+        }
+    }
+
+    static HttpClient newClient() {
+        return 
HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build();
+    }
+
+    static HttpResponse<String> getResponse(HttpClient client, URI uri) throws 
Exception {
+        return getResponse(client, uri, null);
+    }
+
+    static HttpResponse<String> getResponse(HttpClient client, URI uri, String 
authorization) throws Exception {
+        HttpRequest.Builder builder = 
HttpRequest.newBuilder(uri).timeout(Duration.ofSeconds(10)).GET();
+        if (authorization != null) {
+            builder.header("Authorization", authorization);
+        }
+        return client.send(builder.build(), 
HttpResponse.BodyHandlers.ofString());
+    }
+
+    static String get(HttpClient client, URI uri) throws Exception {
+        HttpResponse<String> response = getResponse(client, uri);
+        assertEquals(200, response.statusCode(), "Unexpected response for " + 
uri + ": " + response.body());
+        return response.body().strip();
+    }
+
+    static String basicAuth(String user, String password) {
+        String credentials = user + ":" + password;
+        return "Basic " + 
Base64.getEncoder().encodeToString(credentials.getBytes(StandardCharsets.UTF_8));
+    }
+
+    static void assertMarker(Path markerDirectory, String name) {
+        assertTrue(Files.exists(markerDirectory.resolve(name)), "Missing 
lifecycle marker: " + name);
+    }
+}
diff --git 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/WebAppBuilder.java 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/WebAppBuilder.java
new file mode 100644
index 0000000000..309e9b0151
--- /dev/null
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/WebAppBuilder.java
@@ -0,0 +1,168 @@
+/*
+ * 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.webbeans.web.tomcat;
+
+import java.io.File;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.StandardCopyOption;
+import java.util.ArrayList;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Locale;
+import java.util.Objects;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import javax.tools.Diagnostic;
+import javax.tools.DiagnosticCollector;
+import javax.tools.JavaCompiler;
+import javax.tools.JavaFileObject;
+import javax.tools.StandardJavaFileManager;
+import javax.tools.ToolProvider;
+
+/**
+ * Builds a temporary exploded webapp from a test template.
+ */
+final class WebAppBuilder {
+
+    private final Path destination;
+    private final Path template;
+    private final Set<Path> sourceRoots = new LinkedHashSet<>();
+    private boolean includeBeansXml = true;
+    private boolean includeWebXml = true;
+    private boolean beansXmlInClasses;
+
+    WebAppBuilder(Path destination, String templateName) {
+        this.destination = destination;
+        this.template = 
Path.of(Objects.requireNonNull(System.getProperty("owb.test.webapp.root"),
+                "owb.test.webapp.root system property is not 
set")).resolve(templateName);
+        sourceRoots.add(template.resolve("src"));
+    }
+
+    WebAppBuilder includeBeansXml(boolean include) {
+        includeBeansXml = include;
+        return this;
+    }
+
+    WebAppBuilder includeWebXml(boolean include) {
+        includeWebXml = include;
+        return this;
+    }
+
+    WebAppBuilder beansXmlInClasses() {
+        beansXmlInClasses = true;
+        return this;
+    }
+
+    WebAppBuilder sources(Path... roots) {
+        for (Path root : roots) {
+            sourceRoots.add(root);
+        }
+        return this;
+    }
+
+    Path build(Path shadedJar) throws IOException {
+        Path webInf = template.resolve("WEB-INF");
+        if (Files.isDirectory(webInf)) {
+            copyTree(webInf, destination.resolve("WEB-INF"));
+        }
+
+        Path classes = destination.resolve("WEB-INF/classes");
+        Files.createDirectories(classes);
+        compile(classes, shadedJar);
+
+        if (beansXmlInClasses) {
+            Path source = template.resolve("WEB-INF/beans.xml");
+            Path target = classes.resolve("META-INF/beans.xml");
+            Files.createDirectories(target.getParent());
+            Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
+        }
+
+        return destination;
+    }
+
+    private void copyTree(Path source, Path target) throws IOException {
+        try (Stream<Path> stream = Files.walk(source)) {
+            for (Path path : stream.toList()) {
+                Path relative = source.relativize(path);
+                if (!includeBeansXml && relative.equals(Path.of("beans.xml"))) 
{
+                    continue;
+                }
+                if (!includeWebXml && relative.equals(Path.of("web.xml"))) {
+                    continue;
+                }
+                Path destinationPath = target.resolve(relative);
+                if (Files.isDirectory(path)) {
+                    Files.createDirectories(destinationPath);
+                } else {
+                    Files.createDirectories(destinationPath.getParent());
+                    Files.copy(path, destinationPath, 
StandardCopyOption.REPLACE_EXISTING);
+                }
+            }
+        }
+    }
+
+    private void compile(Path classes, Path shadedJar) throws IOException {
+        List<Path> sources = new ArrayList<>();
+        for (Path sourceRoot : sourceRoots) {
+            if (!Files.isDirectory(sourceRoot)) {
+                continue;
+            }
+            try (Stream<Path> stream = Files.walk(sourceRoot)) {
+                stream.filter(Files::isRegularFile)
+                        .filter(path -> path.toString().endsWith(".java"))
+                        .forEach(sources::add);
+            }
+        }
+        if (sources.isEmpty()) {
+            return;
+        }
+
+        JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
+        if (compiler == null) {
+            throw new IllegalStateException("A JDK is required to run the 
integration tests");
+        }
+
+        String compileClasspath = 
System.getProperty("surefire.test.class.path");
+        if (compileClasspath == null || compileClasspath.isBlank()) {
+            compileClasspath = System.getProperty("java.class.path");
+        }
+        compileClasspath = compileClasspath + File.pathSeparator + shadedJar;
+
+        DiagnosticCollector<JavaFileObject> diagnostics = new 
DiagnosticCollector<>();
+        try (StandardJavaFileManager fileManager = 
compiler.getStandardFileManager(diagnostics, Locale.ROOT,
+                StandardCharsets.UTF_8)) {
+            Iterable<? extends JavaFileObject> units = 
fileManager.getJavaFileObjectsFromPaths(sources);
+            List<String> options = List.of(
+                    "--release", "21",
+                    "-proc:none",
+                    "-classpath", compileClasspath,
+                    "-d", classes.toString());
+            boolean success = compiler.getTask(null, fileManager, diagnostics, 
options, null, units).call();
+            if (!success) {
+                StringBuilder message = new StringBuilder("Failed to compile 
the test webapp:");
+                for (Diagnostic<? extends JavaFileObject> diagnostic : 
diagnostics.getDiagnostics()) {
+                    message.append(System.lineSeparator()).append(diagnostic);
+                }
+                throw new IllegalStateException(message.toString());
+            }
+        }
+    }
+}
diff --git a/modules/owb/src/test/webapp/WEB-INF/beans.xml 
b/modules/owb/src/test/webapp/cdi41/WEB-INF/beans.xml
similarity index 100%
copy from modules/owb/src/test/webapp/WEB-INF/beans.xml
copy to modules/owb/src/test/webapp/cdi41/WEB-INF/beans.xml
diff --git 
a/modules/owb/src/test/webapp/cdi41/WEB-INF/classes/META-INF/services/jakarta.enterprise.inject.spi.Extension
 
b/modules/owb/src/test/webapp/cdi41/WEB-INF/classes/META-INF/services/jakarta.enterprise.inject.spi.Extension
new file mode 100644
index 0000000000..590aeb07a7
--- /dev/null
+++ 
b/modules/owb/src/test/webapp/cdi41/WEB-INF/classes/META-INF/services/jakarta.enterprise.inject.spi.Extension
@@ -0,0 +1,15 @@
+# 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.
+org.apache.webbeans.web.tomcat.testapp.InvokerExtension
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Event.java
similarity index 90%
copy from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
copy to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Event.java
index fdf82e9b6e..00541e0a4c 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
+++ 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Event.java
@@ -16,8 +16,5 @@
  */
 package org.apache.webbeans.web.tomcat.testapp;
 
-import jakarta.enterprise.context.Dependent;
-
-@Dependent
-public class MatchingTarget {
+public class Cdi41Event {
 }
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41EventObserver.java
similarity index 82%
copy from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
copy to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41EventObserver.java
index 843fcae8f5..7b10cc01c9 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
+++ 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41EventObserver.java
@@ -17,11 +17,12 @@
 package org.apache.webbeans.web.tomcat.testapp;
 
 import jakarta.enterprise.context.ApplicationScoped;
+import jakarta.enterprise.event.Observes;
 
 @ApplicationScoped
-public class Greeting {
+public class Cdi41EventObserver {
 
-    public String message() {
-        return "hello-owb-4.1.1";
+    void observe(@Observes Cdi41Event event) {
+        // Observer used to verify BeanContainer.isMatchingEvent().
     }
 }
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Features.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Features.java
similarity index 72%
rename from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Features.java
rename to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Features.java
index be52ef3d28..f638fd557c 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Features.java
+++ 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Features.java
@@ -21,6 +21,7 @@ import java.lang.reflect.Type;
 import java.util.Set;
 
 import jakarta.enterprise.context.Dependent;
+import jakarta.enterprise.context.RequestScoped;
 import jakarta.enterprise.inject.Default;
 import jakarta.enterprise.inject.spi.BeanContainer;
 import jakarta.enterprise.inject.spi.CDI;
@@ -39,4 +40,18 @@ public class Cdi41Features {
 
         return match && !noMatch;
     }
+
+    public boolean matchingEvent() {
+        BeanContainer container = CDI.current().getBeanContainer();
+        Set<Annotation> qualifiers = Set.of(Default.Literal.INSTANCE);
+
+        boolean match = container.isMatchingEvent(Cdi41Event.class, 
qualifiers, Cdi41Event.class, qualifiers);
+        boolean noMatch = container.isMatchingEvent(Cdi41Event.class, 
qualifiers, String.class, qualifiers);
+
+        return match && !noMatch;
+    }
+
+    public boolean contexts() {
+        return 
!CDI.current().getBeanContainer().getContexts(RequestScoped.class).isEmpty();
+    }
 }
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
similarity index 84%
copy from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
copy to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
index 49171da897..b5bd4e6fd0 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
+++ 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
@@ -24,7 +24,8 @@ import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
-@WebServlet(urlPatterns = { "/hello", "/request", "/matching", "/interceptor" 
})
+@WebServlet(urlPatterns = { "/hello", "/request", "/matching", 
"/matching-event", "/contexts", "/interceptor",
+        "/invoker" })
 public class Cdi41Servlet extends HttpServlet {
 
     private static final long serialVersionUID = 1L;
@@ -54,9 +55,18 @@ public class Cdi41Servlet extends HttpServlet {
             case "/matching":
                 result = Boolean.toString(cdi41Features.matching());
                 break;
+            case "/matching-event":
+                result = Boolean.toString(cdi41Features.matchingEvent());
+                break;
+            case "/contexts":
+                result = Boolean.toString(cdi41Features.contexts());
+                break;
             case "/interceptor":
                 result = tracedService.hello();
                 break;
+            case "/invoker":
+                result = InvokerExtension.invoke();
+                break;
             default:
                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
                 return;
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
similarity index 100%
copy from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
copy to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
diff --git 
a/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/InvokerExtension.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/InvokerExtension.java
new file mode 100644
index 0000000000..af3e0e2518
--- /dev/null
+++ 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/InvokerExtension.java
@@ -0,0 +1,48 @@
+/*
+ * 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.webbeans.web.tomcat.testapp;
+
+import jakarta.enterprise.event.Observes;
+import jakarta.enterprise.inject.spi.AnnotatedMethod;
+import jakarta.enterprise.inject.spi.Extension;
+import jakarta.enterprise.inject.spi.ProcessManagedBean;
+import jakarta.enterprise.invoke.Invoker;
+
+public class InvokerExtension implements Extension {
+
+    private static volatile Invoker<InvokerTarget, ?> invoker;
+
+    void process(@Observes ProcessManagedBean<InvokerTarget> event) {
+        AnnotatedMethod<? super InvokerTarget> method = 
event.getAnnotatedBeanClass().getMethods().stream()
+                .filter(candidate -> 
"hello".equals(candidate.getJavaMember().getName()))
+                .findFirst()
+                .orElseThrow(() -> new 
IllegalStateException("InvokerTarget.hello() was not found"));
+        invoker = 
event.createInvoker(method).withInstanceLookup().withArgumentLookup(0).build();
+    }
+
+    public static String invoke() {
+        Invoker<InvokerTarget, ?> current = invoker;
+        if (current == null) {
+            throw new IllegalStateException("The method invoker was not 
initialized");
+        }
+        try {
+            return (String) current.invoke(null, new Object[] { null });
+        } catch (Exception e) {
+            throw new IllegalStateException("Method invoker failed", e);
+        }
+    }
+}
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/InvokerTarget.java
similarity index 87%
copy from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
copy to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/InvokerTarget.java
index fdf82e9b6e..8a061e6f45 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
+++ 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/InvokerTarget.java
@@ -19,5 +19,9 @@ package org.apache.webbeans.web.tomcat.testapp;
 import jakarta.enterprise.context.Dependent;
 
 @Dependent
-public class MatchingTarget {
+public class InvokerTarget {
+
+    public String hello(Greeting greeting) {
+        return "invoked:" + greeting.message();
+    }
 }
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
similarity index 100%
rename from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
rename to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/MatchingTarget.java
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/RequestScopedBean.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/RequestScopedBean.java
similarity index 100%
copy from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/RequestScopedBean.java
copy to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/RequestScopedBean.java
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Traced.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Traced.java
similarity index 100%
rename from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Traced.java
rename to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/Traced.java
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/TracedService.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/TracedService.java
similarity index 100%
rename from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/TracedService.java
rename to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/TracedService.java
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/TracingInterceptor.java
 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/TracingInterceptor.java
similarity index 80%
rename from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/TracingInterceptor.java
rename to 
modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/TracingInterceptor.java
index 816e64822e..bb6413dcc9 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/TracingInterceptor.java
+++ 
b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/TracingInterceptor.java
@@ -28,8 +28,10 @@ public class TracingInterceptor {
 
     @AroundInvoke
     public Object aroundInvoke(InvocationContext context) throws Exception {
-        boolean found = context.getInterceptorBindings().stream()
-                .anyMatch(annotation -> annotation.annotationType() == 
Traced.class);
+        boolean found = context.getInterceptorBinding(Traced.class) != null &&
+                !context.getInterceptorBindings(Traced.class).isEmpty() &&
+                context.getInterceptorBindings().stream()
+                        .anyMatch(annotation -> annotation.annotationType() == 
Traced.class);
         return (found ? "bound:" : "missing:") + context.proceed();
     }
 }
diff --git a/modules/owb/src/test/webapp/WEB-INF/beans.xml 
b/modules/owb/src/test/webapp/lifecycle/WEB-INF/beans.xml
similarity index 100%
copy from modules/owb/src/test/webapp/WEB-INF/beans.xml
copy to modules/owb/src/test/webapp/lifecycle/WEB-INF/beans.xml
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/RequestScopedBean.java
 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
similarity index 56%
rename from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/RequestScopedBean.java
rename to 
modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
index 72c74b3338..5d159fc355 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/RequestScopedBean.java
+++ 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
@@ -16,16 +16,19 @@
  */
 package org.apache.webbeans.web.tomcat.testapp;
 
-import java.util.UUID;
+import jakarta.enterprise.inject.spi.CDI;
+import jakarta.servlet.ServletContextEvent;
+import jakarta.servlet.ServletContextListener;
+import jakarta.servlet.annotation.WebListener;
 
-import jakarta.enterprise.context.RequestScoped;
+@WebListener
+public class LifecycleContextListener implements ServletContextListener {
 
-@RequestScoped
-public class RequestScopedBean {
-
-    private final String id = UUID.randomUUID().toString();
-
-    public String getId() {
-        return id;
+    @Override
+    public void contextInitialized(ServletContextEvent event) {
+        // Listeners are instantiated before the CDI container is started, so
+        // use programmatic lookup rather than field injection here.
+        LifecycleGreeting greeting = 
CDI.current().select(LifecycleGreeting.class).get();
+        event.getServletContext().setAttribute("owb.listener.greeting", 
greeting.message());
     }
 }
diff --git 
a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleFilter.java
 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleFilter.java
new file mode 100644
index 0000000000..4565f859e9
--- /dev/null
+++ 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleFilter.java
@@ -0,0 +1,45 @@
+/*
+ * 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.webbeans.web.tomcat.testapp;
+
+import java.io.IOException;
+
+import jakarta.inject.Inject;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.annotation.WebFilter;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.servlet.http.HttpServletResponse;
+
+@WebFilter(urlPatterns = "/*")
+public class LifecycleFilter implements Filter {
+
+    @Inject
+    private LifecycleGreeting greeting;
+
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
+            throws IOException, ServletException {
+        ((HttpServletResponse) response).setHeader("X-Owb-Greeting", 
greeting.message());
+        HttpServletRequest httpRequest = (HttpServletRequest) request;
+        httpRequest.setAttribute("owb.filter.greeting", greeting.message());
+        chain.doFilter(request, response);
+    }
+}
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleGreeting.java
similarity index 93%
rename from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
rename to 
modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleGreeting.java
index 843fcae8f5..df5032049e 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Greeting.java
+++ 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleGreeting.java
@@ -19,9 +19,9 @@ package org.apache.webbeans.web.tomcat.testapp;
 import jakarta.enterprise.context.ApplicationScoped;
 
 @ApplicationScoped
-public class Greeting {
+public class LifecycleGreeting {
 
     public String message() {
-        return "hello-owb-4.1.1";
+        return "lifecycle-owb-4.1.1";
     }
 }
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleServlet.java
similarity index 54%
copy from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
copy to 
modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleServlet.java
index 49171da897..4d6d35be26 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
+++ 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleServlet.java
@@ -17,51 +17,51 @@
 package org.apache.webbeans.web.tomcat.testapp;
 
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 
+import jakarta.annotation.PostConstruct;
+import jakarta.annotation.PreDestroy;
 import jakarta.inject.Inject;
 import jakarta.servlet.annotation.WebServlet;
 import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
-@WebServlet(urlPatterns = { "/hello", "/request", "/matching", "/interceptor" 
})
-public class Cdi41Servlet extends HttpServlet {
+@WebServlet("/lifecycle")
+public class LifecycleServlet extends HttpServlet {
 
     private static final long serialVersionUID = 1L;
 
     @Inject
-    private Greeting greeting;
+    private LifecycleGreeting greeting;
 
-    @Inject
-    private RequestScopedBean requestScopedBean;
-
-    @Inject
-    private Cdi41Features cdi41Features;
+    @PostConstruct
+    void postConstruct() {
+        writeMarker("postConstruct.marker");
+    }
 
-    @Inject
-    private TracedService tracedService;
+    @PreDestroy
+    void preDestroy() {
+        writeMarker("preDestroy.marker");
+    }
 
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
-        String result;
-        switch (request.getServletPath()) {
-            case "/hello":
-                result = greeting.message();
-                break;
-            case "/request":
-                result = requestScopedBean.getId();
-                break;
-            case "/matching":
-                result = Boolean.toString(cdi41Features.matching());
-                break;
-            case "/interceptor":
-                result = tracedService.hello();
-                break;
-            default:
-                response.sendError(HttpServletResponse.SC_NOT_FOUND);
-                return;
-        }
+        String listener = (String) 
getServletContext().getAttribute("owb.listener.greeting");
+        String filter = (String) request.getAttribute("owb.filter.greeting");
         response.setContentType("text/plain");
-        response.getWriter().println(result);
+        response.getWriter().println(greeting.message() + "|" + listener + "|" 
+ filter);
+    }
+
+    private static void writeMarker(String name) {
+        String markerDirectory = System.getProperty("owb.test.marker.dir");
+        if (markerDirectory != null) {
+            try {
+                Files.writeString(Path.of(markerDirectory).resolve(name), 
"marker");
+            } catch (IOException e) {
+                throw new IllegalStateException("Unable to write lifecycle 
marker " + name, e);
+            }
+        }
     }
 }
diff --git a/modules/owb/src/test/webapp/WEB-INF/beans.xml 
b/modules/owb/src/test/webapp/security/WEB-INF/beans.xml
similarity index 100%
rename from modules/owb/src/test/webapp/WEB-INF/beans.xml
rename to modules/owb/src/test/webapp/security/WEB-INF/beans.xml
diff --git a/modules/owb/src/test/webapp/security/WEB-INF/web.xml 
b/modules/owb/src/test/webapp/security/WEB-INF/web.xml
new file mode 100644
index 0000000000..56ce5e3df3
--- /dev/null
+++ b/modules/owb/src/test/webapp/security/WEB-INF/web.xml
@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+  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="https://jakarta.ee/xml/ns/jakartaee";
+         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance";
+         xsi:schemaLocation="https://jakarta.ee/xml/ns/jakartaee 
https://jakarta.ee/xml/ns/jakartaee/web-app_6_1.xsd";
+         version="6.1">
+
+  <display-name>OWB Security Test</display-name>
+
+  <security-constraint>
+    <web-resource-collection>
+      <web-resource-name>Protected</web-resource-name>
+      <url-pattern>/whoami</url-pattern>
+    </web-resource-collection>
+    <auth-constraint>
+      <role-name>test</role-name>
+    </auth-constraint>
+  </security-constraint>
+
+  <login-config>
+    <auth-method>BASIC</auth-method>
+    <realm-name>OWB Test</realm-name>
+  </login-config>
+
+  <security-role>
+    <role-name>test</role-name>
+  </security-role>
+
+</web-app>
diff --git 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
 
b/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java
similarity index 57%
rename from 
modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
rename to 
modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java
index 49171da897..39f5bc090e 100644
--- 
a/modules/owb/src/test/webapp/src/org/apache/webbeans/web/tomcat/testapp/Cdi41Servlet.java
+++ 
b/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java
@@ -17,6 +17,8 @@
 package org.apache.webbeans.web.tomcat.testapp;
 
 import java.io.IOException;
+import java.security.Principal;
+import java.util.Objects;
 
 import jakarta.inject.Inject;
 import jakarta.servlet.annotation.WebServlet;
@@ -24,44 +26,21 @@ import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
-@WebServlet(urlPatterns = { "/hello", "/request", "/matching", "/interceptor" 
})
-public class Cdi41Servlet extends HttpServlet {
+import org.apache.webbeans.web.tomcat.OpenWebBeansSecurityValve;
 
-    private static final long serialVersionUID = 1L;
-
-    @Inject
-    private Greeting greeting;
+@WebServlet("/whoami")
+public class WhoAmIServlet extends HttpServlet {
 
-    @Inject
-    private RequestScopedBean requestScopedBean;
-
-    @Inject
-    private Cdi41Features cdi41Features;
+    private static final long serialVersionUID = 1L;
 
     @Inject
-    private TracedService tracedService;
+    private Principal principal;
 
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
-        String result;
-        switch (request.getServletPath()) {
-            case "/hello":
-                result = greeting.message();
-                break;
-            case "/request":
-                result = requestScopedBean.getId();
-                break;
-            case "/matching":
-                result = Boolean.toString(cdi41Features.matching());
-                break;
-            case "/interceptor":
-                result = tracedService.hello();
-                break;
-            default:
-                response.sendError(HttpServletResponse.SC_NOT_FOUND);
-                return;
-        }
+        Principal valvePrincipal = OpenWebBeansSecurityValve.getPrincipal();
+        String valveName = valvePrincipal == null ? "null" : 
valvePrincipal.getName();
         response.setContentType("text/plain");
-        response.getWriter().println(result);
+        
response.getWriter().println(Objects.requireNonNull(principal).getName() + "|" 
+ valveName);
     }
 }


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to