When I try to marshal data I got from the database in Apache Camel using JAXB and providing XSD schema I get the error
java.io.org.apache.camel.NoTypeConversionAvailableException: No type converter available to convert from type: java.util.LinkedHashMap to the required type: java.io.InputStream with value {id=5, number=5599, type=B3, ... }
when I try to send the message to ActiveMQ. I'm new to integration and this is my intern Camel project. When I marshal the message to json everything is alright. I thought about converting the message to json and then to XML, but it seems to me that isn't the way I should do it. I've got a prepared XSD schema that looks like this:
<?xml version="1.0" encoding="UTF-8" ?>
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:pc="com.release11.packages"
targetNamespace="com.release11.materials">
<xs:import schemaLocation="packages.xsd"
namespace="com.release11.packages"/>
<xs:simpleType name="materialTypeType">
<xs:restriction base="xs:string">
<xs:enumeration value="A1"/>
<xs:enumeration value="A2"/>
<xs:enumeration value="A3"/>
<xs:enumeration value="B1"/>
<xs:enumeration value="B2"/>
<xs:enumeration value="B3"/>
<xs:enumeration value="C1"/>
<xs:enumeration value="C2"/>
<xs:enumeration value="C3"/>
</xs:restriction>
</xs:simpleType>
<xs:complexType name="materialType">
<xs:sequence>
<xs:element name="Id" type="xs:integer"/>
<xs:element name="Number" type="xs:integer"/>
<xs:element name="Type" type="materialTypeType"/>
<xs:element name="Name" type="xs:string"/>
<xs:element name="Description" type="xs:string"/>
<xs:element name="Is_deleted" type="xs:boolean"/>
<xs:element ref="pc:Packages" minOccurs="0"/>
</xs:sequence>
</xs:complexType>
<xs:complexType name="materialsType">
<xs:sequence>
<xs:element name="Material" type="materialType" maxOccurs="unbounded"/>
</xs:sequence>
</xs:complexType>
<xs:element name="Materials" type="materialsType"/>
</xs:schema>
I tried to find the answer on the web, but I found nothing useful or I couldn't understand the answer so I need someone to explain this to me. Please help me.
Here's my code:
public class InputAdapter {
public static void main(String[] args) throws Exception {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/Packages");
dataSource.setUsername("uname");
dataSource.setPassword("passwd");
SimpleRegistry registry = new SimpleRegistry();
registry.bind("dataSource", dataSource);
ActiveMQConnectionFactory activeMQConnectionFactory = new ActiveMQConnectionFactory();
activeMQConnectionFactory.setBrokerURL("tcp://127.0.0.1:61616");
Connection connection = activeMQConnectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createQueue("MESSAGES_RAW");
MessageProducer producer = session.createProducer(destination);
producer.setDeliveryMode(DeliveryMode.NON_PERSISTENT);
CamelContext context = new DefaultCamelContext(registry);
context.addComponent("activemq", JmsComponent.jmsComponentAutoAcknowledge(activeMQConnectionFactory));
context.addRoutes(new RouteBuilder() {
@Override
public void configure() {
JaxbDataFormat dataFormat = new JaxbDataFormat();
dataFormat.setSchemaLocation("material.xsd");
from("timer://foo?repeatCount=1")
.setBody(constant("SELECT * FROM material;"))
.to("jdbc:dataSource")
.split(body())
.marshal(dataFormat)
.to("activemq:queue:MESSAGES_RAW");
}
});
context.start();
Thread.sleep(1000);
context.stop();
}
}
You can use xjc command-line tool that comes pre-installed with at JDK 8 to generate jaxb classes.
Example:
xjc material.xsd
# With groupId
xjc -p <groupId> material.xsd
# With groupId and bindings configuration file
xjc -p <groupId> -b bindings.xjb material.xsd
Alternatively you can use maven plugin to do the same. By default the plugin will look for schema xsd files from src/main/xsd
and bindings xjb files from src/main/xjb
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>jaxb2-maven-plugin</artifactId>
<version>2.5.0</version>
<executions>
<execution>
<id>xjc</id>
<goals>
<goal>xjc</goal>
</goals>
</execution>
</executions>
<configuration>
<packageName>com.example.group</packageName>
</configuration>
</plugin>
</plugins>
</build>
If you're using JDK 11 or later you'll also have to include couple of related dependencies that are no longer included in the JDK.
<dependencies>
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-xjc</artifactId>
<version>2.3.6</version>
</dependency>
</dependencies>
With these maven should generate the classes to target/generated-sources/jaxb
folder after running mvn clean install
. With maven plugin you're better off creating separate api-project for these and adding it as a dependency for your camel integration project.
You can use jaxb with camel by creating JaxbDataFormat using JAXBContext instance.
JAXBContext jaxbContext = JAXBContext.newInstance(Materials.class);
JaxbDataFormat jaxbDataformat = new JaxbDataFormat(jaxbContext);
from("direct:marshalMaterials")
.routeId("marshalMaterials")
.marshal(jaxbDataformat)
.log("${body}");
Since you're querying a database that by default returns list of maps, you'll have to convert it to appropriate Jaxb object. You can use generated ObjectFactory
class to generate different jaxb class instances.
With JDK 11 you might also need following dependencies
<dependency>
<groupId>jakarta.xml.bind</groupId>
<artifactId>jakarta.xml.bind-api</artifactId>
<version>2.3.3</version>
</dependency>
<!-- versions obtained from dependency-management camel-bom -->
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-impl</artifactId>
</dependency>
<dependency>
<groupId>com.sun.xml.bind</groupId>
<artifactId>jaxb-core</artifactId>
</dependency>
More recent versions of jaxb are using jakarta namespace instead of javax. If you want to use the newer jakarta namespace instead you can use jaxb2-maven-plugin
and jakarta.xml.bind-api
version 3.x or higher. Support for Jakarta namespace is planned for Camel 4.x so if you're using Camel 3.x you might want to wait or upgrade to that first.
With bindings xjb files you can tweak how classes get generated like change class or property names, prevent nested class mess etc.
Example: Empty binding file template
<?xml version="1.0" encoding="UTF-8"?>
<jaxb:bindings xmlns:jaxb="http://java.sun.com/xml/ns/jaxb"
xmlns:xjc="http://java.sun.com/xml/ns/jaxb/xjc"
xmlns:xs="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
version="2.1"
xsi:schemaLocation="http://java.sun.com/xml/ns/jaxb http://java.sun.com/xml/ns/jaxb/bindingschema_2_0.xsd">
...
</jaxb:bindings>