Camel routing based on object value - java

I have two simple Camel routes working for writing to a jms queue and reading from it. I am putting a serialized object to the queue. I am able to deserialize it and covert it to json successfully.
Route for writing:
from("direct:message").to("jms:myqueu")
My route for reading:
from("jms:myqueu")
.marshal()
.json(JsonLibrary.Gson).
.to("file://cc")
Now i want to check a field within the object and route based on that.Also that field should not be part of the final json.
Can i check the value within the object and route based on that( like write to different files?). I can add the annotation in the pojo to avoid the field in final json
I thought of converting object to json, and then sending to queue. Then i can use jsonpath for conditional routing. But then how can i omit a field from final json?

Yes, you can use content based routing to check any field in incoming object and do the routing based on that.
ref: http://camel.apache.org/content-based-router.html
To ignore a field during json marshalling , you can use #JsonIgnore - Jackson annotation.

For reference, i was able to get it working. I added a custom filter class and checked the bean value within that. Also linked it within the choose option for routing.
My route is now:
from("jms:myqueu")
.choice()
.when()
.method(Exp.class,"checkfieldA")
.json(JsonLibrary.Gson).
.to("file://cc")
.when()
.method(Exp.class,"checkfieldB")
.json(JsonLibrary.Gson).
.to("file://dd")
.endChoice()
Here Exp is a normal class and checkfieldA and checkfieldB are two methods returning boolean values.
class Exp{
public boolean checkfieldA(Message message){
myobj obj = (myobj)message.getBody() // this is the object is put to queue. Need to cast to my object type .
if(myobj.isFieldA()){
return true;
}
}
}

Related

Convert Spring webclient error response body from string to object

In this method the msg variable is returning in string format..how can be conversion of it can be taken place into certain java pojo object.
Method image
In place of Mono I tried Mono but it didnt worked for me.
I just want to get the error response body in pojo object format rather then in string format.
Also tried in this manner, but no success.
Tried in this manner
do you know what class the response is in case of an error? ResponseEntity perhaps?
you need to use a JSON converter package to convert it into a POJO, but you first need to make a POJO class of your own with the relevant fields (and make sure to name them in case sensitive manner).
then, inside the .flatMap(...) turn the String into the POJO using the converter.

WebClient does not return a "valid" list of Strings

I have a spring boot app that among others, has an endpoint that when hit, returns a list of strings. I also have another spring boot app that hits the first app's endpoint to get the data. The fetch code:
return webClient.get().uri("/sensors/get-cities").headers(httpHeaders -> {
httpHeaders.set("Authorization", auth);
}).retrieve()
.bodyToFlux(String.class).collectList().block();
The above yields a list but with this format when I inspect it in the debbuger, "["city"]". The outer double quotes, I get them because it's a string but the brackets and the inside double quotes, I do not. I tried replacing these characters but I had no luck with the brackets (tried regex). It is like they are not there, but at the same time they are. I am confused at this point. But I think that the behavior of the fetch code is not normal, it should yield a valid array of strings.
What you are probably getting (im guessing here) is a response body that looks something like this:
[
"New York",
"Madrid",
"London"
]
You then tell webflux that you want to convert the body to a Flux of String by calling bodyToFlux(String.class).
So the framework takes the entire response and makes a string out of it
// A string of the entire array (im escaping the quotation marks)
"[\"New York\",\"Madrid\",\"London\"]"
And then the framework will throw the entire thing into a Flux which means it takes the first position in the Flux. You then emit all the values into a List by calling collectList The equivalent code is sort of:
List<String> oneString = Flux.just("[\"New York\",\"Madrid\",\"London\"]")
.collectList()
.block();
So you get a list, with one string in it, which is the entire body.
What you probably want to do is to get a list out if it. And this is one way to do it:
List<String> strings = webClient.get()
.uri("/sensors/get-cities")
.headers(httpHeaders -> {
httpHeaders.set("Authorization", auth);
})
.retrieve()
.bodyToMono(new ParameterizedTypeReference<List<String>>() {})
.block();
Spring explains ParameterizedTypeReference:
The purpose of this class is to enable capturing and passing a generic Type. In order to capture the generic type and retain it at runtime
So its sort of a class that makes sure we can use generic types like List<T> and helps us with type information.
So what we do is that we now take the response and tell the framework that the body is a list of strings directly. We dont need to do collectList anymore as the framework will stick it in a list for us. We then call block to wait in the response.
Your Springboot API returns result as parsed to JSON (this is default behavior). So it first builds a list of Strings (in your case just a single String "city" and than serializes it to Json. In this case since it is a list it serializes it to JSON array as opposed to JSON Object. Read about JSON here. So in your second Springboot app that hits the API from the first one should assume that you are getting JSON which you need to parse to get your list. To parse it you can use readValue() method of ObjectMapper class of Json Jackson library which is a default JSON library in Springboot. your code would be
List<String> myList;
ObjectMapper = new ObjectMapper();
//Add setters for ObjectMapper configuration here if you want a specific config
try {
myList = objectMapper.readValue(myJsonString, List.class);
} catch(IOException ioe) {
...
}
In addition I wrote my own Open-source library called MgntUtils, that includes JsonUtils class which is a thin wrapper over Json Jackson library. It provides just Json parser and serializer, but in many cases that is all you need. With my library you would only need one dependency as oppose to Jackson, and JsonUtils class just have 4 methods, so by far easier to understand. But in your case if you use my library the code would be very similar to the above code. It would be something like this:
List<String> myList;
try {
myList = JsonUtils.readObjectFromJsonString(myJsonString, List.class);
} catch(IOException ioe) {
...
}
Note that in this case you won't have to instantiate and configure ObjectMapper instance as readObjectFromJsonString is a static method. Anyway if you are interested in using my library you can find maven artifacts here and The library itself with source code and javadoc is on Github here. Javadoc for JsonUtils class is here

Custom handler for response body in Jackson / Spring

I am trying to intercept the object that is being returned in my controller so that I can create a flat JSON structure of the response, before Spring invokes Jackson's serialization process.
I am going to support a query parameter that allows the client to flatten the response body. Something like:
/v1/rest/employees/{employeId}/id?flat=true
The controller method looks something like:
public Employee getEmployee(...) {}
I would like to avoid implementing this flattening logic in every one of my service calls and continue to return the Employee object.
Is there some kind of facility in Spring that would allow me to A) read the query string and B) intercept the object that is being returned as the response body?
Here's one idea. There may be a better way, but this will work:
Define an extra request mapping to do the flat mapping:
#RequestMapping(path = "/endpoint", params = {"flat"})
public String getFlatThing() {
return flatMapper.writeValueAsString(getThing());
}
// The Jackson converter will do its ordinary serialization here.
#RequestMapping(path = "/endpoint")
public Thing getFlatThing() {
return new Thing();
}
the "flatMapper" implementation can be whatever you like so long as it works.
One option is to use Jackson's ObjectMapper to write the value as json first and then use https://github.com/wnameless/json-flattener to flatten that to your desired output. There may also be a way to define a custom ObjectMapper that does flat mapping, though that would take some more work on your part.

Camel route - Ignore return value

I have the following route:
from("INPUT_QUEUE")
.routeId("Test")
.beanRef("logService", "save")
.beanRef("deserialiser", "deserialise")
The "save" method in LogService returns an integer. I do not want this integer to be passed on to the deserialise method. I need the json coming from the input queue to be passed on.
Any help is appreciated and apologies for the simple question as I'm new to camel.
Use the multicast pattern, that allows to route the same message to a number of endpoints and process them in a different way:
from("INPUT_QUEUE")
.routeId("Test")
.multicast()
.beanRef("logService", "save")
.beanRef("deserialiser", "deserialise")
.end();
No the bean component will use the return value of the method as the new message body.
However you can use the language component and set it to not transform, something a like
.to("language:bean:logService.save?transform=false")
I can't remember the exact syntax, but I think you can use dot to separate the method name to call, as shown above
http://camel.apache.org/language
You can define your save() to have an argument like Exchange exchange
save(yourExistingArguments, Exchange exchange){
//performYourOperations and get your integer
//However, do not set the integer in the exchange body.
// exchange.getIn().setBody(integer)
// Do not return the integer. Set the integer as state and make it accessible through getter and setter
That is all. Your message gets passed on as it is without the integer

apache camel and jackson

I'm trying out apache-camel, and I've set up a basic route that calls an http service via http4 component, transforms the result via unmarshal().json(JsonLibrary.Jackson), and then prints out part of the response in a bean component.
The problem I'm having is that it blows up at runtime when it gets to the json unmarhsaller:
No type converter available to convert from type: java.util.HashMap to the required type: com.xxx.MyType
The response is of this format:
{"data":[{"x":"y"},{"x":"z"}]}
And my object model is like:
#lombok.Data
class Response {
private List<Elem> data;
}
#lombok.Data
class Elem {
private String x;
}
So it would appear that the unmarshaller thinks the response is a hash map, whereas I want it to unmarshal into an object structure. Is there a way to get it to do what I want?
Found the answer, posting in case anyone else runs into this. The route builder should be setup like:
from("direct:start").to("http4://...").unmarshal().json(JsonLibrary.Jackson,com.xxx.Response)
.to("bean:com.xxx.MyResponseEchoer")
I.e. pass the class type to the json method.

Categories