aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorChristof Rabensteiner <christof.rabensteiner@iaik.tugraz.at>2019-08-22 10:05:03 +0200
committerChristof Rabensteiner <christof.rabensteiner@iaik.tugraz.at>2019-08-23 15:17:14 +0200
commite6e0950a506d814415d8036e68e54d2034b6d3e7 (patch)
tree8dc0f1c57f063f639ad884a03f951ea9a9f2fb7c
parent763724b04d25d07fce5559c1d7a6c12badab937c (diff)
downloadmoa-zs-e6e0950a506d814415d8036e68e54d2034b6d3e7.tar.gz
moa-zs-e6e0950a506d814415d8036e68e54d2034b6d3e7.tar.bz2
moa-zs-e6e0950a506d814415d8036e68e54d2034b6d3e7.zip
Fix: JAXB + Java >= 9 Class Loader Bug & Ensure Tomcat Deployment
- Problem: Jaxb + JAVA >= 9 Runtime + Tomcat = ClassNotFoundException: ContextFactory. Reason: Apparently, jaxb uses the "wrong" classloader (the system classloader via the thread classloader) and this classloader does not know about jaxb api's and implementations at runtime since oracle decided to move jaxb* out of JRE. Solution: create a new thread, override the thread's "thread" classloader with the "class" classloader and do all jaxb interactions in that thread. See: https://sjhannah.com/blog/2018/11/21/jaxb-hell-on-jdk-9/ - Move "run jaxb interactions in own thread with class class loader" code into own component (JaxbClassNotFoundFix, runInTheadWithClassClassLoader) and wrap ClientFactory and Marshaller code into the "runInTheadWithClassClassLoader". - Ensure that app can be deployed in a Tomcat container (by following this guide: https://www.baeldung.com/spring-boot-war-tomcat-deploy)
-rw-r--r--pom.xml38
-rw-r--r--src/main/java/at/gv/egiz/moazs/MoaZS.java12
-rw-r--r--src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java5
-rw-r--r--src/main/java/at/gv/egiz/moazs/client/ClientFactory.java5
-rw-r--r--src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java13
-rw-r--r--src/main/java/at/gv/egiz/moazs/util/JAXBClassNotFoundFix.java55
6 files changed, 102 insertions, 26 deletions
diff --git a/pom.xml b/pom.xml
index 62a5bdf..a8191bf 100644
--- a/pom.xml
+++ b/pom.xml
@@ -92,11 +92,11 @@
<scope>test</scope>
</dependency>
<dependency>
- <groupId>org.springframework.boot</groupId>
- <artifactId>spring-boot-starter-tomcat</artifactId>
+ <groupId>org.springframework.boot</groupId>
+ <artifactId>spring-boot-starter-tomcat</artifactId>
<version>${springboot.version}</version>
- <scope>provided</scope>
- </dependency>
+ <scope>provided</scope>
+ </dependency>
<!-- cxf -->
<dependency>
<groupId>org.apache.cxf</groupId>
@@ -143,23 +143,23 @@
<version>${jwsapi.version}</version>
</dependency>
<!-- eaaf components. See readme.md for installation -->
- <dependency>
+ <dependency>
+ <groupId>at.gv.egiz.eaaf</groupId>
+ <artifactId>eaaf_module_moa-sig</artifactId>
+ <version>${eaaf-components.version}</version>
+ </dependency>
+ <dependency>
<groupId>at.gv.egiz.eaaf</groupId>
- <artifactId>eaaf_module_moa-sig</artifactId>
+ <artifactId>eaaf-core</artifactId>
<version>${eaaf-components.version}</version>
- </dependency>
- <dependency>
- <groupId>at.gv.egiz.eaaf</groupId>
- <artifactId>eaaf-core</artifactId>
- <version>${eaaf-components.version}</version>
- </dependency>
- <!-- async operation in test -->
- <dependency>
- <groupId>org.awaitility</groupId>
- <artifactId>awaitility</artifactId>
- <version>3.1.6</version>
- <scope>test</scope>
- </dependency>
+ </dependency>
+ <!-- async operation in test -->
+ <dependency>
+ <groupId>org.awaitility</groupId>
+ <artifactId>awaitility</artifactId>
+ <version>3.1.6</version>
+ <scope>test</scope>
+ </dependency>
</dependencies>
<build>
<finalName>moa-zs</finalName>
diff --git a/src/main/java/at/gv/egiz/moazs/MoaZS.java b/src/main/java/at/gv/egiz/moazs/MoaZS.java
index 3009745..e4e4acc 100644
--- a/src/main/java/at/gv/egiz/moazs/MoaZS.java
+++ b/src/main/java/at/gv/egiz/moazs/MoaZS.java
@@ -3,9 +3,19 @@ package at.gv.egiz.moazs;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.builder.SpringApplicationBuilder;
+import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;
+
+import java.net.URL;
+import java.net.URLClassLoader;
@SpringBootApplication
-public class MoaZS {
+public class MoaZS extends SpringBootServletInitializer {
+
+ @Override
+ protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
+ return builder.sources(MoaZS.class);
+ }
public static void main(String[] args) {
SpringApplication.run(MoaZS.class, args);
diff --git a/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java b/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java
index 72f8ba0..46b0c2c 100644
--- a/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java
+++ b/src/main/java/at/gv/egiz/moazs/backend/DeliveryRequestBackend.java
@@ -11,7 +11,8 @@ import at.gv.zustellung.app2mzs.xsd.DeliveryRequestType;
import at.gv.zustellung.msg.xsd.App2ZusePort;
import at.gv.zustellung.msg.xsd.DeliveryRequestStatusType;
import at.gv.zustellung.tnvz.xsd.TNVZServicePort;
-import org.apache.log4j.Logger;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Profile;
import org.springframework.stereotype.Component;
@@ -28,7 +29,7 @@ import static java.lang.String.format;
@Profile("!cluster")
public class DeliveryRequestBackend implements Consumer<String> {
- private static final Logger log = Logger.getLogger(DeliveryRequestBackend.class);
+ private static final Logger log = LoggerFactory.getLogger(DeliveryRequestBackend.class);
private static final String MZS_PIPELINE_ERROR_MSG =
"An error occured while processing the DeliveryRequest with AppDeliveryID=%s. ";
private static final String DELIVERY_REQUEST_MISSING_ERROR_MSG =
diff --git a/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java b/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java
index 94eb712..818c4c9 100644
--- a/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java
+++ b/src/main/java/at/gv/egiz/moazs/client/ClientFactory.java
@@ -2,6 +2,7 @@ package at.gv.egiz.moazs.client;
import at.gv.egiz.moazs.preprocess.ConfigUtil;
import at.gv.egiz.moazs.util.FileUtils;
+import at.gv.egiz.moazs.util.JAXBClassNotFoundFix;
import at.gv.egiz.moazs.util.StoreSOAPBodyBinaryInRepositoryInterceptor;
import at.gv.zustellung.app2mzs.xsd.ClientType;
import at.gv.zustellung.app2mzs.xsd.KeyStoreType;
@@ -25,7 +26,6 @@ import javax.xml.ws.BindingProvider;
import javax.xml.ws.soap.SOAPBinding;
import static at.gv.zustellung.app2mzs.xsd.KeyStoreType.keyStoreTypeBuilder;
-import static java.util.stream.Collectors.toMap;
@Component
public class ClientFactory {
@@ -54,7 +54,10 @@ public class ClientFactory {
* @return the client
*/
public <T> T create(ClientType params, Class<T> clazz) {
+ return JAXBClassNotFoundFix.runInTheadWithClassClassLoader(() -> createClient(params, clazz));
+ }
+ private <T> T createClient(ClientType params, Class<T> clazz) {
var factory = new JaxWsClientFactoryBean();
factory.setServiceClass(clazz);
factory.setAddress(params.getURL());
diff --git a/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java b/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java
index 1a86079..bf502c0 100644
--- a/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java
+++ b/src/main/java/at/gv/egiz/moazs/scheme/Marshaller.java
@@ -9,6 +9,8 @@ import java.io.InputStream;
import java.io.StringWriter;
import java.util.HashMap;
+import static at.gv.egiz.moazs.util.JAXBClassNotFoundFix.runInTheadWithClassClassLoader;
+
/**
* @author xerx593
* Source: https://stackoverflow.com/questions/44676532/how-to-use-spring-to-marshal-and-unmarshal-xml
@@ -32,17 +34,22 @@ public class Marshaller {
}
public <T> String marshallXml(final T obj) {
+
if (obj == null) {
return "null";
}
StringWriter sw = new StringWriter();
Result result = new StreamResult(sw);
- jaxbMarshaller.marshal(obj, result);
- return sw.toString();
+
+ return runInTheadWithClassClassLoader(() -> {
+ jaxbMarshaller.marshal(obj, result);
+ return sw.toString();
+ });
}
public <T> T unmarshallXml(final InputStream xml) {
- return (T) jaxbMarshaller.unmarshal(new StreamSource(xml));
+ return runInTheadWithClassClassLoader(
+ () -> (T) jaxbMarshaller.unmarshal(new StreamSource(xml)));
}
}
diff --git a/src/main/java/at/gv/egiz/moazs/util/JAXBClassNotFoundFix.java b/src/main/java/at/gv/egiz/moazs/util/JAXBClassNotFoundFix.java
new file mode 100644
index 0000000..9e72286
--- /dev/null
+++ b/src/main/java/at/gv/egiz/moazs/util/JAXBClassNotFoundFix.java
@@ -0,0 +1,55 @@
+package at.gv.egiz.moazs.util;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.function.Supplier;
+
+import static at.gv.egiz.moazs.MoaZSException.moaZSException;
+
+@Component
+public class JAXBClassNotFoundFix {
+
+ private static final Logger log = LoggerFactory.getLogger(JAXBClassNotFoundFix.class);
+
+ /**
+ * Solves JAXB's "ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory" in Java >= 9
+ * by running func in a dedicated thread which has its context classloader replaced with a classloader that finds
+ * the ContextFactory.
+ *
+ * See https://sjhannah.com/blog/2018/11/21/jaxb-hell-on-jdk-9/
+ * Note: "runInTheadWithClassClassLoader" is not a typo.
+ * @param func function that is called in a dedicated thread.
+ * @param <A> return value of func.
+ * @return
+ */
+ public static <A> A runInTheadWithClassClassLoader(Supplier<A> func) {
+
+ final Object[] result = new Object[1];
+ final Exception[] error = new Exception[1];
+
+ Thread t = new Thread(() -> {
+ try {
+ result[0] = func.get();
+ } catch (Exception ex) {
+ error[0] = ex;
+ }
+ });
+
+ t.setContextClassLoader(JAXBClassNotFoundFix.class.getClassLoader());
+ t.start();
+
+ try {
+ t.join();
+ } catch (InterruptedException ex) {
+ log.error("Interrupted. ", ex);
+ }
+
+ if (error[0] != null) {
+ throw moaZSException("Could not run function.", error[0]);
+ }
+
+ return (A)result[0];
+ }
+}