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 b945d5fdb5b12e7411cd8b13ac5f85730c425e52 Author: opencode <[email protected]> AuthorDate: Fri Nov 6 15:48:11 2026 +0100 Add optional OWB module coverage and documentation Add JSP/EL, redeploy and multi-webapp isolation, session scope and webapp security filter tests. Document the module build, test prerequisites, configuration and known limitations. --- modules/owb/README.md | 187 +++++++++++++++++++++ modules/owb/pom.xml | 28 +++ .../webbeans/web/tomcat/OpenWebBeansCdi41IT.java | 7 + .../webbeans/web/tomcat/OpenWebBeansJspIT.java | 48 ++++++ .../web/tomcat/OpenWebBeansRedeployIT.java | 105 ++++++++++++ .../web/tomcat/OpenWebBeansSecurityFilterIT.java | 58 +++++++ .../apache/webbeans/web/tomcat/OwbTestServer.java | 88 +++++++--- .../apache/webbeans/web/tomcat/OwbTestSupport.java | 6 + .../apache/webbeans/web/tomcat/WebAppBuilder.java | 11 ++ .../webbeans/web/tomcat/testapp/Cdi41Servlet.java | 10 +- .../web/tomcat/testapp/SessionScopedBean.java | 33 ++++ modules/owb/src/test/webapp/jsp/WEB-INF/beans.xml | 21 +++ modules/owb/src/test/webapp/jsp/index.jsp | 20 +++ .../webbeans/web/tomcat/testapp/JspGreeting.java | 29 ++++ .../src/test/webapp/multi/one/WEB-INF/beans.xml | 21 +++ .../webapp/multi/one/WEB-INF/classes/value.txt | 1 + .../webbeans/web/tomcat/testapp/MultiGreeting.java | 35 ++++ .../webbeans/web/tomcat/testapp/MultiServlet.java | 40 +++++ .../src/test/webapp/multi/two/WEB-INF/beans.xml | 21 +++ .../webapp/multi/two/WEB-INF/classes/value.txt | 1 + .../webbeans/web/tomcat/testapp/MultiGreeting.java | 35 ++++ .../webbeans/web/tomcat/testapp/MultiServlet.java | 40 +++++ .../owb/src/test/webapp/redeploy/WEB-INF/beans.xml | 21 +++ .../web/tomcat/testapp/RedeployGreeting.java | 27 +++ .../web/tomcat/testapp/RedeployServlet.java | 66 ++++++++ .../test/webapp/security-filter/WEB-INF/beans.xml | 21 +++ .../test/webapp/security-filter/WEB-INF/web.xml | 53 ++++++ .../web/tomcat/testapp/FilterWhoAmIServlet.java | 45 +++++ webapps/docs/cdi.xml | 107 ++++++++++-- 29 files changed, 1150 insertions(+), 35 deletions(-) diff --git a/modules/owb/README.md b/modules/owb/README.md new file mode 100644 index 0000000000..8c69eda852 --- /dev/null +++ b/modules/owb/README.md @@ -0,0 +1,187 @@ +<!-- + + 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. + +--> + +# Apache Tomcat CDI support module + +This module packages Apache OpenWebBeans and provides CDI support for +Tomcat. The resulting shaded JAR is intended to be placed in the Tomcat +`lib` directory. + +## Prerequisites + +- Java 21 or later. +- Apache Maven. +- The Tomcat artifacts for `${tomcat.version}` available in the local Maven + repository. + +The default build uses `12.0.0-SNAPSHOT`, which is not published to Maven +Central. Install the required Tomcat artifacts from a local Tomcat build, or +override `tomcat.version` with a released Tomcat version. + +## Building + +```bash +cd modules/owb +mvn clean package +``` + +The shaded JAR is written to: + +```text +target/tomcat-owb-<owb-version>.jar +``` + +`mvn package` builds the JAR but does not run the integration tests. + +## Testing + +Run the full integration test suite: + +```bash +mvn clean verify +``` + +Run a single test class: + +```bash +mvn -Dit.test=OpenWebBeansSecurityIT verify +``` + +The integration tests: + +- Use an embedded Tomcat server. +- Use port `0` and temporary directories. +- Run against the shaded module JAR, not `target/classes`. +- Do not require an external Tomcat installation or a running server. + +### JSP/EL test prerequisites + +The JSP/EL integration test uses `tomcat-embed-jasper` and +`tomcat-embed-el`. For a snapshot build, install them from the local Tomcat +build: + +```bash +cd $TOMCAT_SRC +ant embed-jars + +mvn install:install-file \ + -Dfile=output/embed/tomcat-embed-jasper.jar \ + -DgroupId=org.apache.tomcat.embed \ + -DartifactId=tomcat-embed-jasper \ + -Dversion=12.0.0-SNAPSHOT \ + -Dpackaging=jar + +mvn install:install-file \ + -Dfile=output/embed/tomcat-embed-el.jar \ + -DgroupId=org.apache.tomcat.embed \ + -DartifactId=tomcat-embed-el \ + -Dversion=12.0.0-SNAPSHOT \ + -Dpackaging=jar +``` + +The test suite also uses the Eclipse compiler (`ecj`) for JSP compilation. + +## Test architecture + +The tests are standard Maven Failsafe integration tests: + +- Test classes are named `*IT` and live in `src/test/java`. +- Test webapps live under `src/test/webapp`. +- `WebAppBuilder` compiles and assembles a temporary exploded webapp. +- `OwbTestServer` starts and stops embedded Tomcat. +- `OwbTestSupport` provides HTTP, shaded-JAR and marker-file helpers. + +### Classpath isolation + +The Failsafe configuration deliberately: + +- Points `classesDirectory` at an empty directory so `target/classes` is not + on the test runtime classpath. +- Adds the shaded module JAR explicitly. +- Excludes the unshaded OpenWebBeans, CDI, Interceptor and XBean dependencies + from the test runtime. + +Tests assert that both `OpenWebBeansListener` and `WebBeansContext` are loaded +from the shaded JAR. This prevents the tests from accidentally validating an +unshaded build. + +The test webapps do not contain the module JAR in `WEB-INF/lib`; the module is +provided through the test/common classpath, matching the production +`lib` directory model. + +## Test coverage + +| Test class | Coverage | +|---|---| +| `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 | +| `OpenWebBeansSecurityFilterIT` | Webapp-level `OpenWebBeansSecurityFilter` | +| `OpenWebBeansJspIT` | JSP/EL resolution through OWB | +| `OpenWebBeansRedeployIT` | Multiple webapps, classloader isolation, context restart, `@PreDestroy`, listener registration | + +## Adding a test + +1. Add a webapp template under `src/test/webapp/<name>`. +2. Include `WEB-INF/beans.xml` for a CDI webapp. +3. Put Java sources under `src/`. +4. Add a `*IT` test class. +5. Build the webapp with `WebAppBuilder`. +6. Start it with `OwbTestServer`. +7. Verify HTTP endpoints and, where relevant, marker files or context state. + +Example skeleton: + +```java +Path webapp = new WebAppBuilder(tempDir.resolve("app"), "my-template") + .build(OwbTestSupport.shadedJar()); + +try (OwbTestServer server = OwbTestServer.start(webapp, + ListenerMode.SERVER, false, true)) { + HttpClient client = OwbTestSupport.newClient(); + assertEquals("expected", OwbTestSupport.get(client, + server.baseUri().resolve("endpoint"))); +} +``` + +## Known limitations + +- Field injection into `ServletContextListener` is not supported because + Tomcat instantiates listeners before the CDI container starts. Use + `CDI.current().select(...)` inside the listener instead. +- Producer-level `@Priority` on competing alternative producers is not + covered. OpenWebBeans reports an ambiguous resolution for that scenario, + so no failing gate test is included. +- `ELAwareBeanManager` is not covered. OpenWebBeans provides the core + `BeanManager`, and the supplemental CDI EL API is not part of this module. +- Classloader garbage-collection leak detection is not asserted. The redeploy + tests verify deterministic behavior: pre-destroy callbacks, separate + contexts, no duplicate listener registration and successful restart. + +## Troubleshooting + +- **Missing Tomcat artifacts**: install `tomcat-catalina` and its transitive + Tomcat dependencies for `${tomcat.version}` into the local Maven repository. +- **Missing Jasper/EL artifacts**: run `ant embed-jars` and install + `tomcat-embed-jasper` and `tomcat-embed-el` as described above. +- **Tests run against the wrong classes**: check the Failsafe + `classesDirectory`, `additionalClasspathElements` and + `classpathDependencyExcludes` settings. +- **Port or directory conflicts**: the tests use port `0` and JUnit temporary + directories; do not hard-code either in new tests. diff --git a/modules/owb/pom.xml b/modules/owb/pom.xml index c15db4fffe..f6be93d23a 100644 --- a/modules/owb/pom.xml +++ b/modules/owb/pom.xml @@ -40,6 +40,7 @@ <jakarta-el.version>6.0.1</jakarta-el.version> <tomcat.version>12.0.0-SNAPSHOT</tomcat.version> <junit.version>5.11.4</junit.version> + <ecj.version>3.41.0</ecj.version> </properties> <dependencies> @@ -105,6 +106,24 @@ <version>${junit.version}</version> <scope>test</scope> </dependency> + <dependency> + <groupId>org.apache.tomcat.embed</groupId> + <artifactId>tomcat-embed-el</artifactId> + <version>${tomcat.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.apache.tomcat.embed</groupId> + <artifactId>tomcat-embed-jasper</artifactId> + <version>${tomcat.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>org.eclipse.jdt</groupId> + <artifactId>ecj</artifactId> + <version>${ecj.version}</version> + <scope>test</scope> + </dependency> </dependencies> <build> @@ -116,6 +135,9 @@ <release>21</release> </configuration> </plugin> + <!-- Create an empty directory used to keep target/classes off the + integration test runtime classpath. The tests must run against + the shaded JAR. --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-antrun-plugin</artifactId> @@ -177,6 +199,8 @@ </execution> </executions> </plugin> + <!-- Integration tests use embedded Tomcat. They run after the + shade plugin has produced the deployable JAR. --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-failsafe-plugin</artifactId> @@ -195,6 +219,8 @@ <additionalClasspathElements> <additionalClasspathElement>${project.build.directory}/${project.build.finalName}.jar</additionalClasspathElement> </additionalClasspathElements> + <!-- Remove unshaded copies of the classes contained in the + shaded JAR so the test validates the deployable JAR. --> <classpathDependencyExcludes> <classpathDependencyExclude>org.apache.openwebbeans:openwebbeans-spi</classpathDependencyExclude> <classpathDependencyExclude>org.apache.openwebbeans:openwebbeans-impl</classpathDependencyExclude> @@ -208,6 +234,8 @@ <classpathDependencyExclude>jakarta.interceptor:jakarta.interceptor-api</classpathDependencyExclude> <classpathDependencyExclude>jakarta.annotation:jakarta.annotation-api</classpathDependencyExclude> </classpathDependencyExcludes> + <!-- Test webapp templates and the shaded JAR are passed to + the test JVM explicitly; target/classes is not used. --> <systemPropertyVariables> <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> 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 fda921cab8..0e4d7af80a 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 @@ -53,6 +53,13 @@ class OpenWebBeansCdi41IT { assertFalse(firstRequest.isEmpty()); assertFalse(secondRequest.isEmpty()); + HttpClient sessionClient = OwbTestSupport.newCookieClient(); + String firstSession = OwbTestSupport.get(sessionClient, baseUri.resolve("session")); + String secondSession = OwbTestSupport.get(sessionClient, baseUri.resolve("session")); + assertEquals(firstSession, secondSession); + String differentSession = OwbTestSupport.get(OwbTestSupport.newCookieClient(), baseUri.resolve("session")); + assertNotEquals(firstSession, differentSession); + 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"))); diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansJspIT.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansJspIT.java new file mode 100644 index 0000000000..3824979aa2 --- /dev/null +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansJspIT.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; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertTrue; + +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 OpenWebBeansJspIT { + + @TempDir + Path tempDir; + + @Test + void jspElResolvesCdiBean() throws Exception { + Path shadedJar = OwbTestSupport.shadedJar(); + Path webapp = new WebAppBuilder(tempDir.resolve("jsp"), "jsp").build(shadedJar); + try (OwbTestServer server = OwbTestServer.start(webapp, ListenerMode.SERVER, false, true, "/owb-jsp", true, + null)) { + HttpClient client = OwbTestSupport.newClient(); + HttpResponse<String> response = OwbTestSupport.getResponse(client, server.baseUri().resolve("index.jsp")); + assertEquals(200, response.statusCode(), response.body()); + assertTrue(response.headers().firstValue("Content-Type").orElse("").startsWith("text/html")); + assertTrue(response.body().contains("hello-owb-4.1.1"), response.body()); + } + } +} diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansRedeployIT.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansRedeployIT.java new file mode 100644 index 0000000000..943c0754fb --- /dev/null +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansRedeployIT.java @@ -0,0 +1,105 @@ +/* + * 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.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.net.http.HttpClient; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Arrays; +import java.util.List; + +import org.apache.webbeans.servlet.WebBeansConfigurationListener; +import org.apache.webbeans.web.tomcat.OwbTestServer.Deployment; +import org.apache.webbeans.web.tomcat.OwbTestServer.ListenerMode; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +class OpenWebBeansRedeployIT { + + @TempDir + Path tempDir; + + @Test + void multipleWebappsAreIsolated() throws Exception { + Path shadedJar = OwbTestSupport.shadedJar(); + Path one = new WebAppBuilder(tempDir.resolve("one"), "multi/one").build(shadedJar); + Path two = new WebAppBuilder(tempDir.resolve("two"), "multi/two").build(shadedJar); + + try (OwbTestServer server = OwbTestServer.start( + List.of(new Deployment("/owb-one", one), new Deployment("/owb-two", two)), + ListenerMode.SERVER, false, true, false, null)) { + assertEquals("one", get(server, "/owb-one", "value")); + assertEquals("two", get(server, "/owb-two", "value")); + + ClassLoader oneLoader = server.context("/owb-one").getLoader().getClassLoader(); + ClassLoader twoLoader = server.context("/owb-two").getLoader().getClassLoader(); + assertNotSame(oneLoader, twoLoader); + + server.stopContext("/owb-one"); + assertEquals("two", get(server, "/owb-two", "value")); + } + } + + @Test + void contextRestartStartsFreshContainer() 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("redeploy"), "redeploy").build(shadedJar); + try (OwbTestServer server = OwbTestServer.start(webapp, ListenerMode.SERVER, false, true, + "/owb-redeploy", null)) { + assertEquals("redeploy-owb-4.1.1", get(server, "/owb-redeploy", "redeploy")); + String firstPostConstruct = Files.readString(markerDirectory.resolve("postConstruct.marker")); + assertTrue(Files.notExists(markerDirectory.resolve("preDestroy.marker"))); + + server.stopContext("/owb-redeploy"); + assertTrue(Files.exists(markerDirectory.resolve("preDestroy.marker"))); + + server.startContext("/owb-redeploy"); + assertEquals("redeploy-owb-4.1.1", get(server, "/owb-redeploy", "redeploy")); + String secondPostConstruct = Files.readString(markerDirectory.resolve("postConstruct.marker")); + assertNotEquals(firstPostConstruct, secondPostConstruct); + assertEquals(1, listenerCount(server)); + } + } finally { + if (previousMarkerDirectory == null) { + System.clearProperty("owb.test.marker.dir"); + } else { + System.setProperty("owb.test.marker.dir", previousMarkerDirectory); + } + } + } + + private static String get(OwbTestServer server, String contextPath, String path) throws Exception { + HttpClient client = OwbTestSupport.newClient(); + return OwbTestSupport.get(client, server.baseUri(contextPath).resolve(path)); + } + + private static int listenerCount(OwbTestServer server) { + return (int) Arrays.stream(server.context("/owb-redeploy").findApplicationListeners()) + .filter(WebBeansConfigurationListener.class.getName()::equals) + .count(); + } +} diff --git a/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityFilterIT.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityFilterIT.java new file mode 100644 index 0000000000..2e429542f3 --- /dev/null +++ b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OpenWebBeansSecurityFilterIT.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 OpenWebBeansSecurityFilterIT { + + @TempDir + Path tempDir; + + @Test + void webappSecurityFilterPropagatesPrincipal() throws Exception { + Path shadedJar = OwbTestSupport.shadedJar(); + Path webapp = new WebAppBuilder(tempDir.resolve("security-filter"), "security-filter").build(shadedJar); + try (OwbTestServer server = OwbTestServer.start(webapp, ListenerMode.SERVER, false, false, + "/owb-security-filter", 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/OwbTestServer.java b/modules/owb/src/test/java/org/apache/webbeans/web/tomcat/OwbTestServer.java index 47ac599bae..164e46ef46 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,6 +19,10 @@ 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; import java.util.function.Consumer; import org.apache.catalina.Context; @@ -34,26 +38,48 @@ final class OwbTestServer implements AutoCloseable { CONTEXT } + record Deployment(String contextPath, Path webapp) { + } + private final Tomcat tomcat; - private final Context context; - private final URI baseUri; + private final Map<String, Context> contexts; + private final Map<String, URI> baseUris; - private OwbTestServer(Tomcat tomcat, Context context, URI baseUri) { + private OwbTestServer(Tomcat tomcat, Map<String, Context> contexts, Map<String, URI> baseUris) { this.tomcat = tomcat; - this.context = context; - this.baseUri = baseUri; + this.contexts = contexts; + this.baseUris = baseUris; + } + + static OwbTestServer start(Path webapp, ListenerMode mode, boolean startWithoutBeansXml, + boolean addSecurityValve) throws Exception { + return start(List.of(new Deployment("/owb-test", webapp)), mode, startWithoutBeansXml, addSecurityValve, + false, null); } 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-"); + return start(List.of(new Deployment(contextPath, webapp)), mode, startWithoutBeansXml, addSecurityValve, + false, configurer); + } + + static OwbTestServer start(Path webapp, ListenerMode mode, boolean startWithoutBeansXml, + boolean addSecurityValve, String contextPath, boolean useDefaultWebXml, + Consumer<Tomcat> configurer) throws Exception { + return start(List.of(new Deployment(contextPath, webapp)), mode, startWithoutBeansXml, addSecurityValve, + useDefaultWebXml, configurer); + } + + static OwbTestServer start(List<Deployment> deployments, ListenerMode mode, boolean startWithoutBeansXml, + boolean addSecurityValve, boolean useDefaultWebXml, Consumer<Tomcat> configurer) throws Exception { + Path base = Files.createTempDirectory(deployments.get(0).webapp().getParent(), "tomcat-"); Tomcat tomcat = new Tomcat(); tomcat.setBaseDir(base.toString()); tomcat.setPort(0); tomcat.getConnector(); tomcat.getServer().setPort(-1); - tomcat.setAddDefaultWebXmlToWebapp(false); + tomcat.setAddDefaultWebXmlToWebapp(useDefaultWebXml); // Enable naming so that Tomcat's DefaultInstanceManager invokes // @PostConstruct and @PreDestroy callbacks. tomcat.enableNaming(); @@ -62,17 +88,23 @@ final class OwbTestServer implements AutoCloseable { configurer.accept(tomcat); } - Context context = tomcat.addWebapp(contextPath, webapp.toString()); + Map<String, Context> contexts = new LinkedHashMap<>(); + for (Deployment deployment : deployments) { + Context context = tomcat.addWebapp(deployment.contextPath(), deployment.webapp().toString()); + 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) { 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 { @@ -83,27 +115,41 @@ final class OwbTestServer implements AutoCloseable { } int port = tomcat.getConnector().getLocalPort(); - URI baseUri = URI.create("http://127.0.0.1:" + port + contextPath + "/"); - return new OwbTestServer(tomcat, context, baseUri); + Map<String, URI> baseUris = new LinkedHashMap<>(); + 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)); } - 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 contexts.values().iterator().next(); } - Context context() { - return context; + Context context(String contextPath) { + return contexts.get(contextPath); } URI baseUri() { - return baseUri; + return baseUris.values().iterator().next(); + } + + URI baseUri(String contextPath) { + return baseUris.get(contextPath); } Tomcat tomcat() { return tomcat; } + void stopContext(String contextPath) throws Exception { + context(contextPath).stop(); + } + + void startContext(String contextPath) throws Exception { + context(contextPath).start(); + } + @Override public void close() throws Exception { try { 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 index 7c4f4c38e7..7ffedaf360 100644 --- 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 @@ -19,6 +19,7 @@ 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.CookieManager; import java.net.URI; import java.net.http.HttpClient; import java.net.http.HttpRequest; @@ -55,6 +56,11 @@ final class OwbTestSupport { return HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)).build(); } + static HttpClient newCookieClient() { + return HttpClient.newBuilder().connectTimeout(Duration.ofSeconds(5)) + .cookieHandler(new CookieManager()).build(); + } + static HttpResponse<String> getResponse(HttpClient client, URI uri) throws Exception { return getResponse(client, uri, null); } 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 309e9b0151..c7194f57f6 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 @@ -84,6 +84,17 @@ final class WebAppBuilder { copyTree(webInf, destination.resolve("WEB-INF")); } + // Copy root-level web resources such as JSP pages. + try (Stream<Path> stream = Files.list(template)) { + for (Path child : stream.toList()) { + String name = child.getFileName().toString(); + if ("WEB-INF".equals(name) || "src".equals(name)) { + continue; + } + copyTree(child, destination.resolve(name)); + } + } + Path classes = destination.resolve("WEB-INF/classes"); Files.createDirectories(classes); compile(classes, shadedJar); 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 b5bd4e6fd0..a7b759fa28 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 @@ -24,8 +24,8 @@ import jakarta.servlet.http.HttpServlet; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; -@WebServlet(urlPatterns = { "/hello", "/request", "/matching", "/matching-event", "/contexts", "/interceptor", - "/invoker" }) +@WebServlet(urlPatterns = { "/hello", "/request", "/session", "/matching", "/matching-event", "/contexts", + "/interceptor", "/invoker" }) public class Cdi41Servlet extends HttpServlet { private static final long serialVersionUID = 1L; @@ -36,6 +36,9 @@ public class Cdi41Servlet extends HttpServlet { @Inject private RequestScopedBean requestScopedBean; + @Inject + private SessionScopedBean sessionScopedBean; + @Inject private Cdi41Features cdi41Features; @@ -52,6 +55,9 @@ public class Cdi41Servlet extends HttpServlet { case "/request": result = requestScopedBean.getId(); break; + case "/session": + result = sessionScopedBean.getId(); + break; case "/matching": result = Boolean.toString(cdi41Features.matching()); break; diff --git a/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/SessionScopedBean.java b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/SessionScopedBean.java new file mode 100644 index 0000000000..df8c0e1db0 --- /dev/null +++ b/modules/owb/src/test/webapp/cdi41/src/org/apache/webbeans/web/tomcat/testapp/SessionScopedBean.java @@ -0,0 +1,33 @@ +/* + * 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.util.UUID; + +import jakarta.enterprise.context.SessionScoped; + +@SessionScoped +public class SessionScopedBean implements java.io.Serializable { + + private static final long serialVersionUID = 1L; + + private final String id = UUID.randomUUID().toString(); + + public String getId() { + return id; + } +} diff --git a/modules/owb/src/test/webapp/jsp/WEB-INF/beans.xml b/modules/owb/src/test/webapp/jsp/WEB-INF/beans.xml new file mode 100644 index 0000000000..abc684f5de --- /dev/null +++ b/modules/owb/src/test/webapp/jsp/WEB-INF/beans.xml @@ -0,0 +1,21 @@ +<!-- + 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. +--> +<beans 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/beans_4_1.xsd" + version="4.1" + bean-discovery-mode="all"/> diff --git a/modules/owb/src/test/webapp/jsp/index.jsp b/modules/owb/src/test/webapp/jsp/index.jsp new file mode 100644 index 0000000000..21e1a03c3b --- /dev/null +++ b/modules/owb/src/test/webapp/jsp/index.jsp @@ -0,0 +1,20 @@ +<%-- + 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. +--%> +<%@ page contentType="text/html; charset=UTF-8" %> +<html> +<body>${greeting.message}</body> +</html> diff --git a/modules/owb/src/test/webapp/jsp/src/org/apache/webbeans/web/tomcat/testapp/JspGreeting.java b/modules/owb/src/test/webapp/jsp/src/org/apache/webbeans/web/tomcat/testapp/JspGreeting.java new file mode 100644 index 0000000000..c1de05e7ef --- /dev/null +++ b/modules/owb/src/test/webapp/jsp/src/org/apache/webbeans/web/tomcat/testapp/JspGreeting.java @@ -0,0 +1,29 @@ +/* + * 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.context.ApplicationScoped; +import jakarta.inject.Named; + +@Named("greeting") +@ApplicationScoped +public class JspGreeting { + + public String getMessage() { + return "hello-owb-4.1.1"; + } +} diff --git a/modules/owb/src/test/webapp/multi/one/WEB-INF/beans.xml b/modules/owb/src/test/webapp/multi/one/WEB-INF/beans.xml new file mode 100644 index 0000000000..abc684f5de --- /dev/null +++ b/modules/owb/src/test/webapp/multi/one/WEB-INF/beans.xml @@ -0,0 +1,21 @@ +<!-- + 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. +--> +<beans 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/beans_4_1.xsd" + version="4.1" + bean-discovery-mode="all"/> diff --git a/modules/owb/src/test/webapp/multi/one/WEB-INF/classes/value.txt b/modules/owb/src/test/webapp/multi/one/WEB-INF/classes/value.txt new file mode 100644 index 0000000000..5626abf0f7 --- /dev/null +++ b/modules/owb/src/test/webapp/multi/one/WEB-INF/classes/value.txt @@ -0,0 +1 @@ +one diff --git a/modules/owb/src/test/webapp/multi/one/src/org/apache/webbeans/web/tomcat/testapp/MultiGreeting.java b/modules/owb/src/test/webapp/multi/one/src/org/apache/webbeans/web/tomcat/testapp/MultiGreeting.java new file mode 100644 index 0000000000..be34d9dea8 --- /dev/null +++ b/modules/owb/src/test/webapp/multi/one/src/org/apache/webbeans/web/tomcat/testapp/MultiGreeting.java @@ -0,0 +1,35 @@ +/* + * 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 java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class MultiGreeting { + + public String value() { + try (InputStream input = MultiGreeting.class.getResourceAsStream("/value.txt")) { + return new String(input.readAllBytes(), StandardCharsets.UTF_8).strip(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/modules/owb/src/test/webapp/multi/one/src/org/apache/webbeans/web/tomcat/testapp/MultiServlet.java b/modules/owb/src/test/webapp/multi/one/src/org/apache/webbeans/web/tomcat/testapp/MultiServlet.java new file mode 100644 index 0000000000..9ec9dc883c --- /dev/null +++ b/modules/owb/src/test/webapp/multi/one/src/org/apache/webbeans/web/tomcat/testapp/MultiServlet.java @@ -0,0 +1,40 @@ +/* + * 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.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@WebServlet("/value") +public class MultiServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Inject + private MultiGreeting greeting; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.setContentType("text/plain"); + response.getWriter().println(greeting.value()); + } +} diff --git a/modules/owb/src/test/webapp/multi/two/WEB-INF/beans.xml b/modules/owb/src/test/webapp/multi/two/WEB-INF/beans.xml new file mode 100644 index 0000000000..abc684f5de --- /dev/null +++ b/modules/owb/src/test/webapp/multi/two/WEB-INF/beans.xml @@ -0,0 +1,21 @@ +<!-- + 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. +--> +<beans 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/beans_4_1.xsd" + version="4.1" + bean-discovery-mode="all"/> diff --git a/modules/owb/src/test/webapp/multi/two/WEB-INF/classes/value.txt b/modules/owb/src/test/webapp/multi/two/WEB-INF/classes/value.txt new file mode 100644 index 0000000000..f719efd430 --- /dev/null +++ b/modules/owb/src/test/webapp/multi/two/WEB-INF/classes/value.txt @@ -0,0 +1 @@ +two diff --git a/modules/owb/src/test/webapp/multi/two/src/org/apache/webbeans/web/tomcat/testapp/MultiGreeting.java b/modules/owb/src/test/webapp/multi/two/src/org/apache/webbeans/web/tomcat/testapp/MultiGreeting.java new file mode 100644 index 0000000000..be34d9dea8 --- /dev/null +++ b/modules/owb/src/test/webapp/multi/two/src/org/apache/webbeans/web/tomcat/testapp/MultiGreeting.java @@ -0,0 +1,35 @@ +/* + * 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 java.io.InputStream; +import java.nio.charset.StandardCharsets; + +import jakarta.enterprise.context.ApplicationScoped; + +@ApplicationScoped +public class MultiGreeting { + + public String value() { + try (InputStream input = MultiGreeting.class.getResourceAsStream("/value.txt")) { + return new String(input.readAllBytes(), StandardCharsets.UTF_8).strip(); + } catch (IOException e) { + throw new IllegalStateException(e); + } + } +} diff --git a/modules/owb/src/test/webapp/multi/two/src/org/apache/webbeans/web/tomcat/testapp/MultiServlet.java b/modules/owb/src/test/webapp/multi/two/src/org/apache/webbeans/web/tomcat/testapp/MultiServlet.java new file mode 100644 index 0000000000..9ec9dc883c --- /dev/null +++ b/modules/owb/src/test/webapp/multi/two/src/org/apache/webbeans/web/tomcat/testapp/MultiServlet.java @@ -0,0 +1,40 @@ +/* + * 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.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +@WebServlet("/value") +public class MultiServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Inject + private MultiGreeting greeting; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.setContentType("text/plain"); + response.getWriter().println(greeting.value()); + } +} diff --git a/modules/owb/src/test/webapp/redeploy/WEB-INF/beans.xml b/modules/owb/src/test/webapp/redeploy/WEB-INF/beans.xml new file mode 100644 index 0000000000..abc684f5de --- /dev/null +++ b/modules/owb/src/test/webapp/redeploy/WEB-INF/beans.xml @@ -0,0 +1,21 @@ +<!-- + 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. +--> +<beans 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/beans_4_1.xsd" + version="4.1" + bean-discovery-mode="all"/> diff --git a/modules/owb/src/test/webapp/redeploy/src/org/apache/webbeans/web/tomcat/testapp/RedeployGreeting.java b/modules/owb/src/test/webapp/redeploy/src/org/apache/webbeans/web/tomcat/testapp/RedeployGreeting.java new file mode 100644 index 0000000000..e398bb34d1 --- /dev/null +++ b/modules/owb/src/test/webapp/redeploy/src/org/apache/webbeans/web/tomcat/testapp/RedeployGreeting.java @@ -0,0 +1,27 @@ +/* + * 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.context.ApplicationScoped; + +@ApplicationScoped +public class RedeployGreeting { + + public String message() { + return "redeploy-owb-4.1.1"; + } +} diff --git a/modules/owb/src/test/webapp/redeploy/src/org/apache/webbeans/web/tomcat/testapp/RedeployServlet.java b/modules/owb/src/test/webapp/redeploy/src/org/apache/webbeans/web/tomcat/testapp/RedeployServlet.java new file mode 100644 index 0000000000..1f3687aff2 --- /dev/null +++ b/modules/owb/src/test/webapp/redeploy/src/org/apache/webbeans/web/tomcat/testapp/RedeployServlet.java @@ -0,0 +1,66 @@ +/* + * 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 java.nio.file.Files; +import java.nio.file.Path; +import java.util.UUID; + +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("/redeploy") +public class RedeployServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Inject + private RedeployGreeting greeting; + + @PostConstruct + void postConstruct() { + writeMarker("postConstruct.marker", UUID.randomUUID().toString()); + } + + @PreDestroy + void preDestroy() { + writeMarker("preDestroy.marker", "destroyed"); + } + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + response.setContentType("text/plain"); + response.getWriter().println(greeting.message()); + } + + private static void writeMarker(String name, String content) { + String markerDirectory = System.getProperty("owb.test.marker.dir"); + if (markerDirectory != null) { + try { + Files.writeString(Path.of(markerDirectory).resolve(name), content); + } catch (IOException e) { + throw new IllegalStateException("Unable to write lifecycle marker " + name, e); + } + } + } +} diff --git a/modules/owb/src/test/webapp/security-filter/WEB-INF/beans.xml b/modules/owb/src/test/webapp/security-filter/WEB-INF/beans.xml new file mode 100644 index 0000000000..abc684f5de --- /dev/null +++ b/modules/owb/src/test/webapp/security-filter/WEB-INF/beans.xml @@ -0,0 +1,21 @@ +<!-- + 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. +--> +<beans 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/beans_4_1.xsd" + version="4.1" + bean-discovery-mode="all"/> diff --git a/modules/owb/src/test/webapp/security-filter/WEB-INF/web.xml b/modules/owb/src/test/webapp/security-filter/WEB-INF/web.xml new file mode 100644 index 0000000000..c3ea0117af --- /dev/null +++ b/modules/owb/src/test/webapp/security-filter/WEB-INF/web.xml @@ -0,0 +1,53 @@ +<?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 Filter Test</display-name> + + <filter> + <filter-name>owbSecurityFilter</filter-name> + <filter-class>org.apache.webbeans.web.tomcat.OpenWebBeansSecurityFilter</filter-class> + </filter> + <filter-mapping> + <filter-name>owbSecurityFilter</filter-name> + <url-pattern>/*</url-pattern> + </filter-mapping> + + <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/security-filter/src/org/apache/webbeans/web/tomcat/testapp/FilterWhoAmIServlet.java b/modules/owb/src/test/webapp/security-filter/src/org/apache/webbeans/web/tomcat/testapp/FilterWhoAmIServlet.java new file mode 100644 index 0000000000..da80ebf30a --- /dev/null +++ b/modules/owb/src/test/webapp/security-filter/src/org/apache/webbeans/web/tomcat/testapp/FilterWhoAmIServlet.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 java.security.Principal; + +import jakarta.inject.Inject; +import jakarta.servlet.annotation.WebServlet; +import jakarta.servlet.http.HttpServlet; +import jakarta.servlet.http.HttpServletRequest; +import jakarta.servlet.http.HttpServletResponse; + +import org.apache.webbeans.web.tomcat.OpenWebBeansSecurityFilter; + +@WebServlet("/whoami") +public class FilterWhoAmIServlet extends HttpServlet { + + private static final long serialVersionUID = 1L; + + @Inject + private Principal principal; + + @Override + protected void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException { + Principal filterPrincipal = OpenWebBeansSecurityFilter.getPrincipal(); + String filterName = filterPrincipal == null ? "null" : filterPrincipal.getName(); + response.setContentType("text/plain"); + response.getWriter().println(principal.getName() + "|" + filterName); + } +} diff --git a/webapps/docs/cdi.xml b/webapps/docs/cdi.xml index d8e9f5e98e..32fc23b644 100644 --- a/webapps/docs/cdi.xml +++ b/webapps/docs/cdi.xml @@ -45,34 +45,117 @@ <section name="CDI support"> <p> - CDI 4.1 support is provided by the <code>modules/owb</code> optional module. - It packages the Apache OpenWebBeans project and allows adding CDI 4.1 - support to the Tomcat container. The build process of the module uses - Apache Maven, and is not available as a binary bundle as it is built using - a number of publicly available JARs. + CDI support is provided by the <code>modules/owb</code> optional module. + It packages Apache OpenWebBeans and allows adding CDI support + to the Tomcat container. The build process of the module uses Apache + Maven, and is not available as a binary bundle as it is built using a + number of publicly available JARs. + </p> + + </section> + + <section name="Building"> + + <p> + Building the module requires Java 21 or later, Apache Maven and the Tomcat + artifacts for the Tomcat version used by the module in the local Maven + repository. The default configuration uses the Tomcat version from the + current source tree. </p> <p> The process to build CDI support is the following. <source><![CDATA[cd $TOMCAT_SRC/modules/owb mvn clean && mvn package]]></source> - The module integration tests can be run with <code>mvn clean verify</code>. The resulting JAR at <code>target/tomcat-owb-x.y.z.jar</code> (where x.y.z depends on the - Apache OpenWebBeans version used during the build) - should be processed by the Tomcat migration tool for Jakarta EE, and - then be placed into the <code>lib</code> folder of the Tomcat - installation.<br/> - CDI support can then be enabled for all webapps in the container by adding + Apache OpenWebBeans version used during the build) can be placed directly + into the <code>lib</code> folder of the Tomcat installation. The JAR is + built against Jakarta EE APIs. The Tomcat migration tool for Jakarta EE is + only required for applications that still use <code>javax</code> APIs. + </p> + + </section> + + <section name="Testing"> + + <p> + The module integration tests use an embedded Tomcat server and run + against the shaded module JAR. They are Failsafe integration tests and + are not run by <code>mvn package</code>. + </p> + + <p> + To build the module and run all integration tests: + <source><![CDATA[cd $TOMCAT_SRC/modules/owb +mvn clean verify]]></source> + A single integration test can be run with: + <source><![CDATA[mvn -Dit.test=OpenWebBeansSecurityIT verify]]></source> + </p> + + <p> + The tests cover CDI APIs, both supported listener configuration modes, + <code>beans.xml</code> discovery, security integration, lifecycle + callbacks and JSP/EL integration. See + <code>modules/owb/README.md</code> for the test architecture and for the + prerequisites needed when Tomcat snapshot artifacts are used. + </p> + + </section> + + <section name="Enabling CDI support"> + + <p> + CDI support can be enabled for all webapps in the container by adding the following listener in <code>server.xml</code> nested inside the <code>Server</code> element: <source><![CDATA[<Listener className="org.apache.webbeans.web.tomcat.OpenWebBeansListener" optional="true" startWithoutBeansXml="false" />]]></source> The listener will produce a non fatal error if the CDI container loading fails.<br/> + The listener supports the following properties: + </p> + + <ul> + <li><code>startWithoutBeansXml</code> - if <code>true</code>, CDI is + started even when no <code>beans.xml</code> is present. The default is + <code>true</code>.</li> + <li><code>addSecurityValve</code> - if <code>true</code>, the + <code>OpenWebBeansSecurityValve</code> is added to each context pipeline. + The default is <code>true</code>.</li> + </ul> + + <p> CDI support can also be enabled at the individual webapp level by adding the following listener to the webapp <code>context.xml</code> file nested - inside the <code>Server</code> element: + inside the <code>Context</code> element: <source><![CDATA[<Listener className="org.apache.webbeans.web.tomcat.OpenWebBeansContextLifecycleListener" />]]></source> + The context-level listener supports the same properties as the server-level + listener. + </p> + + <p> + When security integration is required, the listener can add the + <code>OpenWebBeansSecurityValve</code> automatically. Alternatively, the + <code>OpenWebBeansSecurityFilter</code> can be configured in the webapp + <code>WEB-INF/web.xml</code> file. + </p> + + </section> + + <section name="Known limitations"> + + <p> + Servlet and filter injection are supported. A + <code>ServletContextListener</code> is instantiated before the CDI + container starts, so field injection into a listener is not available. + Use programmatic lookup with <code>CDI.current().select(...)</code> inside + a listener instead. + </p> + + <p> + Support for features that depend on optional CDI EL APIs or on CDI + features not implemented by OpenWebBeans may vary. See + <code>modules/owb/README.md</code> for the currently tested feature set. </p> </section> --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
