Extract org.restlet.http.headers value from Camel headers inside a .choice() - apache

I'm trying to extract a value from org.restlet.http.headers header collection in a Camel route.
My incoming POST has a http header property called IncomingRequestType: ABCD.
Camel moves this inside the exchange headers collection, but it is buried inside org.restlet.http.headers which is in-itself a collection of headers.
I can extract the value in a process using the code below:
.process(new Processor() {
public void process(Exchange exchange) throws Exception {
org.restlet.util.Series<Header> httpHeaders = null;
httpHeaders = (Series<Header>) exchange.getIn().getHeader("org.restlet.http.headers");
String reqType = httpHeaders.getValues("IncomingRequestType").toString();
}})
Outside of a process I need to access the IncomingRequestType inside a .choice().when()
e.g. i want to be able to do:
.choice()
.when(header("org.restlet.http.headers")["IncomingRequestType"]).isEqualTo("ABCD"))
Any suggestions on how this can be done. I've tried creating predicates but cannot get a suitable solution.

This can be done in the simple language:
.choice()
.when(simple("${in.header.org.restlet.http.headers[IncomingRequestType]} == 'ABCD'"))

Related

Apache Camel validate before insert data with setBody

I have RouteBuilder as below.
from("seda:requestQueue").routeId("service_request").log(LoggingLevel.INFO, "Processing STARTED, {body = ${body}")
.setBody(body())
.to("select * from SERVICE_REQUEST WHERE requestType=:#TYPE AND requestDate=:#date AND owner=:#OWNER)
.split(body()).streaming().process(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
Map<String, Object> row = exchange.getIn().getBody(Map.class);
if (row == null) {
LOGGER.info("Request is new. No records found.");
return;
}
//Duplicate request. Q(Not sure how to terminate the process with exception)
}
})
.log(LoggingLevel.INFO, "Processing CONTINUE, {body = ${body}")
.setBody(body())
.to("insert into SERVICE_REQUEST (ID,....) ").log(LoggingLevel.INFO, "Processing COMPLETED").end();
I would like to achieve
Whenever request is submitted (for now via SEDA), first check whether the same request has been available in database or not.
If it is not available then only insert into database (new row)
Question:
1. How can I set original request body to insertQuery? As per the above code, body received at seda:requestQueue is not available to to("insert into SERVICE..).
Your splitter will send out messages with a new body (based on the SQL). So, the body itself cannot be used.
Instead, before calling the splitter, set the body to a property of the Exchange. Then, when you need to use it, read the value of the property.
.setProperty("mySampleProperty", simple("${body}")
If you need it back as the body, then -- at that point -- set the body to the value that you previously stored in the Exchange property.
Here's a similar question: Apache Camel: how store variable for later use

Header values getting lost spring integration

Im using header enricher which uses existing headers to set a value of new header. However existing header information is lost and only 3 header remain ie request-id,timestamp and raw-body.
public String vipul(Message<String> message) {
MessageHeaders messageHeaders =message.getHeaders();
if (messageHeaders.containsKey("x-death")) {
List<HashMap<String, Object>> deathList = (List<HashMap<String, Object>>) messageHeaders
.get("x-death");
//logger.debug(message.get("messageId")+" "+deathList);
if (deathList.size() > 0) {
HashMap<String, Object> death = deathList.get(0);
if (death.containsKey("original-expiration")) {
return (String) death.get("original-expiration");
//logger.info(messageHeaders.get("messageId")+" original-expiration = "+death.get("original-expiration"));
}
}
} else {
return null;
}
return "";
}
In this messageHeaders map has only has 3 keys and not all the header keys which are normally there. I need to make a retry system using original expiration .
MY spring integration xml has following snippet :
<int:header-enricher input-channel="fromPushAppointmentErrorHandler1"
output-channel="fromPushAppointmentErrorHandler">
<int:header name="original_expiration" method="vipul" ref="errorhelper"/>
</int:header-enricher>
First of all it looks like you also need an overwrite="true" for that <int:header name="original_expiration"> since the logic in your vipul() is about to produce a new value for existing header and that is not going to happen since the value is already there in headers.
The fact that you are missing some headers in this your logic might be dictated by some upstream <transformer> which returns the whole Message without copying request headers.

Camel check header value after split

I have a problem in my Camel Route when I try to check the value of a header.
So what is happening is that I go to a processor, do my stuff, then I create 2 different message that I put inside the body.
After that I go back on my route, I split my body so I can route the 2 differents messages, and there I use a .choice().when() on the header CamelFileName to check if it contains some value.
It doesn't find the value and then doesn't go inside the .when()
Here is the source code to make it more clear :
// My route
from("myQuartz")
.routeId("myId")
.bean(myProcessor.class)
.split(body())
.to("log:test?showAll=true&multiline=true")
.log('[${header.CamelFileName}]')
.choice()
.when(header('CamelFileName').contains('myString1'))
// do my stuff
.endChoice()
.when(header('CamelFileName').contains('myString2'))
// do my other stuff
.endChoice()
.otherwise()
.log("It did not go inside the when")
.to("log:test?showAll=true&multiline=true")
.endChoice()
.end()
So here I am simply trying to check if the CamelFileName header contains a string (it's not a variable), but it keep going inside the otherwise.
The log just after the split show me that the CamelFileName header is correct and do contains the string I am looking for.
I tried different ways of checking the value inside the when() such as using a simple(), but it doesn't work.
My file is a groovy file.
Thanks for your help.
EDIT :
So to explain what is inside my body I will show you the processor source code.
I create two DefaultMessage,
I set them a body and a CamelFileName header,
I put them into a list and then I put that list into my exchange body.
After that I go back to the route and I split the body so it will separate the two messages and route them.
Here is what's happening in my processor :
// Message 1
DefaultMessage message1 = new DefaultMessage()
message1.setBody(bodyContent)
def fileName1 = "myString1_blablabla.txt"
message1.setHeader("CamelFileName",fileName1)
listMessages.add(message1)
// Message 2
DefaultMessage message2 = new DefaultMessage()
message2.setBody(bodyContent)
def fileName2 = "myString2_blablabla.txt"
message2.setHeader("CamelFileName",fileName2)
listMessages.add(message2)
exchange.in.setBody(listMessages)
I've setup a simpler test for your route. It routes data to the proper when clause. When you split(), the headers get copied for each exchange, so I'm not sure why you would expect (given your route) why the elements of the list would have different header values.
public class SampleTest extends CamelTestSupport{
#Override
protected RouteBuilder createRouteBuilder() throws Exception {
return new RouteBuilder() {
#Override
public void configure() throws Exception {
from("direct:start")
.setHeader("CamelFileName", simple("myString1"))
.split(body())
.choice()
.when(header("CamelFileName").contains("myString1"))
.to("mock:myString1")
.endChoice()
.when(header("CamelFileName").contains("myString2"))
.to("mock:myString2")
.endChoice()
.otherwise()
.to("mock:otherwise")
.endChoice()
.end();
}
};
}
#Test
public void test() throws Exception {
//Setup mock body
java.util.List<String> myList = new java.util.ArrayList<String>();
myList.add("1");
myList.add("2");
MockEndpoint mock1 = getMockEndpoint("mock:myString1");
MockEndpoint mock2 = getMockEndpoint("mock:myString2");
MockEndpoint mockOtherwise = getMockEndpoint("mock:otherwise");
mock1.expectedMessageCount(myList.size());
mock2.expectedMessageCount(0);
mockOtherwise.expectedMessageCount(0);
template.sendBody("direct:start", myList);
assertMockEndpointsSatisfied();
}
}

How copy selected property (not all) from camel to activiti

Camel scenario:
read file
do something e.g store content in database
run activiti process and pass variable from previous step
RouteBuilder:
from("file:/home/work/Inbox")
.to("bean:sourceFileService?method=storeFile")
.to("activiti:Receive?copyVariablesFromProperties=true")
During call bean sourceFileService property 'sourceFileId' is set.
This variable should be transferred to the process Receive as variable.
If I don't use copyVariablesFromProperties any variable is not set in process.
One the other side when copyVariablesFromProperties=true then camel try pass all properties and exception occurs:
ActivitiException: couldn't find a variable type that is able to serialize GenericFile
(because one of property is object represents read file, there are 7 other unwanted property )
How pass only selected property to activiti endpoint or in camel any next 'to' ?
change sourceFileService's storeFile method signature as follows;
public String storeFile(... your other params, #OutHeaders Map headers) {
...
headers.put("sourceFileId", "32132132");
....
}
and you can access the set sourceFileId in your activiti endpoint
I have found that the use of copyVariablesFromProperties is not necessary.
The same is achieved by
.setBody().properties()
.to("activiti:Receive")
When in body is Map camel set variables for activiti process using that map.
But I still get exception for the same reason (pass unwanted, not serializable object).
The only solution I have found is
from("file:/home/work/Inbox")
.to("bean:sourceFileService?method=storeFile")
.setBody(method(Helper.class))
.to("activiti:Receive")
where
public class Helper {
#Handler
public Map getProcessVariables(Exchange exchange) {
Map<String, Object> variables = new HashMap<String, Object>();
variables.put("sourceFileId, exchange.getProperty("sourceFileId"));
return variables;
}
}

An interesting Restlet Attribute behavior

Using Restlet 2.1 for Java EE, I am discovering an interesting problem with its ability to handle attributes.
Suppose you have code like the following:
cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
and on your browser you provide the following URL:
http://localhost:8100/testpath/command
then, of course, the attr attribute gets set to "command".
Unfortunately, suppose you want the attribute to be something like command/test, as in the following URL:
http://localhost:8100/testpath/command/test
or if you want to dynamically add things with different levels, like:
http://localhost:800/testpath/command/test/subsystems/network/security
in both cases the attr attribute is still set to "command"!
Is there some way in a restlet application to make an attribute that can retain the "slash", so that one can, for example, make the attr attribute be set to "command/test"? I would like to be able to just grab everything after testpath and have the entire string be the attribute.
Is this possible? Someone please advise.
For the same case I usually change the type of the variable :
Route route = cmp.getDefaultHost().attach("/testpath/{attr}",SomeServerResource.class);
route.getTemplate().getVariables().get("attr") = new Variable(Variable.TYPE_URI_PATH);
You can do this by using url encoding.
I made the following attachment in my router:
router.attach("/test/{cmd}", TestResource.class);
My test resource class looks like this, with a little help from Apache Commons Codec URLCodec
#Override
protected Representation get() {
try {
String raw = ResourceWrapper.get(this, "cmd");
String decoded = new String(URLCodec.decodeUrl(raw.getBytes()));
return ResourceWrapper.wrap(raw + " " + decoded);
} catch(Exception e) { throw new RuntimeException(e); }
}
Note my resource wrapper class is simply utility methods. The get returns the string of the url param, and the wrap returns a StringRepresentation.
Now if I do something like this:
http://127.0.0.1/test/haha/awesome
I get a 404.
Instead, I do this:
http://127.0.0.1/test/haha%2fawesome
I have URLEncoded the folder path. This results in my browser saying:
haha%2fawesome haha/awesome
The first is the raw string, the second is the result. I don't know if this is suitable for your needs as it's a simplistic example, but as long as you URLEncode your attribute, you can decode it on the other end.