Apache Camel - all headers are lost except String during routing to ActiveMQ - apache-camel

I am setting a List in exchange object's header with other couple of headers and routing it to ActiveMQ.
exchange.getOut().setHeader("testList", testList);
exchange.getOut().setHeader("testObject", testObject);
exchange.getOut().setHeader("header1", "value1");
exchange.getOut().setHeader("header2", "value2");
In the next route when I access the exchange object's header, "testList" and testObject are not present ! But remaining headers are present (exchange.getIn().getHeader).
Can't we send any header ( list or any other object) except String ?

According to Camel documentation:
For the exchange.in.header, the following rules apply for the header
values:
The values must be primitives or their counter objects (such as Integer, Long, Character). The types, String, CharSequence, Date,
BigDecimal and BigInteger are all converted to their toString()
representation. All other types are dropped.
More information can be found here: http://camel.apache.org/jms
or here http://docs.oracle.com/javaee/1.4/api/javax/jms/Message.html
See Camel manual section : Message format when sending
Camel will remove headers not allowed types, but you can use vm or seda components instead of activemq. Or you can convert your list into XML or JSON, convert it to a string and send this string
through JMS.

Related

How to dynamically return a from endpoint in apache camel DSL

Here is my code
from("google-pubsub:123:subscription1?maxMessagesPerPoll=3 & concurrentConsumers=5" ).routeId("myroute")
.process(new ProducerProcessor())
to("google-pubsub:123:topic1")
;
In my code above ,the from channel I want to make it generic.Basically it should be able to consume data from good-pubsub or may be from a file or from a JMS queue.Hence depending upon a parameter I want to return
a different from channel.Something like below
private RouteDefinition fromChannel(String parameter) {
if (parameter is "google" then
return from("google-pubsub:123:subscription1?maxMessagesPerPoll=3 & concurrentConsumers=5" )
if (parameter is "file" then
return from(/my/fileFolder/)).split(body().tokenize("\n")).streaming().parallelProcessing();
}
I tried this but I am getting null pointer exception in the fromChannel method.Please let me know if you have better ideas.
Rewrite based on comment
You can for example create a (static) template route for every input type and generate the routes based on a configured endpoint list.
I described such an endpoint configuration and route generation scenario in this answer.
Like this you can generate the split part for every file route and any other specialty for other route types.
All these input routes are routing at their end to a common processing route
.from(pubsubEndpoint)
.to("direct:genericProcessingRoute")
.from(fileEndpoint)
.split(body()
.tokenize("\n"))
.streaming()
.parallelProcessing()
.to("direct:genericProcessingRoute")
.from("direct:genericProcessingRoute")
... [generic processing]
.to("google-pubsub:123:topic1")
The multiple input (and output) routes around a common core route is called hexagonal architecture and Camel fits very well into this.

How to Pass POJO over JMS Queue in Apache Camel

I am relatively new to Jboss Fuse & Apache Camel. I poll a directory for specific XML files (using file filter) and extract info from XML content and save it in POJO, which then needs to be sent over JMS Queue (databaseQueue) along with the XML file to a route where I need to extract the params from POJO and insert in Database. But I am unable to pass the POJO over JMS queue to be able to retrieve in another route. I can't set the POJO in body as the original payload will be lost. Please help. Below is the sample route.
<route id="fileRoute">
<from uri="file:{{PFlowIn_AEROW}}?delay={{PFlowScanDelay}}&filter=#fileFilter&delete=true"/>
<process ref="saveFlowParamsInPOJO"/>
<recipientList>
<simple>activemq:queue:databaseQueue, activemq:queue:messageArchiveQueue, activemq:queue:XmlValidationQueue</simple>
</recipientList>
</route>
As far as I know you can only set one Object in message body, however you have two options.
Maybe you can deal with an object that contains the XML and your POJO.
Another option is to try to add the POJO to the message headers. Try to add it in your Processor step and the retrieve it from the other side.
exchange.getIn().setHeader("myPOJO", POJOvalue);
Edit:
As #BeenaRautela indicates, the second option proposed is not valid because headers only accepts non-object data type.
Another options are:
Send the POJO in the message body and try to send the XML as String in a header.
Store the POJO params in a Map and set it in a header.
Convert your POJO into data-interchange format (e.g. json) and save into header
Convert POJO to json string
Save json string into message header
Convert json string to POJO or map
You could import json library which are available in apache camel json component and then perform marshal/unmarshal.
If you wish to convert your POJO within your bean, you may also try Jackson ObjectMapper class to do conversion.
// Convert from POJO to json String
ObjectMapper objectMapper = new ObjectMapper();
String pojoAsString = objectMapper.writeValueAsString(yourPojo);
// Convert from json String to POJO
YourPojo yourPojo = objectMapper.readValue(pojoAsString , YourPojo.class);
// convert from json String to map
Map<String, Object> map = objectMapper.readValue(pojoAsString , new TypeReference<Map<String,Object>>(){});
Source of above ObjectMapper examples

How do I set CSV header in camel DSL?

I have one CSV file without header which is containing two value like
"lenovo","30000"
I want to set header in first row using camel DSL and pass it to another route, header is like:
"laptop","price"
My DSL route:
from("file:...?fileName=file1.csv&noop=true")
//Something I want to include string like this
.addLineInBody("laptop"+"price").append("\n")
.to("file:../?fileName=output.csv");
How can I do this in camel DSL?
I do not fully understand your header-problem. If you want to just prepend the CSV file with a static header a save it again as CSV you could simply use a Java bean to prepend the message body.
If you want to convert CSV to a data structure or vice versa you should check out the Camel CSV documentation: http://camel.apache.org/csv.html
Update:
You can write a java bean like this. The important part is the #Body Camel annotation to inject the message body into the method. See http://camel.apache.org/parameter-binding-annotations.html for more information about this.
Do the body manipulation as you like.
public class CsvHeaderEnricher {
public String enrichCsvHeader(#Body String messageBody) {
String enrichedBody = "YourHeader" + messageBody;
return enrichedBody;
}
}
Then you can call the bean in your route like this:
from("file:...?fileName=file1.csv&noop=true")
.bean(new CsvHeaderEnricher())
.to("file:../?fileName=output.csv");
You can also register the bean in the Spring context, autowire it into the Route class and use an instance variable instead of new
As long as your bean contains only one method, you don't need to tell Camel what method to use

Possible to initialize a bindy (Apache Camel DataFormat - FixedLength and use it in the same route

My input file consists of several type of FixedLengthRecord, so I have lots of FixedLengthDataFormat to unmarshal each post.
I split the body per row
for the first I should realize which DataFormat I should use, and create an object
Then unmarshal
Something like this one:
from(myURI)
.split().tokenize("\n")
.process(initializeMyBindyDataFormat)
.unmarshal(bindy)
.end();
But my problem is, I get NPE for that bindy object when I initilize it via a process.
But if I create a bindy object before my route definition (before from) it will be work fine. My bindy object is depended on body and I cannot initialize it before route definition.
Actually Apache Camel process initialization of bindy object before starting the route
The answer is using .inout
Since I want to have unmarshalling in another route, a simple example should be as below:
from(myURI)
.split().tokenize("\n")
.inout("direct:unmarshalSpecificRow")
.end();
from(direct:unmarshalSpecificRow")
.choice()
.when(firstPredicate)
unmarshal(new BindyFixedLengthDataFormat(package1)
.when(secondPredicate)
unmarshal(new BindyFixedLengthDataFormat(package1)
.when(thirdPredicate)
unmarshal(new BindyFixedLengthDataFormat(package1)
.otherwise()
.throwException(new IllegalArgumentException("Unrecognised post")
.end();
Thanks jakub-korab for his help.
In this case I think it is better to divide your processing in two seps.
A main route which receives the different data. Here you define the predicate rules that determine what kind of body it is. Check the start of the body, or something that determines it is of this type or that. Add a choice() when() and based on which predicate gets set to true set it to separate route.
In the secondary routes add the specific bindy format and do your marshal/unmarshal work.
An example from the the documentation:
Predicate isWidget = header("type").isEqualTo("widget");
from("jms:queue:order")
.choice()
.when(isWidget).to("bean:widgetOrder")
.when(isWombat).to("bean:wombatOrder")
.otherwise()
.to("bean:miscOrder")
.end();
http://camel.apache.org/predicate.html

Cloud Endpoints not accepting JSON array

I want to build my endpoint, which accept JSON array of below format:
[
{
"test":"Math",
"result":"Pass"
},
{
"test":"Science",
"result":"FirstClass"
}
]
It will be a POST call with the above JSON to my endpoint.
I tried it with servlet too but did not get the required result, and also tried to with list and inserting in a new class and posting to that class. Thanks in advance.
Is that an accurate representation of the JSON object which is being sent over? Because one does not simply send a a POST request with a JSON object of their param object to a cloud endpoint. See here for a thorough guide to Endpoint API interaction from a javascript perspective - notice how the client library exposes an object "gapi" through which calls are made. If you're sending this JSON from an iOS or Android app, there are similar client libraries which can be generated for you by a cloud endpoints build tool.
After much frustration, I resorted to reading the docs more carefully. In that quest, I found an important note in the doc:
https://cloud.google.com/endpoints/docs/frameworks/java/parameter-and-return-types
"Any type except a parameter or injected type is considered an entity type. ... Entity types cannot be annotated with #Named"
With all examples showing named parameters, I was stumped as the docs don't explain further, but then found a solution. It ends up that if you do not have named parameters, everything is just passed in as a LinkedHashMap. Usually, you can do any work you need to with just that data structure, but if you HAVE to have it in JSON, you can convert it. Here are some examples:
#ApiMethod(name = "endpointIterfaceName.createItems", httpMethod = "post", path = "test/items")
public WhateverReturnType createItems(LinkedHashMap<String, Object> itemsMap) {
// Do Stuff with map values
return whateverReturnValue;
}
With this, you need to be sure that you post your data with the Content-Type of json (i.e. Content-Type:application/json; charset=UTF-8). So, for example, when testing, with a jquery ajax call you would need to set dataType to "json" or with Postman, you would select "Raw" then JSON (application/json).
If you really want to convert this to a JSON object in Java because for whatever reason you can not use a hash map, you can do the following in your method:
// Use gson library to convert the map to a string
Gson gson = new Gson();
String mapAsJsonString = gson.toJson(itemsMap);
// create a JSON object from the new string representation
JSONObject obj = new JSONObject(mapAsJsonString);
As a side note, if this is passed as Content-Type:text then the whole body will be in the map as the first key of the map. You could do some inadvisable things here and just get that key and avoid converting the map to a string and then to a json object, but, like I said, that is inadvisable. :)

Resources