camel-xmljson define output types - apache-camel

I am using camel-xmljson component to transform xml to json in spring.
I have the next code in my camel-context:
<dataFormats>
<xmljson id="xmljson" forceTopLevelObject="false"/>
</dataFormats>
<route id="LCG-Producer" autoStartup="false">
<from uri="activemq:to_in"/>
<marshal ref="xmljson"/>
<to uri="activemq:to_out"/>
</route>
The body to transform is the next:
<?xml version="1.0"?>
<example>
<cadena1>aaaaaaa</cadena1>
<entero1>1511</entero1>
</example>
The problem is that the json generated is the next:
{
"cadena1": "aaaaaaa",
"entero1": "1511"
}
The field "entero1" is generated as a String and it is an Integer, i mean, i need that output (without ""):
{
"cadena1": "aaaaaaa",
"entero1":1511
}
Is there any way to define the data format?

I have checked source code of XmlJsonDataFormat and I guess it is not possible.
However you can use same serializers to make you our processor.
For example:
(Disclaimer: the code is not efficient, it is only to give you ideas)
#Override
public void process(Exchange exchange) throws Exception {
XMLSerializer serializer = new XMLSerializer();
JSON json=serializer.read(exchange.getIn().getBody(String.class));
StringWriter writer=new StringWriter();
JsonConfig jsonConfig=new JsonConfig();
jsonConfig.registerJsonValueProcessor("entero1", new JsonValueProcessor() {
#Override
public Object processArrayValue(Object value, JsonConfig jsonConfig) {
return new BigDecimal(value.toString());
}
#Override
public Object processObjectValue(String key, Object value, JsonConfig jsonConfig) {
return new BigDecimal(value.toString());
}
});
JSONSerializer.toJSON(json, jsonConfig).write(writer);
writer.close();
exchange.getIn().setBody(writer.toString());
}

According to this document http://json-lib.sourceforge.net/usage.html#xml xmlserializer treats all data as string, unless "type" attribute is specified on the xml element, e.g.:
<example>
<cadena1>aaaaaaa</cadena1>
<entero1 type="number">1511</entero1>
</example>

Related

Camel:How to substitute output file path location dynamically?

Here is my Route:
<route id="comingFromSomewhere">
<from uri="direct:comingFromSomewhere" ></from>
<process ref="placeHolderProcessor" ></process>
<to uri="file://{{property.FinalPath}}" />
</route>
Here is my processor:
public class PlaceHolderProcessor implements Processor {
String gerantOutPutFilePath="C:/{clientName}/OUTPUT";
#Override
public void process(Exchange exchange) throws Exception {
String fileName = (String) exchange.getIn().getHeader("CamelFileName");
String clientName= (trioptimaFileName.split("-"))[0].toUpperCase();
String path = gerantOutPutFilePath.replace("{clientName}", clientName);
exchange.setProperty("FinalPath", path);
}
}
When I'm trying to change my output path dynamically based on input file name. It's not working for me.Seems like issue is at '<to uri="file://{{property.FinalPath}}" /> ' Can anyone please help me to fix ??
Use Camel's dynamic message endpoint like this:
<toD uri="file://${property.FinalPath}/" />

How to map the response for the REST request by Apache camel HTTP component

I have implemented following route with Apache camel XML mode.
<restConfiguration component="servlet" bindingMode="json" contextPath="/abc-esb/rest" port="8181">
<dataFormatProperty key="prettyPrint" value="true" />
<dataFormatProperty key="json.in.disableFeatures" value="FAIL_ON_EMPTY_BEANS,FAIL_ON_UNKNOWN_PROPERTIES" />
</restConfiguration>
<rest path="/service-http" consumes="application/json" produces="application/json">
<put type="com.abc.abcd.esb.models.UserServiceMapping" uri="/create">
<route>
<setHeader headerName="Authorization">
<simple>Basic YWRtaW46YWRtaW4=</simple>
</setHeader>
<setHeader headerName="CamelHttpMethod">
<constant>POST</constant>
</setHeader>
<setBody>
<simple>
{"SystemUserDetails":{"userName":"${body.objectDetails.userName}",
"password": "${body.objectDetails.password}"}}
</simple>
</setBody>
<to uri="http://192.168.1.20:8081/abcd/services/UserServiceRest/V1.0/users/create?bridgeEndpoint=true" />
</route>
</put>
</rest>
Functionality happens as expected. But camel returns HTTP 500 mentioning following error.
com.fasterxml.jackson.databind.JsonMappingException: No serializer
found for class
org.apache.camel.converter.stream.CachedOutputStream$WrappedInputStream
and no properties discovered to create BeanSerializer (to avoid
exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS) ) at
com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.failForEmpty(UnknownSerializer.java:69)
at
com.fasterxml.jackson.databind.ser.impl.UnknownSerializer.serialize(UnknownSerializer.java:32)
at
com.fasterxml.jackson.databind.ser.DefaultSerializerProvider.serializeValue(DefaultSerializerProvider.java:130)
When I analyze a tcp dump related to this scenario, I found this HTTP 500 occurred when trying to mapping the {"status" : "Success"} response from the external webservice.
Content of the UserServiceMapping class
private String serviceName;
private String serviceMethod;
private String mappedServiceObject;
private SystemUserDetails objectDetails;
// private StatusDetails statusDetails;
public String getMappedServiceObject() {
return mappedServiceObject;
}
public void setMappedServiceObject(String mappedServiceObject) {
this.mappedServiceObject = mappedServiceObject;
}
public String getServiceName() {
return serviceName;
}
public void setServiceName(String serviceName) {
this.serviceName = serviceName;
}
public String getServiceMethod() {
return serviceMethod;
}
public void setServiceMethod(String serviceMethod) {
this.serviceMethod = serviceMethod;
}
public SystemUserDetails getObjectDetails() {
return objectDetails;
}
public void setObjectDetails(SystemUserDetails objectDetails) {
this.objectDetails = objectDetails;
}
/*
* public StatusDetails getStatusDetails() { return statusDetails; }
*
* public void setStatusDetails(StatusDetails statusDetails) {
* this.statusDetails = statusDetails; }
*/
Request response flow of the scenario
How ever up to now I couldn't find an exact reason for this issue but I could by-pass the problem by setting Accept: application/xml other than Accept: application/json in my external REST client which makes the initial REST request. (REST service endpoint in other end configured to produce both json and xml)

Best way to write a Camel Test on a Content Based Routing

I'm a bit stuck on how to proceed in writing a Simple Camel Test case for a CBR. Say I have this simple route:
<route id="_route1">
<from id="_to1" uri="file:/home/user/data/eip-demo/in?noop=true"/>
<choice id="_choice1">
<when id="_when1">
<simple>${header.CamelFileName} regex '^.*xml$'</simple>
<to id="_to2" uri="file:/home/user/data/eip-demo/xml"/>
</when>
<when id="_when2">
<simple>${header.CamelFileName} regex '^.*txt$'</simple>
<to id="_to3" uri="file:/home/user/data/eip-demo/txt"/>
</when>
<otherwise id="_otherwise1">
<log id="_log1" message="File unknown!"/>
<to id="_to2" uri="file:/tmp"/>
</otherwise>
</choice>
</route>
What is the most correct way to test this route? I think I could replace the file: component with a direct: component and Produce the files in the Test Case. However, then I will not be able to run the route directly from the IDE (Jboss Developer Studio). What is the most correct approach to coding a route like that which needs to be tested?
UPDATE: I've refined a bit the Camel Test created by JBoss Developer Studio:
public class CamelContextXmlTest extends CamelSpringTestSupport {
// TODO Create test message bodies that work for the route(s) being tested
// Expected message bodies
protected Object[] expectedBodies = { "<something id='1'>expectedBody1</something>",
"textfile" };
protected Object[] name = { "test.xml",
"test.txt" };
// Templates to send to input endpoints
#Produce(uri = "file:/home/data/eip-demo/in?noop=true")
protected ProducerTemplate inputEndpoint;
// Mock endpoints used to consume messages from the output endpoints and then perform assertions
#EndpointInject(uri = "mock:output")
protected MockEndpoint outputEndpoint;
#EndpointInject(uri = "mock:output2")
protected MockEndpoint output2Endpoint;
#EndpointInject(uri = "mock:output3")
protected MockEndpoint output3Endpoint;
#Test
public void testCamelRoute() throws Exception {
// Create routes from the output endpoints to our mock endpoints so we can assert expectations
context.addRoutes(new RouteBuilder() {
#Override
public void configure() throws Exception {
from("file:/home/user/data/eip-demo/xml").to(outputEndpoint);
from("file:/home/user/data/eip-demo/txt").to(output2Endpoint);
}
});
// Define some expectations
// TODO Ensure expectations make sense for the route(s) we're testing
//outputEndpoint.expectedBodiesReceivedInAnyOrder(expectedBodies);
// Send some messages to input endpoints
int index=0;
for (Object expectedBody : expectedBodies) {
inputEndpoint.sendBodyAndHeader(expectedBody,"CamelFileName",name[index]);
index++;
}
outputEndpoint.expectedMessageCount(1);
output2Endpoint.expectedMessageCount(1);
// Validate our expectations
assertMockEndpointsSatisfied();
}
#Override
protected ClassPathXmlApplicationContext createApplicationContext() {
return new ClassPathXmlApplicationContext("META-INF/spring/camel-context.xml");
}
}
Is it a good solution or can it be improved?
What you need here is AdviseWith:
http://camel.apache.org/advicewith.html
You simply need to replace/modify your endpoints on the fly OR simply add a mock on the fly after each condition of your CBR. If you couldn't do it, let me know and I will help with with some actual code that does this job for you.
R.

get exchange related properties like ExchangePattern ID and BodyType in message

I'm new to Camel. I'm trying to read a file from one location, making some change in the file content and sending the file to another location. I trying to get exchange related information added in the file like the following:
Exchange[ExchangePattern: InOnly, BodyType: String, Body:
Test Message from custom processor:
Text from input file.
First line in the above sample is not coming. So far I wrote the following code:
applicationContext.xml:
<bean id="customProcessor" class="com.javacodegeeks.camel.CustomProcessor"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route>
<from uri="file:C:\\inputdir\\" />
<process ref="customProcessor"/>
<to uri="file:C:\\outputdir\\" />
</route>
CustomProcessor.java:
public class CustomProcessor implements Processor {
public void process(Exchange exchange) throws Exception {
String msgBody = exchange.getIn().getBody(String.class);
exchange.getIn().setBody("Test Message from custom processor: " + msgBody);
}
}
Main class:
public static final void main(String[] args) throws Exception {
ApplicationContext appContext = new ClassPathXmlApplicationContext(
"applicationContext.xml");
CamelContext camelContext = SpringCamelContext.springCamelContext(
appContext, false);
try {
camelContext.start();
Thread.sleep(200000);
} finally {
camelContext.stop();
}
}
You can access all the properties of Camel Exchange using Exchanges' method getProperties() i.e. exchange.getProperties() which return Map, so using this you can get the information and save it into the file using your CustomProcessor Camel processor.
For more information have a look on:
http://camel.apache.org/maven/current/camel-core/apidocs/org/apache/camel/Exchange.html

How do I properly use Mocks when testing Camel routes?

I am trying to write a Camel test that checks to ensure a content based router is routing XML files correctly. Here are the enpoints and the route in my blueprint.xml:
<endpoint uri="activemq:queue:INPUTQUEUE" id="jms.queue.input" />
<endpoint uri="activemq:queue:QUEUE1" id="jms.queue.1" />
<endpoint uri="activemq:queue:QUEUE2" id="jms.queue.2" />
<route id="general-jms.to.specific-jms">
<from ref="jms.queue.input" />
<choice>
<when>
<xpath>//object-type = '1'</xpath>
<log message="Sending message to queue: QUEUE1" />
<to ref="jms.queue.1" />
</when>
<when>
<xpath>//object-type = '2'</xpath>
<log message="Sending message to queue: QUEUE2" />
<to ref="jms.queue.2" />
</when>
<otherwise>
<log message="No output was able to be determined based on the input." />
</otherwise>
</choice>
</route>
Right now, all I am trying to do is send in a sample source file that has an <object-type> of 1 and verify that is it routed to the correct queue (QUEUE1) and is the correct data (should just send the entire XML file to QUEUE1). Here is my test code:
public class RouteTest extends CamelBlueprintTestSupport {
#Override
protected String getBlueprintDescriptor() {
return "/OSGI-INF/blueprint/blueprint.xml";
}
#Override
public String isMockEndpointsAndSkip() {
return "activemq:queue:QUEUE1";
}
#Test
public void testQueue1Route() throws Exception {
getMockEndpoint("mock:activemq:queue:QUEUE1").expectedBodiesReceived(context.getTypeConverter().convertTo(String.class, new File("src/test/resources/queue1-test.xml")));
template.sendBody("activemq:queue:INPUTQUEUE", context.getTypeConverter().convertTo(String.class, new File("src/test/resources/queue1-test.xml")));
assertMockEndpointsSatisfied();
}
}
When I run this test, I see the log message that I put in the route definition that says it is sending it to QUEUE1, but the JUnit test fails with this error message: java.lang.AssertionError: mock://activemq:queue:QUEUE1 Received message count. Expected: <1> but was: <0>.
Can someone help me understand what I am doing wrong?
My understanding is that Camel will automatically mock the QUEUE1 endpoint since I overrode the isMockEndpointsAndSkip() and provided the QUEUE1 endpoint uri. I thought this meant I should be able to use that endpoint in the getMockEnpoint() method just by appending "mock:" to the beginning of the uri. Then I should have a mocked endpoint of which I can set expections on (i.e. that is has to have the input file).
If I am unclear on something please let me know and any help is greatly appreciated!
The solution is to use CamelTestSupport.replaceRouteFromWith.
This method is completely lacking any documentation, but it works for me when invoking it like this:
public class FooTest extends CamelTestSupport {
#Override
public void setUp() throws Exception {
replaceRouteFromWith("route-id", "direct:route-input-replaced");
super.setUp();
}
// other stuff ...
}
This will also prevent the starting of the original consumer of the from destination of the route. For example that means it isn't necessary anymore to have a activemq instance running when a route with an activemq consumer is to be tested.
After working on this for quite some time, the only solution I came up with that actaully worked for me was to use the createRouteBuilder() method in my test class to add a route to a mocked endpoint at the end of the route defined in my blueprint.xml file. Then I can check that mocked endpoint for my expectations. Below is my final code for the test class. The blueprint XML remained the same.
public class RouteTest extends CamelBlueprintTestSupport {
#Override
protected String getBlueprintDescriptor() {
return "/OSGI-INF/blueprint/blueprint.xml";
}
#Test
public void testQueue1Route() throws Exception {
getMockEndpoint("mock:QUEUE1").expectedBodiesReceived(context.getTypeConverter().convertTo(String.class, new File("src/test/resources/queue1-test.xml")));
template.sendBody("activemq:queue:INPUTQUEUE", context.getTypeConverter().convertTo(String.class, new File("src/test/resources/queue1-test.xml")));
assertMockEndpointsSatisfied();
}
#Test
public void testQueue2Route() throws Exception {
getMockEndpoint("mock:QUEUE2").expectedBodiesReceived(context.getTypeConverter().convertTo(String.class, new File("src/test/resources/queue2-test.xml")));
template.sendBody("activemq:queue:INPUTQUEUE", context.getTypeConverter().convertTo(String.class, new File("src/test/resources/queue2-test.xml")));
assertMockEndpointsSatisfied();
}
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
public void configure() throws Exception {
from("activemq:queue:QUEUE1").to("mock:QUEUE1");
from("activemq:queue:QUEUE2").to("mock:QUEUE2");
}
};
}
}
While this solution works, I don't fully understand why I can't just use isMockEndpointsAndSkip() instead of having to manually define a new route at the end of my existing blueprint.xml route. It is my understanding that defining isMockEndpointsAndSkip() with return "*"; will inject mocked endpoints for all of your endpoints defined in your blueprint.xml file. Then you can check for your expections on those mocked endpoints. But for some reason, this does not work for me.

Resources