I am new to Apache Camel and mock testing so here it goes...
I have an XML with no XSD schema which I have no influence on. Child elements of this XML hold data which I want to bind to my business pojo. This POJO (WeatherCurrent) is already JPA annotated and I was thinking of adding JAXB annotation so the splitted XML could be mapped to my POJO.
As this XML has a root element and I only want its childs (metData) I have a problem how to annotate my POJO as I can not use @XmlRootElement.
This is partialy described here: http://camel.apache.org/splitter.html at Streaming big XML payloads using Tokenizer language chapter. My POJO is like order xml element in that example. I need just a few elements out of metData xml element to map to my POJO fields.
There is also a chapter Partial marshalling/unmarshalling at http://camel.apache.org/jaxb.html but there is no JAVA DSL example (a must), nor how to annotate the pojo to work with XML fragments.
So far I have this test code:
import java.io.File;
import org.apache.camel.EndpointInject;
import org.apache.camel.Exchange;
import org.apache.camel.ProducerTemplate;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.component.mock.MockEndpoint;
import org.apache.camel.converter.jaxb.JaxbDataFormat;
import org.apache.camel.spi.DataFormat;
import org.apache.camel.test.junit4.CamelTestSupport;
import org.junit.Test;
public class WeatherCurrentTest extends CamelTestSupport {
@EndpointInject(uri = "file:src/test/resources")
private ProducerTemplate inbox;
@Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
@Override
public void configure() throws Exception {
DataFormat jaxbDataFormat = new JaxbDataFormat("com.mycompany.model.entities.weather");// WARNING two packages for JaxbDataFormat
from("file:src/test/resources/?fileName=observation_si_latest.xml&noop=true&idempotent=false")
.split()
.tokenizeXML("metData")
.unmarshal(jaxbDataFormat)
.to("mock:meteo");
}
};
}
@Test
public void testMetData() throws Exception {
MockEndpoint mock = getMockEndpoint("mock:meteo");
mock.expectedMessageCount(9);
File meteo = new File("src/test/resources/observation_si_latest.xml");
String content = context.getTypeConverter().convertTo(String.class, meteo);
inbox.sendBodyAndHeader(content, Exchange.FILE_NAME, "src/test/resources/observation_si_latest.xml");
mock.assertIsSatisfied();
}
}
The XML (observation_si_latest.xml) comes in this form:
<?xml version="1.0" encoding="UTF-8"?>
<data id="MeteoSI_WebMet_observation_xml">
<language>sl</language>
<metData>
<domain_altitude>55</domain_altitude>
<domain_title>NOVA GORICA</domain_title>
<domain_shortTitle>BILJE</domain_shortTitle>
<tsValid_issued>09.03.2012 15:00 CET</tsValid_issued>
<t_degreesC>15</t_degreesC>
</metData>
<metData>
<domain_meteosiId>KREDA-ICA_</domain_meteosiId>
I left out lots of elements of the metData elements for brevity. I want to map (amongs others) domain_title to my JPA annotated POJO's station field and later save it database, hopefully all in one smart and short Camel route.
The POJO (no JAXB annotations yet):
@Entity
@Table(name="weather_current")
public class WeatherCurrent implements Serializable {
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
private String station;
@Temporal( TemporalType.TIMESTAMP)
@Column(name="successfully_updated")
private Date successfullyUpdated;
private short temperature;
@Column(name="wind_direction")
private String windDirection;
}
I also left out lots of fields and methods.
So the idea is to map the value of the *domain_title* to WeatherCurrent POJO's station field and to do so for each metData element and save a list of WeatherCurrent objects to database.
Any advice on the best way on how to implement this is welcome.
It turns out I had one wrong assumption of not being able to use the @XmlRootElement. The route and the test passes with success after I annotated the POJO and added jaxb.index file beside it. Will post the solution later or tomorrow as I am on a train now.
Hours later...
The JAXB annotations on the POJO (on top of JPA ones):
@Entity
@Table(name="weather_current")
@XmlRootElement(name = "metData")
@XmlAccessorType(XmlAccessType.FIELD)
public class WeatherCurrent implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy=GenerationType.IDENTITY)
private int id;
@XmlElement(name = "nn_shortText")
private String conditions;
@XmlElement(name = "rh")
private short humidity;
@XmlElement(name = "msl")
private short pressure;
@Column(name="pressure_tendency")
@XmlElement(name = "pa_shortText")
private String pressureTendency;
@Temporal( TemporalType.TIMESTAMP)
@XmlElement(name = "tsValid_issued")
private Date published;
@XmlElement(name = "domain_longTitle")
private String station;
enabled me to get a list of WeatherCurrent Exchage objects. Just for test I routed each one to my EchoBean to print out one property:
.unmarshal(jaxbDataFormat).bean(EchoBean.class, "printWeatherStation")
and EchoBean:
public class EchoBean {
public String printWeatherStation(WeatherCurrent weatherCurrent) {
return weatherCurrent.getStation();
}
}
nicely prints out the names of the weather stations with the log Camel component.
The one undocumented thing that bothered me was that I had to put this jaxb.index file next WeatherCurrent java source although at http://camel.apache.org/jaxb.html it clearly says that jaxb context is initialized with
DataFormat jaxb = new JaxbDataFormat("com.acme.model");
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With