You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@tomee.apache.org by rz...@apache.org on 2024/07/03 18:43:28 UTC

(tomee) branch main updated: TOMEE-4332 Load SpringWebUtils in the context class loader in TomEE w… (#1166)

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

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


The following commit(s) were added to refs/heads/main by this push:
     new a8edb2f4a7 TOMEE-4332 Load SpringWebUtils in the context class loader in TomEE w… (#1166)
a8edb2f4a7 is described below

commit a8edb2f4a7588ee3ea266b69d42435ce27649ec2
Author: Jonathan Gallimore <jo...@jrg.me.uk>
AuthorDate: Wed Jul 3 19:43:23 2024 +0100

    TOMEE-4332 Load SpringWebUtils in the context class loader in TomEE w… (#1166)
    
    * TOMEE-4332 Load SpringWebUtils in the context class loader in TomEE when the cxf-rs service is initialized
    
    * Fix missing license headers
    
    ---------
    
    Co-authored-by: Richard Zowalla <rz...@apache.org>
---
 .../arquillian-tomee-jaxrs-tests/pom.xml           |  22 +++
 .../tests/jaxrs/spring/AlternativeGreeter.java     |  38 +++++
 .../tests/jaxrs/spring/DemoApplication.java        |  29 ++++
 .../tests/jaxrs/spring/GreetingController.java     |  35 +++++
 .../tests/jaxrs/spring/ServletInitializer.java     |  29 ++++
 .../tests/jaxrs/spring/SpringWebappTest.java       | 158 +++++++++++++++++++++
 .../src/test/resources/spring-webmvc-pom.xml       |  61 ++++++++
 .../apache/openejb/server/cxf/rs/CxfRSService.java |   6 +
 8 files changed, 378 insertions(+)

diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/pom.xml b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/pom.xml
index 6092894a7f..79e888a922 100644
--- a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/pom.xml
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/pom.xml
@@ -40,6 +40,28 @@
       <version>${project.version}</version>
       <type>war</type>
     </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-loader-tools</artifactId>
+      <version>3.1.11</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-web</artifactId>
+      <version>3.1.11</version>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-tomcat</artifactId>
+      <version>3.1.11</version>
+      <scope>provided</scope>
+    </dependency>
+    <dependency>
+      <groupId>org.springframework.boot</groupId>
+      <artifactId>spring-boot-starter-test</artifactId>
+      <version>3.1.11</version>
+      <scope>test</scope>
+    </dependency>
   </dependencies>
 
   <properties>
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/AlternativeGreeter.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/AlternativeGreeter.java
new file mode 100644
index 0000000000..1f260e02df
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/AlternativeGreeter.java
@@ -0,0 +1,38 @@
+/*
+ * 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.openejb.arquillian.tests.jaxrs.spring;
+
+import jakarta.ejb.Lock;
+import jakarta.ejb.LockType;
+import jakarta.ejb.Singleton;
+import jakarta.ws.rs.GET;
+import jakarta.ws.rs.Path;
+import jakarta.ws.rs.Produces;
+import jakarta.ws.rs.core.MediaType;
+
+@Singleton
+@Lock(LockType.READ)
+public class AlternativeGreeter {
+
+    @Path("hello")
+    @GET
+    @Produces(MediaType.TEXT_PLAIN)
+    public String sayHello() {
+        return "Hello World!";
+    }
+
+}
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/DemoApplication.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/DemoApplication.java
new file mode 100644
index 0000000000..6236078f62
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/DemoApplication.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.openejb.arquillian.tests.jaxrs.spring;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class DemoApplication {
+
+	public static void main(String[] args) {
+		SpringApplication.run(DemoApplication.class, args);
+	}
+
+}
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/GreetingController.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/GreetingController.java
new file mode 100644
index 0000000000..6cab6ff394
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/GreetingController.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.openejb.arquillian.tests.jaxrs.spring;
+
+import org.springframework.http.MediaType;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+@RequestMapping("/greet")
+public class GreetingController {
+
+    @GetMapping(produces = MediaType.TEXT_PLAIN_VALUE)
+    @ResponseBody
+    public String sayHello() {
+        return "Hello World!";
+    }
+
+}
\ No newline at end of file
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/ServletInitializer.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/ServletInitializer.java
new file mode 100644
index 0000000000..85291247c8
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/ServletInitializer.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.openejb.arquillian.tests.jaxrs.spring;
+
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+public class ServletInitializer extends SpringBootServletInitializer {
+
+	@Override
+	protected SpringApplicationBuilder configure(SpringApplicationBuilder application) {
+		return application.sources(DemoApplication.class);
+	}
+
+}
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/SpringWebappTest.java b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/SpringWebappTest.java
new file mode 100644
index 0000000000..9599c001d2
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/java/org/apache/openejb/arquillian/tests/jaxrs/spring/SpringWebappTest.java
@@ -0,0 +1,158 @@
+/**
+ * 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.openejb.arquillian.tests.jaxrs.spring;
+
+import org.apache.ziplock.WebModule;
+import org.jboss.arquillian.container.test.api.Deployment;
+import org.jboss.arquillian.junit.Arquillian;
+import org.jboss.arquillian.test.api.ArquillianResource;
+import org.jboss.shrinkwrap.api.ShrinkWrap;
+import org.jboss.shrinkwrap.api.exporter.ZipExporter;
+import org.jboss.shrinkwrap.api.spec.WebArchive;
+import org.jboss.shrinkwrap.resolver.api.ResolutionException;
+import org.jboss.shrinkwrap.resolver.api.ResolvedArtifact;
+import org.jboss.shrinkwrap.resolver.api.maven.Maven;
+import org.jboss.shrinkwrap.resolver.api.maven.MavenResolvedArtifact;
+import org.jboss.shrinkwrap.resolver.api.maven.ScopeType;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.springframework.boot.loader.tools.Libraries;
+import org.springframework.boot.loader.tools.Library;
+import org.springframework.boot.loader.tools.LibraryCallback;
+import org.springframework.boot.loader.tools.LibraryScope;
+import org.springframework.boot.loader.tools.Repackager;
+
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.stream.Collectors;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+@RunWith(Arquillian.class)
+public class SpringWebappTest {
+
+    @ArquillianResource
+    private URL url;
+
+    @Deployment(testable = false)
+    public static WebArchive getArchive() throws Exception {
+        MavenResolvedArtifact[] dependencies;
+        try { // try offline first since it is generally faster
+            dependencies = Maven.configureResolver()
+                    .workOffline()
+                    .loadPomFromFile("src/test/resources/spring-webmvc-pom.xml")
+                    .importCompileAndRuntimeDependencies().resolve().withTransitivity()
+                    .asResolvedArtifact();
+        } catch (ResolutionException re) { // try on central
+            dependencies = Maven.resolver()
+                    .loadPomFromFile("src/test/resources/spring-webmvc-pom.xml")
+                    .importCompileAndRuntimeDependencies().resolve().withTransitivity()
+                    .asResolvedArtifact();
+        }
+
+
+        final WebArchive archive = new WebModule(SpringWebappTest.class.getSimpleName()).getArchive();
+        archive.addClasses(AlternativeGreeter.class, DemoApplication.class, GreetingController.class, ServletInitializer.class);
+        archive.addAsLibraries(toFiles(dependencies));
+
+        // repackage as a Spring Boot WAR file
+        final File originalWarFile = File.createTempFile("test", ".war");
+        archive.as(ZipExporter.class).exportTo(originalWarFile, true);
+
+        final Repackager repackager = new Repackager(originalWarFile);
+        final File repackagedWarFile = File.createTempFile("repackaged", ".war");
+
+        repackager.repackage(repackagedWarFile, new MavenResolvedArtifactLibraries(dependencies));
+
+        final WebArchive repackaged = ShrinkWrap.createFromZipFile(WebArchive.class, repackagedWarFile);
+
+        System.out.println(repackaged.toString(true));
+        return repackaged;
+    }
+
+    private static File[] toFiles(MavenResolvedArtifact[] dependencies) {
+        return Arrays.stream(dependencies)
+                .map(ResolvedArtifact::asFile)
+                .collect(Collectors.toList())
+                .toArray(new File[dependencies.length]);
+    }
+
+    @Test
+    public void validate() throws Exception {
+        final String launchProfile = System.getProperty("arquillian.launch");
+        if ("tomee-embedded".equals(launchProfile)) {
+            System.out.println("Skipping this test in TomEE embedded");
+            return;
+        }
+
+        final InputStream is = new URL(url.toExternalForm() + "hello").openStream();
+        final ByteArrayOutputStream os = new ByteArrayOutputStream();
+
+        int bytesRead;
+        byte[] buffer = new byte[8192];
+        while ((bytesRead = is.read(buffer)) > -1) {
+            os.write(buffer, 0, bytesRead);
+        }
+
+        is.close();
+        os.close();
+
+        final String output = new String(os.toByteArray(), "UTF-8");
+        assertNotNull("Response shouldn't be null", output);
+        assertTrue("Output should contain: " + "Hello World!" + "\n" + output, output.contains("Hello World!"));
+    }
+
+    public static class MavenResolvedArtifactLibraries implements Libraries {
+
+        private final MavenResolvedArtifact[] artifacts;
+
+        public MavenResolvedArtifactLibraries(final MavenResolvedArtifact[] artifacts) {
+            this.artifacts = artifacts;
+        }
+
+        @Override
+        public void doWithLibraries(final LibraryCallback callback) throws IOException {
+            for (final MavenResolvedArtifact artifact : artifacts) {
+                callback.library(new Library(artifact.asFile(), toScope(artifact)));
+            }
+        }
+
+        private LibraryScope toScope(MavenResolvedArtifact artifact) {
+            final ScopeType scope = artifact.getScope();
+            if (ScopeType.COMPILE.equals(scope)) {
+                return LibraryScope.COMPILE;
+            } else if (ScopeType.RUNTIME.equals(scope)) {
+                return LibraryScope.COMPILE;
+            } else if (ScopeType.TEST.equals(scope)) {
+                return LibraryScope.PROVIDED;
+            } else if (ScopeType.PROVIDED.equals(scope)) {
+                return LibraryScope.PROVIDED;
+            } else if (ScopeType.SYSTEM.equals(scope)) {
+                return LibraryScope.CUSTOM;
+            } else if (ScopeType.IMPORT.equals(scope)) {
+                return LibraryScope.CUSTOM;
+            }
+
+            throw new IllegalStateException("Unsupported scope: " + scope);
+        }
+    }
+}
diff --git a/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/resources/spring-webmvc-pom.xml b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/resources/spring-webmvc-pom.xml
new file mode 100644
index 0000000000..3706e818cc
--- /dev/null
+++ b/arquillian/arquillian-tomee-tests/arquillian-tomee-jaxrs-tests/src/test/resources/spring-webmvc-pom.xml
@@ -0,0 +1,61 @@
+<?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.
+-->
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
+    <modelVersion>4.0.0</modelVersion>
+    <parent>
+        <groupId>org.springframework.boot</groupId>
+        <artifactId>spring-boot-starter-parent</artifactId>
+        <version>3.1.11</version>
+        <relativePath/> <!-- lookup parent from repository -->
+    </parent>
+    <groupId>org.apache.openejb.arquillian.tests</groupId>
+    <artifactId>spring-webmvc-deps</artifactId>
+    <version>0.0.1-SNAPSHOT</version>
+    <packaging>war</packaging>
+    <dependencies>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-web</artifactId>
+            <exclusions>
+                <exclusion>
+                    <groupId>org.apache.tomcat.embed</groupId>
+                    <artifactId>*</artifactId>
+                </exclusion>
+            </exclusions>
+        </dependency>
+
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-tomcat</artifactId>
+            <scope>provided</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.springframework.boot</groupId>
+            <artifactId>spring-boot-starter-test</artifactId>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.tomee</groupId>
+            <artifactId>jakartaee-api</artifactId>
+            <version>10.0-M2</version>
+            <scope>provided</scope>
+        </dependency>
+    </dependencies>
+</project>
diff --git a/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java b/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java
index f8f78eb1b2..1035eca7fe 100644
--- a/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java
+++ b/server/openejb-cxf-rs/src/main/java/org/apache/openejb/server/cxf/rs/CxfRSService.java
@@ -167,6 +167,12 @@ public class CxfRSService extends RESTService {
         config = properties;
         factoryByListener = "true".equalsIgnoreCase(properties.getProperty("openejb.cxf-rs.factoryByListener", "false"));
 
+        try {
+            Class.forName("org.apache.cxf.jaxrs.springmvc.SpringWebUtils");
+        } catch (Throwable t) {
+            // ignore
+        }
+
         System.setProperty("org.apache.johnzon.max-string-length",
                 SystemInstance.get().getProperty("org.apache.johnzon.max-string-length",
                         properties.getProperty("org.apache.johnzon.max-string-length", "8192")));