Camel route - Ignore return value - java

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

Related

How to avoid if

I have a Request object with field request_type and number of other fields. request_type can be 'additionRequest' , 'deletionRequest' 'informationRequest'.
Based on request_type other fields in Request object are processed differently.
My simple minded approach is
if additionRequest
algorithm1
else if deletionRequest
algorithm2
else if deletionRequest
algorithm3
end
How I can avoid these if statements and still apply proper algorithm?
If you want to avoid conditional statements then you can leverage object oriented features such as:
Map<String, Function<Request, Result>> parsers = new HashMap<>();
parsers.put("additionRequest", request -> {
// parse and generate Result here
return result;
});
Result result = parsers.get(request.request_type).apply(request);
Seems to me that perhaps a Command Pattern could come in handy here. If you make an object structure of these commands and encapsulate the algorithm that you want to execute within the object, then you can construct the specific sub-objects and later on use "execute" method of the command to invoke the algorithm. Just make sure you are using polymorphic references:
if additionRequest
algorithm1
else if deletionRequest
algorithm2
else if deletionRequest
algorithm3
end
will become
void theRequestExecutor(Reqest req) {
req.executeAlgorithm()
}
https://en.wikipedia.org/wiki/Command_pattern
Use HashMap<RequestType, RequestHandler> for this case. RequestHandler can be an interface which will be implemented for each situation you want to handle.
Hope this help.
You can create Map of key:String, value :Function of RequestType ReturnType. Depending on type of request it will call corresponding function.
Example:
Map<String, Function<RequestType, ResponseType> requestProcessors = new HashMap<>;
requestProcessors.add("additionRequest", this::methodToHandleAddRequest);
requestProcessors.add("deletionRequest", this::methodToHandleDeleteRequest);
Inside request handler do
return this.requestProcessors.get(request.request_type).apply(request);
Note you may have to create response interface if different responses are different. Different response type will inherit from response interface
The object-oriented solution is always to include the logic with the data. In this case include whatever you want a request to do in the request object itself, instead of (presumably) passing out pure data.
Something like
public interface Request {
Response apply();
}
Anything else, like creating a map of functions, creating a thin abstraction layer or applying some pattern is a workaround. These are suitable only if the first solution can not be applied. This might be the case if the Request objects are not under your control, for example generated automatically or third party classes.

Camel routing based on object value

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;
}
}
}

Override HttpMessageConverter for Spring RequestBody

I have the following code:
#RequestMapping(value="/mobile/device", method = RequestMethod.PUT)
public ResponseEntity<Void> flagDevice (#RequestBody List<MobileDeviceData> devicedataList, #RequestHeader(value="special_code") String specialCode) {
// Implementation details
}
Each instance of MobileDeviceData that gets created needs to have a param field filled in with the RequestHeader special_code.
How would I go about doing this so that it is fully populated by the time the flagDevice method body gets called?
Thanks in advance.
This is non trivial.
An HttpMessageConverter is already provided that deserializes the JSON, that's the MappingJackson2HttpMessageConverter. It has access to request headers. You could extend that class to also use the headers for deserialization (this is extremely difficult to do generically, as opposed to only for MobileDeviceData).
You could use Spring AOP, intercept the method, retrieve the arguments, cast to the appropriate types, and assign the value yourself.
The solution I would go for is the simplest: do it yourself in the handler method. Loop the the List and use a corresponding setter to set the specialCode for each MobileDeviceData.
Another option is to define your own HandlerMethodArgumentResolver specifically for List<MobileDeviceData> parameters that need to be constructed from header vales.

Default/Constant values for POST/PUT arguments with Retrofit

Using the Retrofit REST Client library from Square, is there anyway of providing default/constant values for POST/PUT fields in a call.
I know about including constant query parameters by simply including them in the path, but this work for Body parameters.
I have an API that looks similar to:
POST /api/create
type=constanta&value={value}
POST /api/create
type=constantb&value={value}&otherValue={otherValue}
where the second variant requires an additional argument to be supplied. Rather than having a single java method that took all three arguments, I was hoping to be able to elide the constants from the method call, something like:
create(String value);
create(String value, String otherValue);
and have retrofit inject the type argument constant.
Given that adding #FormUrlEncoded can be added to modify how the body is encoded, if it's not natively supported by Retrofit, is there anyway of adding my own annotation and injecting such default values? ( It doesn't appear that RequestInterceptor allows one to modify the body.. ).
Maybe one option would be to send an object, which encapsulates all your values, instead of all string values separately? The object would implement your default values.
For example, you could create a class:
public class CreateObject {
private String type = "constant";
private String value;
private String otherValue;
public CreateObject(String value, String otherValue) {
this.value = value;
this.otherValue = otherValue;
}
}
Your class handles your constant. You could just set it to a default value "constant", like I did above, or set it on the fly in the constructor.
Now all you've to do is to create the object with the values and make the request with Retrofit. Instead of using the string values directly, just pass the object. Your interface could look like this:
public interface CreateService {
#POST("/api/create")
void create(#Body CreateObject create, Callback<CreateObject> cb);
}
The request implementation like this:
CreateObject create = new CreateObject("value", "otherValue");
createService.create(create, new Callback<CreateObject)() {…});
This should include all three of your values in the request body, if they are set. If a value is null, it won't be included in the request body. Based on your two examples above, you would now only need one interface method. Which values are sent is based on the createObject you pass on. For example, if you set otherValue as null, it won't be part of the request body.
My examples were modified from: https://futurestud.io/blog/retrofit-send-objects-in-request-body/
Is it possible for you to use Guava or Java 8 Optional as second argument in method? Then if that argument will be absent you can just ignore it

Consuming different input JSON format by same URL and same method in Spring RESTful service

I'm going to implement a RESTful webservice using Spring.
Let it be an ordinary PUT method, something like this:
#RequestMapping(method=RequestMethod.PUT, value="/foo")
public #ResponseBody void updateFoo(#RequestBody Foo foo) {
fooService.update(foo);
}
In such a case input JSON format (if it corresponds to Foo class) will be successfully converted to Foo instance with no extra efforts, or error will be issued in case of wrong format.
But I'd like to make the service able to consume two different types of formats using same method (e.g. PUT) and same URL (e.g. /foo).
So that it possibly looked like:
//PUT method #1
#RequestMapping(method=RequestMethod.PUT, value="/foo")
public #ResponseBody void updateFoo(#RequestBody Foo foo) {
fooService.update(foo);
}
//PUT method #2
#RequestMapping(method=RequestMethod.PUT, value="/foo")
public #ResponseBody void updateFoo(#RequestBody FooExtra fooExtra) {
fooService.update(fooExtra);
}
and Spring converter tried to convert input JSON not only in Foo but in FooExtra as well and invoked corresponding PUT method depending on input format.
In fact, I tried to implement it exactly as it described above but without success. Is it even possible? Maybe, I need some kind of "trick"?
What is the best (and the most proper) way to achieve such behavior? Of course, I could always make two different URLs but I'd like to know whether it is possible with the same one.
Your attempt didn't work simply because Spring tried to match your methods against the request, by looking at url and method type, which are in both cases the same. It does not work like overloading in Java; argument types do not differentiate your methods.
But there are good news. SpringMVC can also examine request headers and request parameters when trying to match your handler methods. Since what you want to pass is actually pure metadata -an alternative format type of the same information- it makes perfect sense to use a custom request header. It's very easy to add custom headers when using a rest api. See the following link for JAX-RS: Adding a custom header.
Now in your server side you should configure the handler methods as:
//PUT method #1
#RequestMapping(method=RequestMethod.PUT, value="/foo", headers="returnType=Foo")
public #ResponseBody Foo updateFoo(#RequestBody Foo foo) {
fooService.update(foo);
}
//PUT method #2
#RequestMapping(method=RequestMethod.PUT, value="/foo", headers="returnType=FooExtra")
public #ResponseBody FooExtra updateFoo(#RequestBody FooExtra fooExtra) {
fooService.update(fooExtra);
}
Note also that if you want to access a return value with #ResponseBody you have to return your object, otherwise make the methods void
For understanding it we should think how Spring works, it uses Dispatcher Servlet. I don't think that spring does "combine" work for different types of input.
So my answer will be: "trick" with two different urls ;)

Categories