When I try to send message through the JmsTemplate, Jaxb2Marshaller throws an error while attempting to conver obj to xml

Viewed 18

I try to get hands on expirience with jaxb library to parse xml. Here is my code:

JmsConfig.class

@Configuration
public class JmsConfig {
    @Value("${context.path}")
    String contextPath;
    @Value("${schema.location}")
    FileSystemResource schema;

    @Bean
    public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
        DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
        factory.setConnectionFactory(connectionFactory);
        factory.setMessageConverter(marshallingMessageConverter());
        return factory;
    }

    @Bean
    public MessageConverter marshallingMessageConverter() {
        MarshallingMessageConverter marshallingMessageConverter = new MarshallingMessageConverter();
        marshallingMessageConverter.setMarshaller(jaxb2Marshaller());
        marshallingMessageConverter.setUnmarshaller(jaxb2Unmarshaller());
        return marshallingMessageConverter;
    }

    @Bean
    public Marshaller jaxb2Marshaller() {
        return getJaxb2Marshaller();
    }

    @Bean
    public Unmarshaller jaxb2Unmarshaller() {
        return getJaxb2Marshaller();
    }

    private Jaxb2Marshaller getJaxb2Marshaller() {
        Jaxb2Marshaller jaxb2Marshaller = new Jaxb2Marshaller();
        jaxb2Marshaller.setContextPath(contextPath);
        jaxb2Marshaller.setSchema(schema);
        jaxb2Marshaller.setMarshallerProperties(of(JAXB_FORMATTED_OUTPUT, true));
        return jaxb2Marshaller;
    }

}

JmsApplication

@SpringBootApplication
public class JmsApplication implements CommandLineRunner {

    public static void main(String[] args) {
        SpringApplication.run(JmsApplication.class, args);
    }

    @Autowired
    private JmsTemplate jmsTemplate;

    @Value("${outbound.endpoint}")
    private String destination;

    @Override
    public void run(String... args) throws Exception {
        ObjectFactory of = new ObjectFactory();
        ProductDto productDto = of.createProductDto();

        productDto.setProductId(UUID.randomUUID().toString());
        productDto.setProductName("Cool product");

        JAXB.marshal(productDto, System.out);

        jmsTemplate.convertAndSend(destination, productDto);
    }

}

POM

<?xml version="1.0" encoding="UTF-8"?>
<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>2.7.3</version>
        <relativePath/>
    </parent>

    <groupId>com.mikhail</groupId>
    <artifactId>JMS</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>JMS</name>
    <description>JMS</description>
    <properties>
        <java.version>11</java.version>
        <maven-jaxb2-plugin.version>0.15.1</maven-jaxb2-plugin.version>
        <xml-bind-api.version>4.0.0</xml-bind-api.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>com.sun.xml.bind</groupId>
            <artifactId>jaxb-impl</artifactId>
            <version>4.0.0</version>
        </dependency>

        <dependency>
            <groupId>jakarta.activation</groupId>
            <artifactId>jakarta.activation-api</artifactId>
            <version>2.1.0</version>
        </dependency>

        <dependency>
            <groupId>javax.xml.bind</groupId>
            <artifactId>jaxb-api</artifactId>
            <version>2.4.0-b180830.0359</version>
        </dependency>

        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <version>42.5.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-activemq</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-oxm</artifactId>
            <version>5.3.23</version>
        </dependency>

        <dependency>
            <groupId>jakarta.xml.bind</groupId>
            <artifactId>jakarta.xml.bind-api</artifactId>
            <version>${xml-bind-api.version}</version>
        </dependency>

    </dependencies>

    <build>

        <plugins>

            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>

            <plugin>
                <groupId>org.codehaus.mojo</groupId>
                <artifactId>jaxb2-maven-plugin</artifactId>
                <version>3.1.0</version>
                <executions>
                    <execution>
                        <id>schemagen</id>
                        <goals>
                            <goal>xjc</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <packageName>xjc.generated</packageName>
                    <outputDirectory>${basedir}/target/generated-sources/xjc</outputDirectory>
                    <sources>${basedir}/src/main/resources/xsd</sources>
                    <xjbSources>
                        <xjbSource>${basedir}/src/main/resources/xjb/default.xjb</xjbSource>
                    </xjbSources>
                </configuration>

            </plugin>

        </plugins>

    </build>

So, the issue! When I try to send through JmsTemplate generated ProductDto object in xml format, Jaxb2Marshaller throws an error:

Caused by: javax.xml.bind.JAXBException: class xjc.generated.ProductDto nor any of its super class is known to this context. at com.sun.xml.bind.v2.runtime.JAXBContextImpl.getBeanInfo(JAXBContextImpl.java:572) ~[jaxb-runtime-2.3.6.jar:2.3.6] at com.sun.xml.bind.v2.runtime.XMLSerializer.childAsRoot(XMLSerializer.java:452) ~[jaxb-runtime-2.3.6.jar:2.3.6] at com.sun.xml.bind.v2.runtime.MarshallerImpl.write(MarshallerImpl.java:298) ~[jaxb-runtime-2.3.6.jar:2.3.6] at com.sun.xml.bind.v2.runtime.MarshallerImpl.marshal(MarshallerImpl.java:226) ~[jaxb-runtime-2.3.6.jar:2.3.6] at org.springframework.oxm.jaxb.Jaxb2Marshaller.marshal(Jaxb2Marshaller.java:709) ~[spring-oxm-5.3.23.jar:5.3.23]

But if I use JAXB.marshal() I get the correct output in my console:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<productDto productId="1eb48960-0a60-42c1-98fd-0459451e0600">
    <productName>Cool product</productName>
</productDto

So here is the generated class:

/**
 * <p>Java class for anonymous complex type.
 * 
 * <p>The following schema fragment specifies the expected content contained within this class.
 * 
 * <pre>
 * &lt;complexType&gt;
 *   &lt;complexContent&gt;
 *     &lt;restriction base="{http://www.w3.org/2001/XMLSchema}anyType"&gt;
 *       &lt;sequence&gt;
 *         &lt;element ref="{}productName"/&gt;
 *       &lt;/sequence&gt;
 *       &lt;attribute ref="{}productId use="required""/&gt;
 *     &lt;/restriction&gt;
 *   &lt;/complexContent&gt;
 * &lt;/complexType&gt;
 * </pre>
 * 
 * 
 */
@XmlAccessorType(XmlAccessType.FIELD)
@XmlType(name = "", propOrder = {
    "productName"
})
@XmlRootElement(name = "productDto")
public class ProductDto
    implements Serializable
{

    private final static long serialVersionUID = 1L;
    @XmlElement(required = true)
    protected String productName;
    @XmlAttribute(name = "productId", required = true)
    protected String productId;

    /**
     * Gets the value of the productName property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getProductName() {
        return productName;
    }

    /**
     * Sets the value of the productName property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setProductName(String value) {
        this.productName = value;
    }

    /**
     * Gets the value of the productId property.
     * 
     * @return
     *     possible object is
     *     {@link String }
     *     
     */
    public String getProductId() {
        return productId;
    }

    /**
     * Sets the value of the productId property.
     * 
     * @param value
     *     allowed object is
     *     {@link String }
     *     
     */
    public void setProductId(String value) {
        this.productId = value;
    }

}

I will be gratefull for any ideas!

1 Answers

I don't know how, but I changed jaxb maven plugin and some jaxb dependencies and it worked for me.

So I found that I can use old jaxb API(a link):

In the JAXB version 2, the APIs are using the old Java EE package javax.xml.*

<dependency>
      <groupId>javax.xml.bind</groupId>
      <artifactId>jaxb-api</artifactId>
      <version>2.3.1</version>
  </dependency>

  <!-- JAXB RI -->
  <dependency>
      <groupId>org.eclipse.persistence</groupId>
      <artifactId>org.eclipse.persistence.moxy</artifactId>
      <version>2.7.8</version>
  </dependency>

And I changed my previous maven plugin to this one(a link):

           <plugin>
                <groupId>org.jvnet.jaxb2.maven2</groupId>
                <artifactId>maven-jaxb2-plugin</artifactId>
                <version>0.14.0</version>
                <executions>
                    <execution>
                        <id>schemagen</id>
                        <goals>
                            <goal>generate</goal>
                        </goals>
                    </execution>
                </executions>
                <configuration>
                    <generateDirectory>${basedir}/target/generated-sources/xjc</generateDirectory>
                    <schemaDirectory>${basedir}/src/main/resources/xsd</schemaDirectory>
                    <bindingDirectory>${basedir}/src/main/resources/xjb</bindingDirectory>
                </configuration>
            </plugin>

And now my ActiveMq dashboard looks like this:

Object parsed to XML and sent to ActiveMQ

Btw, config code did not change, and I have also added this dependency:

<dependency>
    <groupId>jakarta.activation</groupId>
    <artifactId>jakarta.activation-api</artifactId>
    <version>2.1.0</version>
</dependency>

I hope this will help someone!

Related