How to convert XML to String with JAXB and spring-boot?
When I run a mvn spring-boot:run
on the folder that has the pom.xml file the application starts and serializes a POJO into a XML correctly, but when I do it by going to the target folder and starting it by using java -jar
in the jar file I get javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath
caused by .java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
.
In my maven I have the following JAXB dependencies:
<dependency>
<groupId>javax.xml.bind</groupId>
<artifactId>jaxb-api</artifactId>
<version>2.3.1</version>
</dependency>
<dependency>
<groupId>org.glassfish.jaxb</groupId>
<artifactId>jaxb-runtime</artifactId>
<version>2.3.1</version>
<scope>runtime</scope>
</dependency>
Here's the method that serializes a POJO into XML:
private static final Pattern REMOVE_HEADER = Pattern.compile("\\<\\?xml(.+?)\\?\\>");
public static String toXML(final Object data) {
try {
final var jaxbMarshaller = JAXBContext.newInstance(data.getClass()).createMarshaller();
jaxbMarshaller.setProperty(Marshaller.JAXB_FORMATTED_OUTPUT, Boolean.TRUE);
final var sw = new StringWriter();
jaxbMarshaller.marshal(data, sw);
return XMLUtils.REMOVE_HEADER.matcher(sw.toString()).replaceAll(StringUtils.EMPTY).strip();
} catch (final JAXBException e) {
XMLUtils.LOGGER.error("Error while converting POJO to XML. ERROR: {}.", e.getMessage(), e);
}
return "";
}
Here's the log when I start the application with java -jar:
javax.xml.bind.JAXBException: Implementation of JAXB-API has not been found on module path or classpath.
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:278)
at javax.xml.bind.ContextFinder.find(ContextFinder.java:421)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:721)
at javax.xml.bind.JAXBContext.newInstance(JAXBContext.java:662)
at br.com.site.system.shared.util.XMLUtils.toXML(XMLUtils.java:56)
at br.com.site.system.shared.util.FinanTokenEncoderUtils.encodeBase64(FinanTokenEncoderUtils.java:41)
at br.com.site.system.converter.PlanConverter.lambda$convert$0(PlanConverter.java:84)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:952)
at java.base/java.util.stream.ReduceOps$ReduceTask.doLeaf(ReduceOps.java:926)
at java.base/java.util.stream.AbstractTask.compute(AbstractTask.java:327)
at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.helpCC(ForkJoinPool.java:1115)
at java.base/java.util.concurrent.ForkJoinPool.awaitJoin(ForkJoinPool.java:1687)
at java.base/java.util.concurrent.ForkJoinTask.doInvoke(ForkJoinTask.java:411)
at java.base/java.util.concurrent.ForkJoinTask.invoke(ForkJoinTask.java:736)
at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateParallel(ReduceOps.java:919)
at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:233)
at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:578)
at br.com.site.system.converter.PlanConverter.convert(PlanConverter.java:90)
at br.com.site.system.converter.OperationConverter.lambda$convert$0(OperationConverter.java:59)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:195)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1654)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:484)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:474)
at java.base/java.util.stream.Nodes$SizedCollectorTask.compute(Nodes.java:1886)
at java.base/java.util.concurrent.CountedCompleter.exec(CountedCompleter.java:746)
at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:290)
at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1020)
at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1656)
at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1594)
at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:177)
Caused by: java.lang.ClassNotFoundException: com.sun.xml.internal.bind.v2.ContextFactory
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:583)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:521)
at javax.xml.bind.ServiceLoaderUtil.nullSafeLoadClass(ServiceLoaderUtil.java:122)
at javax.xml.bind.ServiceLoaderUtil.safeLoadClass(ServiceLoaderUtil.java:155)
at javax.xml.bind.ContextFinder.newInstance(ContextFinder.java:276)
... 35 common frames omitted
Here's an image of the generated spring-boot fat jar with the JAXB dependencies:
And finally here's my dependency tree:
--- maven-dependency-plugin:3.2.0:tree (default-cli) @ system-infrastructure ---
br.com.site.system:system-infrastructure:jar:0.0.1-SNAPSHOT
+- br.com.site.system:system-core:jar:0.0.1-SNAPSHOT:compile
| +- org.apache.commons:commons-lang3:jar:3.12.0:compile
| +- javax.validation:validation-api:jar:2.0.1.Final:compile
| +- javax.xml.bind:jaxb-api:jar:2.3.1:compile
| | \- javax.activation:javax.activation-api:jar:1.2.0:compile
| \- org.glassfish.jaxb:jaxb-runtime:jar:2.3.5:runtime
| +- org.glassfish.jaxb:txw2:jar:2.3.5:runtime
| +- com.sun.istack:istack-commons-runtime:jar:3.0.12:runtime
| \- com.sun.activation:jakarta.activation:jar:1.2.2:runtime
+- io.projectreactor:reactor-test:jar:3.4.12:test
| \- io.projectreactor:reactor-core:jar:3.4.12:compile
| \- org.reactivestreams:reactive-streams:jar:1.0.3:compile
+- org.junit.jupiter:junit-jupiter-api:jar:5.8.1:test
| +- org.opentest4j:opentest4j:jar:1.2.0:test
| +- org.junit.platform:junit-platform-commons:jar:1.8.1:test
| \- org.apiguardian:apiguardian-api:jar:1.1.2:test
+- org.mockito:mockito-inline:jar:4.0.0:test
| \- org.mockito:mockito-core:jar:4.0.0:test
| +- net.bytebuddy:byte-buddy:jar:1.11.22:test
| +- net.bytebuddy:byte-buddy-agent:jar:1.11.22:test
| \- org.objenesis:objenesis:jar:3.2:test
+- org.pitest:pitest-junit5-plugin:jar:0.15:test
+- org.springframework.boot:spring-boot-starter-test:jar:2.6.1:test
| +- org.springframework.boot:spring-boot-starter:jar:2.6.1:compile
| | +- org.springframework.boot:spring-boot:jar:2.6.1:compile
| | | \- org.springframework:spring-context:jar:5.3.13:compile
| | | +- org.springframework:spring-aop:jar:5.3.13:compile
| | | \- org.springframework:spring-expression:jar:5.3.13:compile
| | +- org.springframework.boot:spring-boot-autoconfigure:jar:2.6.1:compile
| | +- org.springframework.boot:spring-boot-starter-logging:jar:2.6.1:compile
| | | +- ch.qos.logback:logback-classic:jar:1.2.7:compile
| | | | \- ch.qos.logback:logback-core:jar:1.2.7:compile
| | | +- org.apache.logging.log4j:log4j-to-slf4j:jar:2.14.1:compile
| | | | \- org.apache.logging.log4j:log4j-api:jar:2.14.1:compile
| | | \- org.slf4j:jul-to-slf4j:jar:1.7.32:compile
| | +- jakarta.annotation:jakarta.annotation-api:jar:1.3.5:compile
| | \- org.yaml:snakeyaml:jar:1.29:compile
| +- org.springframework.boot:spring-boot-test:jar:2.6.1:test
| +- org.springframework.boot:spring-boot-test-autoconfigure:jar:2.6.1:test
| +- com.jayway.jsonpath:json-path:jar:2.6.0:test
| | +- net.minidev:json-smart:jar:2.4.7:test
| | | \- net.minidev:accessors-smart:jar:2.4.7:test
| | | \- org.ow2.asm:asm:jar:9.1:test
| | \- org.slf4j:slf4j-api:jar:1.7.32:compile
| +- jakarta.xml.bind:jakarta.xml.bind-api:jar:2.3.3:compile
| | \- jakarta.activation:jakarta.activation-api:jar:1.2.2:compile
| +- org.assertj:assertj-core:jar:3.21.0:test
| +- org.hamcrest:hamcrest:jar:2.2:test
| +- org.junit.jupiter:junit-jupiter:jar:5.8.1:test
| | +- org.junit.jupiter:junit-jupiter-params:jar:5.8.1:test
| | \- org.junit.jupiter:junit-jupiter-engine:jar:5.8.1:test
| | \- org.junit.platform:junit-platform-engine:jar:1.8.1:test
| +- org.mockito:mockito-junit-jupiter:jar:4.0.0:test
| +- org.skyscreamer:jsonassert:jar:1.5.0:test
| | \- com.vaadin.external.google:android-json:jar:0.0.20131108.vaadin1:test
| +- org.springframework:spring-core:jar:5.3.13:compile
| | \- org.springframework:spring-jcl:jar:5.3.13:compile
| +- org.springframework:spring-test:jar:5.3.13:test
| \- org.xmlunit:xmlunit-core:jar:2.8.3:test
+- org.springframework.boot:spring-boot-starter-actuator:jar:2.6.1:compile
| +- org.springframework.boot:spring-boot-actuator-autoconfigure:jar:2.6.1:compile
| | +- org.springframework.boot:spring-boot-actuator:jar:2.6.1:compile
| | \- com.fasterxml.jackson.datatype:jackson-datatype-jsr310:jar:2.13.0:compile
| \- io.micrometer:micrometer-core:jar:1.8.0:compile
| +- org.hdrhistogram:HdrHistogram:jar:2.1.12:compile
| \- org.latencyutils:LatencyUtils:jar:2.0.3:runtime
+- org.springframework.boot:spring-boot-starter-validation:jar:2.6.1:compile
| +- org.apache.tomcat.embed:tomcat-embed-el:jar:9.0.55:compile
| \- org.hibernate.validator:hibernate-validator:jar:6.2.0.Final:compile
| +- jakarta.validation:jakarta.validation-api:jar:2.0.2:compile
| +- org.jboss.logging:jboss-logging:jar:3.4.2.Final:compile
| \- com.fasterxml:classmate:jar:1.5.1:compile
+- org.springframework.boot:spring-boot-starter-webflux:jar:2.6.1:compile
| +- org.springframework.boot:spring-boot-starter-json:jar:2.6.1:compile
| | +- com.fasterxml.jackson.datatype:jackson-datatype-jdk8:jar:2.13.0:compile
| | \- com.fasterxml.jackson.module:jackson-module-parameter-names:jar:2.13.0:compile
| +- org.springframework.boot:spring-boot-starter-reactor-netty:jar:2.6.1:compile
| | \- io.projectreactor.netty:reactor-netty-http:jar:1.0.13:compile
| | +- io.netty:netty-codec-http:jar:4.1.70.Final:compile
| | | +- io.netty:netty-common:jar:4.1.70.Final:compile
| | | +- io.netty:netty-buffer:jar:4.1.70.Final:compile
| | | +- io.netty:netty-transport:jar:4.1.70.Final:compile
| | | +- io.netty:netty-codec:jar:4.1.70.Final:compile
| | | \- io.netty:netty-handler:jar:4.1.70.Final:compile
| | +- io.netty:netty-codec-http2:jar:4.1.70.Final:compile
| | +- io.netty:netty-resolver-dns:jar:4.1.70.Final:compile
| | | +- io.netty:netty-resolver:jar:4.1.70.Final:compile
| | | \- io.netty:netty-codec-dns:jar:4.1.70.Final:compile
| | +- io.netty:netty-resolver-dns-native-macos:jar:osx-x86_64:4.1.70.Final:compile
| | | \- io.netty:netty-resolver-dns-classes-macos:jar:4.1.70.Final:compile
| | +- io.netty:netty-transport-native-epoll:jar:linux-x86_64:4.1.70.Final:compile
| | | +- io.netty:netty-transport-native-unix-common:jar:4.1.70.Final:compile
| | | \- io.netty:netty-transport-classes-epoll:jar:4.1.70.Final:compile
| | \- io.projectreactor.netty:reactor-netty-core:jar:1.0.13:compile
| | \- io.netty:netty-handler-proxy:jar:4.1.70.Final:compile
| | \- io.netty:netty-codec-socks:jar:4.1.70.Final:compile
| +- org.springframework:spring-web:jar:5.3.13:compile
| | \- org.springframework:spring-beans:jar:5.3.13:compile
| \- org.springframework:spring-webflux:jar:5.3.13:compile
+- net.logstash.logback:logstash-logback-encoder:jar:7.0.1:compile
| \- com.fasterxml.jackson.core:jackson-databind:jar:2.13.0:compile
| +- com.fasterxml.jackson.core:jackson-annotations:jar:2.13.0:compile
| \- com.fasterxml.jackson.core:jackson-core:jar:2.13.0:compile
+- org.springdoc:springdoc-openapi-webflux-ui:jar:1.6.1:compile
| +- org.springdoc:springdoc-openapi-webflux-core:jar:1.6.1:compile
| | \- org.springdoc:springdoc-openapi-common:jar:1.6.1:compile
| | +- io.swagger.core.v3:swagger-models:jar:2.1.11:compile
| | +- io.swagger.core.v3:swagger-annotations:jar:2.1.11:compile
| | +- io.swagger.core.v3:swagger-integration:jar:2.1.11:compile
| | | \- io.swagger.core.v3:swagger-core:jar:2.1.11:compile
| | | \- com.fasterxml.jackson.dataformat:jackson-dataformat-yaml:jar:2.13.0:compile
| | \- io.github.classgraph:classgraph:jar:4.8.116:compile
| +- org.webjars:swagger-ui:jar:4.1.3:compile
| \- org.webjars:webjars-locator-core:jar:0.48:compile
\- org.mapstruct:mapstruct:jar:1.4.2.Final:compile
And finally, here are my model classes:
import java.util.Objects;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlElement;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "finans")
@XmlAccessorType(XmlAccessType.FIELD)
public class FinanTokenDTO {
@XmlAttribute
private String pln;
@XmlAttribute
private String ope;
@XmlAttribute
private String mod;
@XmlAttribute
private String mis;
@XmlAttribute
private String val;
@XmlAttribute
private String car;
@XmlAttribute
private String dti;
@XmlAttribute
private String dtf;
@XmlAttribute
private String ota;
@XmlElement
private FinanDTO finan;
public FinanTokenDTO() {
super();
}
public FinanTokenDTO(final String plnParam, final String opeParam, final String modParam, final String misParam,
final String valParam, final String carParam, final String dtiParam, final String dtfParam,
final String otaParam, final FinanDTO finanParam) {
this();
this.pln = plnParam;
this.ope = opeParam;
this.mod = modParam;
this.mis = misParam;
this.val = valParam;
this.car = carParam;
this.dti = dtiParam;
this.dtf = dtfParam;
this.ota = otaParam;
this.finan = finanParam;
}
public String getPln() {
return this.pln;
}
public void setPln(final String plnParam) {
this.pln = plnParam;
}
public String getOpe() {
return this.ope;
}
public void setOpe(final String opeParam) {
this.ope = opeParam;
}
public String getMod() {
return this.mod;
}
public void setMod(final String modParam) {
this.mod = modParam;
}
public String getMis() {
return this.mis;
}
public void setMis(final String misParam) {
this.mis = misParam;
}
public String getVal() {
return this.val;
}
public void setVal(final String valParam) {
this.val = valParam;
}
public String getCar() {
return this.car;
}
public void setCar(final String carParam) {
this.car = carParam;
}
public String getDti() {
return this.dti;
}
public void setDti(final String dtiParam) {
this.dti = dtiParam;
}
public String getDtf() {
return this.dtf;
}
public void setDtf(final String dtfParam) {
this.dtf = dtfParam;
}
public String getOta() {
return this.ota;
}
public void setOta(final String otaParam) {
this.ota = otaParam;
}
public FinanDTO getFinan() {
return this.finan;
}
public void setFinan(final FinanDTO finanParam) {
this.finan = finanParam;
}
@Override
public int hashCode() {
return Objects.hash(this.car, this.dtf, this.dti, this.finan, this.mis, this.mod, this.ope, this.ota, this.pln,
this.val);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
final var other = (FinanTokenDTO) obj;
return Objects.equals(this.car, other.car) && Objects.equals(this.dtf, other.dtf)
&& Objects.equals(this.dti, other.dti) && Objects.equals(this.finan, other.finan)
&& Objects.equals(this.mis, other.mis) && Objects.equals(this.mod, other.mod)
&& Objects.equals(this.ope, other.ope) && Objects.equals(this.ota, other.ota)
&& Objects.equals(this.pln, other.pln) && Objects.equals(this.val, other.val);
}
@Override
public String toString() {
return new StringBuilder().append("FinanTokenDTO [pln=").append(this.pln).append(", ope=").append(this.ope)
.append(", mod=").append(this.mod).append(", mis=").append(this.mis).append(", val=").append(this.val)
.append(", car=").append(this.car).append(", dti=").append(this.dti).append(", dtf=").append(this.dtf)
.append(", ota=").append(this.ota).append(", finan=").append(this.finan)
.append('}').toString();
}
import java.util.Objects;
import javax.xml.bind.annotation.XmlAccessType;
import javax.xml.bind.annotation.XmlAccessorType;
import javax.xml.bind.annotation.XmlAttribute;
import javax.xml.bind.annotation.XmlRootElement;
@XmlRootElement(name = "finan")
@XmlAccessorType(XmlAccessType.FIELD)
public class FinanDTO {
@XmlAttribute
private String prd;
@XmlAttribute
private String pkg;
@XmlAttribute
private String val;
@XmlAttribute
private String fty;
@XmlAttribute
private String safetyPay;
public FinanDTO() {
super();
}
public FinanDTO(final String prdParam, final String pkgParam, final String valParam, final String ftyParam,
final String safetypayParam) {
this();
this.prd = prdParam;
this.pkg = pkgParam;
this.val = valParam;
this.fty = ftyParam;
this.safetyPay = safetypayParam;
}
public String getPrd() {
return this.prd;
}
public void setPrd(final String prdParam) {
this.prd = prdParam;
}
public String getPkg() {
return this.pkg;
}
public void setPkg(final String pkgParam) {
this.pkg = pkgParam;
}
public String getVal() {
return this.val;
}
public void setVal(final String valParam) {
this.val = valParam;
}
public String getFty() {
return this.fty;
}
public void setFty(final String ftyParam) {
this.fty = ftyParam;
}
public String getSafetyPay() {
return this.safetyPay;
}
public void setSafetyPay(final String safetyPayParam) {
this.safetyPay = safetyPayParam;
}
@Override
public int hashCode() {
return Objects.hash(this.fty, this.pkg, this.prd, this.safetyPay, this.val);
}
@Override
public boolean equals(final Object obj) {
if (this == obj) {
return true;
}
if (obj == null || this.getClass() != obj.getClass()) {
return false;
}
final var other = (FinanDTO) obj;
return Objects.equals(this.fty, other.fty) && Objects.equals(this.pkg, other.pkg)
&& Objects.equals(this.prd, other.prd) && Objects.equals(this.safetyPay, other.safetyPay)
&& Objects.equals(this.val, other.val);
}
@Override
public String toString() {
return new StringBuilder().append("FinanDTO [prd=").append(this.prd).append(", pkg=").append(this.pkg)
.append(", val=").append(this.val).append(", fty=").append(this.fty).append(", safetyPay=")
.append(this.safetyPay).append('}').toString();
}
}
I am using java 11 and I am aware that in java 11 JAXB was removed from the SE JDK because it is considered as a EE feature.
I am not able to execute it using mvn spring-boot:run
in production environment because of the size of the docker image and security related issues using docker container.
Since Spring Boot generates a fat jar, shouldn't the application run with java -jar
applied to the spring boot fat jar generated file the same way as it does with mvn spring-boot:run
inside the folder with pom.xml?
EDIT:
After a lot of digging and testing I found that the problem occurs when we try to marshall several valid POJOs using parallel stream instead of stream on the POJOs list. I uploaded the code in java-eleven-jaxb-hell to better understanding, unfortunatelly I cannot change parallelStream to stream because of performance issues. Just to remember, for the problem to happen you have to run java -jar
against spring-boot generated fat jar in target folder.
from Recent Questions - Stack Overflow https://ift.tt/3mzDBuz
https://ift.tt/3pul0SH
Comments
Post a Comment