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 e747e04eb1143c93fac09fa9f3e3c69970d11c44
Author: opencode <[email protected]>
AuthorDate: Fri Nov 6 16:34:23 2026 +0100

    Add extended OWB integration and unit test coverage
    
    Add unit tests for TomcatPlugin and OpenWebBeansInstanceManager, verify 
negative beans.xml behavior, context-level security valve settings, dynamic 
deployment, filter/listener lifecycle callbacks and security principal proxy 
modes.
---
 modules/owb/README.md                              |  16 ++-
 .../tomcat/OpenWebBeansInstanceManagerTest.java    | 151 +++++++++++++++++++++
 .../web/tomcat/OpenWebBeansSecurityIT.java         |  73 ++++++++--
 .../tomcat/OpenWebBeansTomcatIntegrationIT.java    |  35 +++++
 .../apache/webbeans/web/tomcat/OwbTestServer.java  |  54 ++++++--
 .../webbeans/web/tomcat/TomcatPluginTest.java      | 150 ++++++++++++++++++++
 .../apache/webbeans/web/tomcat/WebAppBuilder.java  |  18 +++
 .../web/tomcat/testapp/CustomPrincipal.java}       |  19 +--
 .../webbeans/web/tomcat/testapp/Cdi41Servlet.java  |   5 +-
 .../tomcat/testapp/LifecycleContextListener.java   |  17 +++
 .../web/tomcat/testapp/LifecycleFilter.java        |  19 +++
 .../owb/src/test/webapp/security/WEB-INF/web.xml   |   2 +
 .../webbeans/web/tomcat/testapp/WhoAmIServlet.java |  26 +++-
 13 files changed, 537 insertions(+), 48 deletions(-)

diff --git a/modules/owb/README.md b/modules/owb/README.md
index 8c69eda852..94a2455b5d 100644
--- a/modules/owb/README.md
+++ b/modules/owb/README.md
@@ -53,11 +53,19 @@ target/tomcat-owb-<owb-version>.jar
 
 Run the full integration test suite:
 
+Run the unit tests:
+
+```bash
+mvn test
+```
+
+Run the unit and integration tests:
+
 ```bash
 mvn clean verify
 ```
 
-Run a single test class:
+Run a single integration test class:
 
 ```bash
 mvn -Dit.test=OpenWebBeansSecurityIT verify
@@ -129,9 +137,11 @@ provided through the test/common classpath, matching the 
production
 
 | Test class | Coverage |
 |---|---|
+| `TomcatPluginTest` | Servlet spec class handling, managed-bean rejection, 
injection support and security service registration |
+| `OpenWebBeansInstanceManagerTest` | Delegation for all `InstanceManager` 
creation variants and destruction fallback |
 | `OpenWebBeansCdi41IT` | CDI injection, request/session scope, 
`isMatchingBean`, `isMatchingEvent`, `getContexts`, interceptor bindings, 
method invoker |
-| `OpenWebBeansTomcatIntegrationIT` | Server/context listener modes, 
`startWithoutBeansXml`, alternate `beans.xml` location, security valve 
configuration, filter/servlet injection, lifecycle callbacks |
-| `OpenWebBeansSecurityIT` | BASIC authentication, `@Inject Principal`, 
security valve principal |
+| `OpenWebBeansTomcatIntegrationIT` | Server/context listener modes, 
`startWithoutBeansXml` positive and negative behavior, alternate `beans.xml` 
location, security valve configuration, dynamic deployment, 
filter/listener/servlet injection and lifecycle callbacks |
+| `OpenWebBeansSecurityIT` | BASIC authentication, `@Inject Principal`, 
security valve principal, principal proxy modes and custom proxy APIs |
 | `OpenWebBeansSecurityFilterIT` | Webapp-level `OpenWebBeansSecurityFilter` |
 | `OpenWebBeansJspIT` | JSP/EL resolution through OWB |
 | `OpenWebBeansRedeployIT` | Multiple webapps, classloader isolation, context 
restart, `@PreDestroy`, listener registration |
diff --git 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansInstanceManagerTest.java
 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansInstanceManagerTest.java
new file mode 100644
index 0000000000..8c65af9eec
--- /dev/null
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansInstanceManagerTest.java
@@ -0,0 +1,151 @@
+/*
+ * 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.assertInstanceOf;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.lang.reflect.InvocationTargetException;
+import java.util.ArrayList;
+import java.util.List;
+
+import javax.naming.NamingException;
+
+import org.apache.tomcat.InstanceManager;
+import org.junit.jupiter.api.Test;
+
+class OpenWebBeansInstanceManagerTest {
+
+    @Test
+    void newInstanceByClassDelegates() throws Exception {
+        RecordingInstanceManager delegate = new RecordingInstanceManager();
+        OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager(
+                getClass().getClassLoader(), delegate);
+
+        Object instance = manager.newInstance(TestBean.class);
+
+        assertEquals(1, delegate.classInstances);
+        assertInstanceOf(TestBean.class, instance);
+    }
+
+    @Test
+    void newInstanceByClassNameDelegates() throws Exception {
+        RecordingInstanceManager delegate = new RecordingInstanceManager();
+        OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager(
+                getClass().getClassLoader(), delegate);
+
+        Object instance = manager.newInstance(TestBean.class.getName());
+
+        assertEquals(1, delegate.stringInstances);
+        assertInstanceOf(TestBean.class, instance);
+    }
+
+    @Test
+    void newInstanceByClassNameAndClassLoaderDelegates() throws Exception {
+        RecordingInstanceManager delegate = new RecordingInstanceManager();
+        OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager(
+                getClass().getClassLoader(), delegate);
+
+        Object instance = manager.newInstance(TestBean.class.getName(), 
getClass().getClassLoader());
+
+        assertEquals(1, delegate.stringLoaderInstances);
+        assertInstanceOf(TestBean.class, instance);
+    }
+
+    @Test
+    void newInstanceByObjectDoesNotDelegateCreation() throws Exception {
+        RecordingInstanceManager delegate = new RecordingInstanceManager();
+        OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager(
+                getClass().getClassLoader(), delegate);
+        TestBean instance = new TestBean();
+
+        manager.newInstance(instance);
+
+        assertEquals(0, delegate.objectInstances);
+        assertTrue(delegate.destroyed.isEmpty());
+    }
+
+    @Test
+    void destroyInstanceAlwaysDelegates() throws Exception {
+        RecordingInstanceManager delegate = new RecordingInstanceManager();
+        OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager(
+                getClass().getClassLoader(), delegate);
+        TestBean instance = new TestBean();
+
+        manager.destroyInstance(instance);
+
+        assertEquals(List.of(instance), delegate.destroyed);
+    }
+
+    @Test
+    void destroyKnownInstanceDelegates() throws Exception {
+        RecordingInstanceManager delegate = new RecordingInstanceManager();
+        OpenWebBeansInstanceManager manager = new OpenWebBeansInstanceManager(
+                getClass().getClassLoader(), delegate);
+
+        Object instance = manager.newInstance(TestBean.class);
+        manager.destroyInstance(instance);
+
+        assertEquals(List.of(instance), delegate.destroyed);
+    }
+
+    static class TestBean {
+    }
+
+    static class RecordingInstanceManager implements InstanceManager {
+
+        private int classInstances;
+        private int stringInstances;
+        private int stringLoaderInstances;
+        private int objectInstances;
+        private final List<Object> destroyed = new ArrayList<>();
+
+        @Override
+        public Object newInstance(Class<?> clazz) throws 
IllegalAccessException, InvocationTargetException,
+                NamingException, InstantiationException {
+            classInstances++;
+            return new TestBean();
+        }
+
+        @Override
+        public Object newInstance(String className) throws 
IllegalAccessException, InvocationTargetException,
+                NamingException, InstantiationException, 
ClassNotFoundException {
+            stringInstances++;
+            return new TestBean();
+        }
+
+        @Override
+        public void newInstance(Object object) throws IllegalAccessException, 
InvocationTargetException,
+                NamingException {
+            objectInstances++;
+        }
+
+        @Override
+        public Object newInstance(String className, ClassLoader classLoader)
+                throws IllegalAccessException, InvocationTargetException, 
NamingException, InstantiationException,
+                ClassNotFoundException {
+            stringLoaderInstances++;
+            return new TestBean();
+        }
+
+        @Override
+        public void destroyInstance(Object object) throws 
IllegalAccessException, InvocationTargetException {
+            destroyed.add(object);
+        }
+    }
+}
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
index a19d5fad5b..b8f177e6b7 100644
--- 
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
@@ -17,31 +17,33 @@
 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.assertTrue;
 
 import java.net.URI;
 import java.net.http.HttpClient;
 import java.net.http.HttpResponse;
 import java.nio.file.Path;
+import java.util.function.Consumer;
 
 import org.apache.webbeans.web.tomcat.OwbTestServer.ListenerMode;
+import org.apache.webbeans.web.tomcat.testapp.CustomPrincipal;
 import org.junit.jupiter.api.Test;
 import org.junit.jupiter.api.io.TempDir;
 
 class OpenWebBeansSecurityIT {
 
+    private static final String PRINCIPAL_PROXY_PROPERTY =
+            "org.apache.webbeans.component.PrincipalBean.proxy";
+    private static final String PRINCIPAL_PROXY_APIS_PROPERTY =
+            "org.apache.webbeans.component.PrincipalBean.proxyApis";
+
     @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");
-                })) {
+        try (OwbTestServer server = startSecurityServer("security")) {
             HttpClient client = OwbTestSupport.newClient();
             URI uri = server.baseUri().resolve("whoami");
 
@@ -49,10 +51,59 @@ class OpenWebBeansSecurityIT {
             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());
+            assertEquals("testuser|testuser", authenticatedBody(server, 
"whoami"));
         }
     }
+
+    @Test
+    void principalProxyEnabled() throws Exception {
+        try (OwbTestServer server = startSecurityServer("security-proxy")) {
+            String type = authenticatedBody(server, 
"security-service-principal-type");
+            assertTrue(type.contains("TomcatSecurityServicePrincipal"), type);
+        }
+    }
+
+    @Test
+    void principalProxyDisabled() throws Exception {
+        try (OwbTestServer server = startSecurityServer("security-no-proxy",
+                builder -> 
builder.openWebBeansProperty(PRINCIPAL_PROXY_PROPERTY, "false"))) {
+            String type = authenticatedBody(server, 
"security-service-principal-type");
+            assertFalse(type.contains("TomcatSecurityServicePrincipal"), type);
+        }
+    }
+
+    @Test
+    void customPrincipalProxyApis() throws Exception {
+        try (OwbTestServer server = 
startSecurityServer("security-custom-proxy",
+                builder -> builder
+                        .openWebBeansProperty(PRINCIPAL_PROXY_PROPERTY, "true")
+                        .openWebBeansProperty(PRINCIPAL_PROXY_APIS_PROPERTY, 
CustomPrincipal.class.getName()))) {
+            String type = authenticatedBody(server, 
"security-service-principal-custom");
+            assertEquals("custom", type, type);
+        }
+    }
+
+    private OwbTestServer startSecurityServer(String name) throws Exception {
+        return startSecurityServer(name, builder -> {
+        });
+    }
+
+    private OwbTestServer startSecurityServer(String name, 
Consumer<WebAppBuilder> customizer) throws Exception {
+        WebAppBuilder builder = new WebAppBuilder(tempDir.resolve(name), 
"security");
+        customizer.accept(builder);
+        Path webapp = builder.build(OwbTestSupport.shadedJar());
+        return OwbTestServer.start(webapp, ListenerMode.SERVER, false, true, 
"/owb-security",
+                tomcat -> {
+                    tomcat.addUser("testuser", "testpass");
+                    tomcat.addRole("testuser", "test");
+                });
+    }
+
+    private static String authenticatedBody(OwbTestServer server, String path) 
throws Exception {
+        HttpClient client = OwbTestSupport.newClient();
+        HttpResponse<String> response = OwbTestSupport.getResponse(client,
+                server.baseUri().resolve(path), 
OwbTestSupport.basicAuth("testuser", "testpass"));
+        assertEquals(200, response.statusCode(), response.body());
+        return response.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
index 632297d09e..4abb80cc79 100644
--- 
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
@@ -64,6 +64,7 @@ class OpenWebBeansTomcatIntegrationIT {
                 .build(shadedJar);
         try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, true, true)) {
             assertHello(server);
+            assertEquals("injected", status(server));
             assertWebBeansListener(server, true);
         }
     }
@@ -75,6 +76,7 @@ class OpenWebBeansTomcatIntegrationIT {
                 .includeBeansXml(false)
                 .build(shadedJar);
         try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.SERVER, false, true)) {
+            assertEquals("not-injected", status(server));
             assertWebBeansListener(server, false);
         }
     }
@@ -101,6 +103,32 @@ class OpenWebBeansTomcatIntegrationIT {
         }
     }
 
+    @Test
+    void contextSecurityValveDisabled() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path webapp = new 
WebAppBuilder(tempDir.resolve("context-no-security-valve"), 
"cdi41").build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(webapp, 
ListenerMode.CONTEXT, false, false)) {
+            assertHello(server);
+            assertSecurityValve(server, false);
+        }
+    }
+
+    @Test
+    void dynamicContextDeployment() throws Exception {
+        Path shadedJar = OwbTestSupport.shadedJar();
+        Path initial = new WebAppBuilder(tempDir.resolve("dynamic-initial"), 
"cdi41").build(shadedJar);
+        Path dynamic = new WebAppBuilder(tempDir.resolve("dynamic-app"), 
"multi/one").build(shadedJar);
+        try (OwbTestServer server = OwbTestServer.start(initial, 
ListenerMode.SERVER, false, true)) {
+            assertHello(server);
+
+            server.deploy("/owb-dynamic", dynamic);
+
+            HttpClient client = OwbTestSupport.newClient();
+            assertEquals("one", OwbTestSupport.get(client, 
server.baseUri("/owb-dynamic").resolve("value")));
+            assertHello(server);
+        }
+    }
+
     @Test
     void filterListenerAndServletInjection() throws Exception {
         Path markerDirectory = tempDir.resolve("markers");
@@ -120,6 +148,8 @@ class OpenWebBeansTomcatIntegrationIT {
                 OwbTestSupport.assertMarker(markerDirectory, 
"postConstruct.marker");
             }
             OwbTestSupport.assertMarker(markerDirectory, "preDestroy.marker");
+            OwbTestSupport.assertMarker(markerDirectory, 
"filterPreDestroy.marker");
+            OwbTestSupport.assertMarker(markerDirectory, 
"listenerPreDestroy.marker");
         } finally {
             if (previousMarkerDirectory == null) {
                 System.clearProperty("owb.test.marker.dir");
@@ -134,6 +164,11 @@ class OpenWebBeansTomcatIntegrationIT {
         assertEquals("hello-owb-4.1.1", OwbTestSupport.get(client, 
server.baseUri().resolve("hello")));
     }
 
+    private static String status(OwbTestServer server) throws Exception {
+        HttpClient client = OwbTestSupport.newClient();
+        return OwbTestSupport.get(client, 
server.baseUri().resolve("injection-status"));
+    }
+
     private static void assertSecurityValve(OwbTestServer server, boolean 
expected) {
         boolean found = 
Arrays.stream(server.context().getPipeline().getValves())
                 .anyMatch(valve -> valve instanceof OpenWebBeansSecurityValve);
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
index d6c8e8723d..7b607afd7d 100644
--- 
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
@@ -19,7 +19,6 @@ package org.apache.webbeans.web.tomcat;
 import java.net.URI;
 import java.nio.file.Files;
 import java.nio.file.Path;
-import java.util.Collections;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
@@ -45,11 +44,20 @@ final class OwbTestServer implements AutoCloseable {
     private final Tomcat tomcat;
     private final Map<String, Context> contexts;
     private final Map<String, URI> baseUris;
+    private final ListenerMode listenerMode;
+    private final boolean startWithoutBeansXml;
+    private final boolean addSecurityValve;
+    private final int port;
 
-    private OwbTestServer(Tomcat tomcat, Map<String, Context> contexts, 
Map<String, URI> baseUris) {
+    private OwbTestServer(Tomcat tomcat, Map<String, Context> contexts, 
Map<String, URI> baseUris,
+            ListenerMode listenerMode, boolean startWithoutBeansXml, boolean 
addSecurityValve, int port) {
         this.tomcat = tomcat;
         this.contexts = contexts;
         this.baseUris = baseUris;
+        this.listenerMode = listenerMode;
+        this.startWithoutBeansXml = startWithoutBeansXml;
+        this.addSecurityValve = addSecurityValve;
+        this.port = port;
     }
 
     static OwbTestServer start(Path webapp, ListenerMode mode, boolean 
startWithoutBeansXml,
@@ -92,19 +100,8 @@ final class OwbTestServer implements AutoCloseable {
         Map<String, Context> contexts = new LinkedHashMap<>();
         for (Deployment deployment : deployments) {
             Context context = tomcat.addWebapp(deployment.contextPath(), 
deployment.webapp().toString());
-            if (context instanceof StandardContext standardContext) {
-                // These checks require --add-opens for java.lang and java.rmi.
-                // They are not relevant to the module integration tests.
-                standardContext.setClearReferencesThreadLocals(false);
-                standardContext.setClearReferencesRmiTargets(false);
-            }
+            configureContext(context, mode, startWithoutBeansXml, 
addSecurityValve);
             contexts.put(deployment.contextPath(), context);
-            if (mode == ListenerMode.CONTEXT) {
-                OpenWebBeansContextLifecycleListener listener = new 
OpenWebBeansContextLifecycleListener();
-                listener.setStartWithoutBeansXml(startWithoutBeansXml);
-                listener.setAddSecurityValve(addSecurityValve);
-                context.addLifecycleListener(listener);
-            }
         }
 
         if (mode == ListenerMode.SERVER) {
@@ -126,7 +123,18 @@ final class OwbTestServer implements AutoCloseable {
         for (String contextPath : contexts.keySet()) {
             baseUris.put(contextPath, URI.create("http://127.0.0.1:"; + port + 
contextPath + "/"));
         }
-        return new OwbTestServer(tomcat, 
Collections.unmodifiableMap(contexts), Collections.unmodifiableMap(baseUris));
+        return new OwbTestServer(tomcat, contexts, baseUris, mode, 
startWithoutBeansXml, addSecurityValve, port);
+    }
+
+    Context deploy(String contextPath, Path webapp) throws Exception {
+        Context context = tomcat.addWebapp(contextPath, webapp.toString());
+        configureContext(context, listenerMode, startWithoutBeansXml, 
addSecurityValve);
+        if (!context.getState().isAvailable()) {
+            context.start();
+        }
+        contexts.put(contextPath, context);
+        baseUris.put(contextPath, URI.create("http://127.0.0.1:"; + port + 
contextPath + "/"));
+        return context;
     }
 
     Context context() {
@@ -166,6 +174,22 @@ final class OwbTestServer implements AutoCloseable {
         }
     }
 
+    private static void configureContext(Context context, ListenerMode mode, 
boolean startWithoutBeansXml,
+            boolean addSecurityValve) {
+        if (context instanceof StandardContext standardContext) {
+            // These checks require --add-opens for java.lang and java.rmi.
+            // They are not relevant to the module integration tests.
+            standardContext.setClearReferencesThreadLocals(false);
+            standardContext.setClearReferencesRmiTargets(false);
+        }
+        if (mode == ListenerMode.CONTEXT) {
+            OpenWebBeansContextLifecycleListener listener = new 
OpenWebBeansContextLifecycleListener();
+            listener.setStartWithoutBeansXml(startWithoutBeansXml);
+            listener.setAddSecurityValve(addSecurityValve);
+            context.addLifecycleListener(listener);
+        }
+    }
+
     private static void closeQuietly(Tomcat tomcat) {
         try {
             tomcat.stop();
diff --git 
a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/TomcatPluginTest.java
 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/TomcatPluginTest.java
new file mode 100644
index 0000000000..a9e53217a6
--- /dev/null
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/TomcatPluginTest.java
@@ -0,0 +1,150 @@
+/*
+ * 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.assertDoesNotThrow;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertSame;
+import static org.junit.jupiter.api.Assertions.assertThrows;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.io.IOException;
+
+import jakarta.enterprise.inject.spi.BeanManager;
+import jakarta.servlet.Filter;
+import jakarta.servlet.FilterChain;
+import jakarta.servlet.FilterConfig;
+import jakarta.servlet.ServletContextEvent;
+import jakarta.servlet.ServletContextListener;
+import jakarta.servlet.ServletException;
+import jakarta.servlet.ServletRequest;
+import jakarta.servlet.ServletRequestEvent;
+import jakarta.servlet.ServletRequestListener;
+import jakarta.servlet.ServletResponse;
+import jakarta.servlet.http.HttpServlet;
+import jakarta.servlet.http.HttpSessionEvent;
+import jakarta.servlet.http.HttpSessionListener;
+
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.exception.WebBeansConfigurationException;
+import org.apache.webbeans.spi.SecurityService;
+import org.junit.jupiter.api.Test;
+
+class TomcatPluginTest {
+
+    @Test
+    void securityServiceRegistration() {
+        TomcatPlugin plugin = new TomcatPlugin();
+
+        assertTrue(plugin.supportService(SecurityService.class));
+        assertFalse(plugin.supportService(BeanManager.class));
+
+        SecurityService service = 
plugin.getSupportedService(SecurityService.class);
+        assertNotNull(service);
+        assertSame(WebBeansContext.getInstance().getSecurityService(), 
service);
+        assertNull(plugin.getSupportedService(BeanManager.class));
+    }
+
+    @Test
+    void servletSpecClassesAreRejectedAsManagedBeans() {
+        TomcatPlugin plugin = new TomcatPlugin();
+
+        assertThrows(WebBeansConfigurationException.class, () -> 
plugin.isManagedBean(TestServlet.class));
+        assertThrows(WebBeansConfigurationException.class, () -> 
plugin.isManagedBean(TestFilter.class));
+        assertThrows(WebBeansConfigurationException.class,
+                () -> plugin.isManagedBean(TestServletContextListener.class));
+        assertThrows(WebBeansConfigurationException.class, () -> 
plugin.isManagedBean(TestSessionListener.class));
+        assertThrows(WebBeansConfigurationException.class,
+                () -> plugin.isManagedBean(TestServletRequestListener.class));
+    }
+
+    @Test
+    void nonServletClassesAreManagedBeans() {
+        TomcatPlugin plugin = new TomcatPlugin();
+
+        assertDoesNotThrow(() -> plugin.isManagedBean(PlainBean.class));
+        assertDoesNotThrow(() -> plugin.isManagedBean(Object.class));
+    }
+
+    @Test
+    void javaEeComponentInjectionSupport() {
+        TomcatPlugin plugin = new TomcatPlugin();
+
+        
assertTrue(plugin.supportsJavaEeComponentInjections(TestServlet.class));
+        assertTrue(plugin.supportsJavaEeComponentInjections(TestFilter.class));
+        
assertTrue(plugin.supportsJavaEeComponentInjections(TestServletContextListener.class));
+        
assertTrue(plugin.supportsJavaEeComponentInjections(TestSessionListener.class));
+        
assertTrue(plugin.supportsJavaEeComponentInjections(TestServletRequestListener.class));
+        assertFalse(plugin.supportsJavaEeComponentInjections(PlainBean.class));
+        assertFalse(plugin.supportsJavaEeComponentInjections(Object.class));
+    }
+
+    static class TestServlet extends HttpServlet {
+        private static final long serialVersionUID = 1L;
+    }
+
+    static class TestFilter implements Filter {
+        @Override
+        public void init(FilterConfig filterConfig) {
+        }
+
+        @Override
+        public void doFilter(ServletRequest request, ServletResponse response, 
FilterChain chain)
+                throws IOException, ServletException {
+            chain.doFilter(request, response);
+        }
+
+        @Override
+        public void destroy() {
+        }
+    }
+
+    static class TestServletContextListener implements ServletContextListener {
+        @Override
+        public void contextInitialized(ServletContextEvent event) {
+        }
+
+        @Override
+        public void contextDestroyed(ServletContextEvent event) {
+        }
+    }
+
+    static class TestSessionListener implements HttpSessionListener {
+        @Override
+        public void sessionCreated(HttpSessionEvent event) {
+        }
+
+        @Override
+        public void sessionDestroyed(HttpSessionEvent event) {
+        }
+    }
+
+    static class TestServletRequestListener implements ServletRequestListener {
+        @Override
+        public void requestInitialized(ServletRequestEvent event) {
+        }
+
+        @Override
+        public void requestDestroyed(ServletRequestEvent event) {
+        }
+    }
+
+    static class PlainBean {
+    }
+}
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
index c7194f57f6..918508649c 100644
--- 
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
@@ -23,9 +23,11 @@ import java.nio.file.Files;
 import java.nio.file.Path;
 import java.nio.file.StandardCopyOption;
 import java.util.ArrayList;
+import java.util.LinkedHashMap;
 import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Locale;
+import java.util.Map;
 import java.util.Objects;
 import java.util.Set;
 import java.util.stream.Stream;
@@ -45,6 +47,7 @@ final class WebAppBuilder {
     private final Path destination;
     private final Path template;
     private final Set<Path> sourceRoots = new LinkedHashSet<>();
+    private final Map<String, String> openWebBeansProperties = new 
LinkedHashMap<>();
     private boolean includeBeansXml = true;
     private boolean includeWebXml = true;
     private boolean beansXmlInClasses;
@@ -78,6 +81,11 @@ final class WebAppBuilder {
         return this;
     }
 
+    WebAppBuilder openWebBeansProperty(String name, String value) {
+        openWebBeansProperties.put(name, value);
+        return this;
+    }
+
     Path build(Path shadedJar) throws IOException {
         Path webInf = template.resolve("WEB-INF");
         if (Files.isDirectory(webInf)) {
@@ -106,6 +114,16 @@ final class WebAppBuilder {
             Files.copy(source, target, StandardCopyOption.REPLACE_EXISTING);
         }
 
+        if (!openWebBeansProperties.isEmpty()) {
+            Path target = 
classes.resolve("META-INF/openwebbeans/openwebbeans.properties");
+            Files.createDirectories(target.getParent());
+            StringBuilder properties = new StringBuilder();
+            for (Map.Entry<String, String> entry : 
openWebBeansProperties.entrySet()) {
+                
properties.append(entry.getKey()).append('=').append(entry.getValue()).append('\n');
+            }
+            Files.writeString(target, properties.toString(), 
StandardCharsets.UTF_8);
+        }
+
         return destination;
     }
 
diff --git 
a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/testapp/CustomPrincipal.java
similarity index 55%
copy from 
modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
copy to 
modules/owb/src/test/java/org/apache/webbeans/web/tomcat/testapp/CustomPrincipal.java
index 5d159fc355..dedfd4848d 100644
--- 
a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
+++ 
b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/testapp/CustomPrincipal.java
@@ -16,19 +16,10 @@
  */
 package org.apache.webbeans.web.tomcat.testapp;
 
-import jakarta.enterprise.inject.spi.CDI;
-import jakarta.servlet.ServletContextEvent;
-import jakarta.servlet.ServletContextListener;
-import jakarta.servlet.annotation.WebListener;
+import java.security.Principal;
 
-@WebListener
-public class LifecycleContextListener implements ServletContextListener {
-
-    @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());
-    }
+/**
+ * Test interface used to verify the Principal proxy API configuration.
+ */
+public interface CustomPrincipal extends Principal {
 }
diff --git 
a/modules/owb/src/test/webapp/cdi41/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
index a7b759fa28..ce0fabb9f7 100644
--- 
a/modules/owb/src/test/webapp/cdi41/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
@@ -25,7 +25,7 @@ import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
 @WebServlet(urlPatterns = { "/hello", "/request", "/session", "/matching", 
"/matching-event", "/contexts",
-        "/interceptor", "/invoker" })
+        "/interceptor", "/invoker", "/injection-status" })
 public class Cdi41Servlet extends HttpServlet {
 
     private static final long serialVersionUID = 1L;
@@ -73,6 +73,9 @@ public class Cdi41Servlet extends HttpServlet {
             case "/invoker":
                 result = InvokerExtension.invoke();
                 break;
+            case "/injection-status":
+                result = greeting == null ? "not-injected" : "injected";
+                break;
             default:
                 response.sendError(HttpServletResponse.SC_NOT_FOUND);
                 return;
diff --git 
a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
index 5d159fc355..eb7147f16a 100644
--- 
a/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
+++ 
b/modules/owb/src/test/webapp/lifecycle/src/org/apache/webbeans/web/tomcat/testapp/LifecycleContextListener.java
@@ -16,6 +16,11 @@
  */
 package org.apache.webbeans.web.tomcat.testapp;
 
+import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
+
+import jakarta.annotation.PreDestroy;
 import jakarta.enterprise.inject.spi.CDI;
 import jakarta.servlet.ServletContextEvent;
 import jakarta.servlet.ServletContextListener;
@@ -31,4 +36,16 @@ public class LifecycleContextListener implements 
ServletContextListener {
         LifecycleGreeting greeting = 
CDI.current().select(LifecycleGreeting.class).get();
         event.getServletContext().setAttribute("owb.listener.greeting", 
greeting.message());
     }
+
+    @PreDestroy
+    void preDestroy() {
+        String markerDirectory = System.getProperty("owb.test.marker.dir");
+        if (markerDirectory != null) {
+            try {
+                
Files.writeString(Path.of(markerDirectory).resolve("listenerPreDestroy.marker"),
 "marker");
+            } catch (IOException e) {
+                throw new IllegalStateException("Unable to write listener 
lifecycle marker", e);
+            }
+        }
+    }
 }
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
index 4565f859e9..68a689eba8 100644
--- 
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
@@ -17,7 +17,10 @@
 package org.apache.webbeans.web.tomcat.testapp;
 
 import java.io.IOException;
+import java.nio.file.Files;
+import java.nio.file.Path;
 
+import jakarta.annotation.PreDestroy;
 import jakarta.inject.Inject;
 import jakarta.servlet.Filter;
 import jakarta.servlet.FilterChain;
@@ -42,4 +45,20 @@ public class LifecycleFilter implements Filter {
         httpRequest.setAttribute("owb.filter.greeting", greeting.message());
         chain.doFilter(request, response);
     }
+
+    @PreDestroy
+    void preDestroy() {
+        writeMarker("filterPreDestroy.marker");
+    }
+
+    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/security/WEB-INF/web.xml 
b/modules/owb/src/test/webapp/security/WEB-INF/web.xml
index 56ce5e3df3..d7f4d6c54e 100644
--- a/modules/owb/src/test/webapp/security/WEB-INF/web.xml
+++ b/modules/owb/src/test/webapp/security/WEB-INF/web.xml
@@ -26,6 +26,8 @@
     <web-resource-collection>
       <web-resource-name>Protected</web-resource-name>
       <url-pattern>/whoami</url-pattern>
+      <url-pattern>/security-service-principal-type</url-pattern>
+      <url-pattern>/security-service-principal-custom</url-pattern>
     </web-resource-collection>
     <auth-constraint>
       <role-name>test</role-name>
diff --git 
a/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java
 
b/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java
index 39f5bc090e..dadac92dfd 100644
--- 
a/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java
+++ 
b/modules/owb/src/test/webapp/security/src/org/apache/webbeans/web/tomcat/testapp/WhoAmIServlet.java
@@ -18,7 +18,6 @@ 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;
@@ -26,9 +25,11 @@ import jakarta.servlet.http.HttpServlet;
 import jakarta.servlet.http.HttpServletRequest;
 import jakarta.servlet.http.HttpServletResponse;
 
+import org.apache.webbeans.config.WebBeansContext;
+import org.apache.webbeans.spi.SecurityService;
 import org.apache.webbeans.web.tomcat.OpenWebBeansSecurityValve;
 
-@WebServlet("/whoami")
+@WebServlet(urlPatterns = { "/whoami", "/security-service-principal-type", 
"/security-service-principal-custom" })
 public class WhoAmIServlet extends HttpServlet {
 
     private static final long serialVersionUID = 1L;
@@ -38,9 +39,26 @@ public class WhoAmIServlet extends HttpServlet {
 
     @Override
     protected void doGet(HttpServletRequest request, HttpServletResponse 
response) throws IOException {
+        String path = request.getServletPath();
+        response.setContentType("text/plain");
+
+        if ("/security-service-principal-type".equals(path)) {
+            Principal current = securityService().getCurrentPrincipal();
+            response.getWriter().println(current.getClass().getName());
+            return;
+        }
+        if ("/security-service-principal-custom".equals(path)) {
+            Principal current = securityService().getCurrentPrincipal();
+            response.getWriter().println(current instanceof CustomPrincipal ? 
"custom" : "not-custom");
+            return;
+        }
+
         Principal valvePrincipal = OpenWebBeansSecurityValve.getPrincipal();
         String valveName = valvePrincipal == null ? "null" : 
valvePrincipal.getName();
-        response.setContentType("text/plain");
-        
response.getWriter().println(Objects.requireNonNull(principal).getName() + "|" 
+ valveName);
+        response.getWriter().println(principal.getName() + "|" + valveName);
+    }
+
+    private static SecurityService securityService() {
+        return WebBeansContext.currentInstance().getSecurityService();
     }
 }


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

Reply via email to