Apache Camel inOut routes, out exchange marshaling/unmarshaling with jaxb - activemq

we have this apache camel route,
from(commandDrop).marshal(jaxbDataFormat).to(jmsQueue);
from(jmsQueue).inOut("jms:doCommand?requestTimeout=500000");
from("jms:doCommand").unmarshal(jaxbDataFormat).beanRef("bean");
.... and a bean class like this
class BeanClass {
public void doCommand(Command command, Exchange exchange){
{
command.run();
exchange.getOut().setBody(command);
}
}
we are trying to put a message and wait for a reply on the route like this
Object ret = template.requestBody(commandDrop, new TestCommand());
Objects on the route in the forward direction are getting marshaled/unmarshaled splendidly. But the setBody call is causing a java.io.NotSerializableException. Is there a way to configure the route to use the same jaxb marshaling/unmarshaling on the way back? My Command class contain some jaxb- generated class objects that are not serializable. They are handled well by the marshal/unmarshal in the forward direction and would be great if they can be on the way back. Am relatively new to camel so not sure if this is the best way to go about this.
thanks a bunch.

You can marshal it after the bean invocation
from("jms:doCommand").unmarshal(jaxbDataFormat).beanRef("bean").marshal(jaxbDataFormat);

Related

Getting Request Object in Spring Webflux elastic thread

I am facing one issue.
I am calling some API's in parallel using Spring Webflux. If any child thread faces any issue, it needs to log the request. Now the issue is , for logging a normal POJO class in which there is a static method which gets a bean via ApplicationContent and store the data in a Queue.
Now the issue is, I want to access request params like Request URL / Controller etc.
I tried
ServletRequestAttributes sra =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
logger.error("====="+sra);
HttpServletRequest httpRequest = sra.getRequest();
but In this case, sra is null. I tried adding the following code,
#Configuration
public class InheritableRequestContextListener extends RequestContextListener {
private static final String REQUEST_ATTRIBUTES_ATTRIBUTE =
InheritableRequestContextListener.class.getName() + ".REQUEST_ATTRIBUTES";
#Override
public void requestInitialized(ServletRequestEvent requestEvent) {
System.out.println("111111111111111111");
if (!(requestEvent.getServletRequest() instanceof HttpServletRequest)) {
throw new IllegalArgumentException(
"Request is not an HttpServletRequest: " + requestEvent.getServletRequest());
}
HttpServletRequest request = (HttpServletRequest) requestEvent.getServletRequest();
ServletRequestAttributes attributes = new ServletRequestAttributes(request);
request.setAttribute(REQUEST_ATTRIBUTES_ATTRIBUTE, attributes);
LocaleContextHolder.setLocale(request.getLocale());
RequestContextHolder.setRequestAttributes(attributes, true);
}
}
but this is not helping. Can anyone help. I am using springboot version ; 2.0.2.RELEASE.
There are several reasons as to why your implementation does not work.
Webflux is thread agnostic, which means that any thread can deal with anything at anytime in the application. If the application finds it efficiant to switch the current executing thread, it will do so.
Servlet applications on the other hand assigns one thread to each request and sticks with that thread throughtout execution.
ApplicationContext uses as you can see ServletRequests, so it is not usable in a Webflux application. It in turn uses threadlocal to store the request object to the designated thread.
In webflux you cant use threadlocal, bucase as soon as the application switches threads everything in threadlocal is gone. Thats why you get null.
So how do you pass data from thread to thread.
What you need to do is to implement a filter that intercepts the request, extracs the information you want and places it in the reactive context object.
https://projectreactor.io/docs/core/release/reference/#context
Here is a post that addresses the problem.
https://developpaper.com/implementation-of-requestcontextholder-in-spring-boot-webflux/

Retrieve WS request in CXF Web service

I got a CXF OSGi Web service (based on the example demo in servicemix: https://github.com/apache/servicemix/tree/master/examples/cxf/cxf-jaxws-blueprint)
The Web service works fine and i call all the available implemented methods of the service.
My question is how can i retrieve the request inside a WS method and parse in a string XML format.
I have found that this is possible inside interceptors for logging, but i want also to the WS-Request inside my methods.
For storing the request in the database I suggest to extend the new CXF message logging.
You can implement a custom LogEventSender that writes into the database.
I had similar requirement where I need to save data into DB once method is invoked. I had used ThreadLocal with LoggingInInterceptor and LoggingOutInterceptor. For example in LoggingInInterceptor I used to set the message into ThreadContext and in webservice method get the message using LoggingContext.getMessage() and in LoggingOutInterceptor I used to removed the message(NOTE: Need to be careful here you need to explictly remove the message from thread context else you will end up with memory leak, and also incase of client side code interceptors get reversed.
public class LoggingContext {
private static ThreadLocal<String> message;
public static Optional<String> getMessage() {
return Optional.ofNullable(message.get());
}
public static void setMessage(final String message) {
LoggingContext.message = new ThreadLocal<>();
LoggingContext.message.set(message);
}
}
Not an answer to this question but i achieved to do my task by using JAXB in the end and do some manipulations there.

Apache camel nested routes

I am new to Apache camel. I have very common use case that i am struggling to configure camel route. The use case is to take execution context
Update database using execution context.
Then using event on the execution context, create a byte message and send over MQ.
Then in the next step again use execution context and perform event processing.
Update database using execution context.
So basically its kind of nested routes. In the below configuration I need to have access to the executionContext that executionController has created in the updateSchedulerState, sendNotification, processEvent and updateSchedulerState i.e. steps annotated as 1,2, 3 and 4 respectively.
from("direct:processMessage")
.routeId("MessageExecutionRoute")
.beanRef("executionController", "getEvent", true)
.beanRef("executionController", "updateSchedulerState", true) (1)
.beanRef("executionController", "sendNotification", true) (2)
.beanRef("messageTransformer", "transform", true)
.to("wmq:NOTIFICATION")
.beanRef("executionController", "processEvent", true) (3)
.beanRef("eventProcessor", "process", true)
.beanRef("messageTransformer", "transform", true)
.to("wmq:EVENT")
.beanRef("executionController", "updateSchedulerState", true); (4)
Kindly let me know how should i configure the route for the above use case.
Thanks,
Vaibhav
So you need to access this executionContext in your beans at various points in the route?
If I understand correctly, you can put this executionContext in an exchange Property, and it will persist throughout the route.
Setting the exchange property can be done via the Exchange.setProperty() method or various camel dsl functions such as like this:
from("direct:xyz)
.setProperty("awesome", constant("YES"))
//...
You can access exchange properties from a bean by adding a method argument of type Exchange, like this:
public class MyBean {
public void foo(Something something, Exchange exchange) {
if ("YES".equals(exchange.getProperty("awesome"))) {
// ...
}
}
}
Or via #Property like this:
public class MyBean {
public void foo(Something something, #Property String awesome) {
if ("YES".equals(awesome)) {
// ...
}
}
}
This presumes you are using later versions of camel.
Does this help?

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

Resolving constructor dependency on service used in NancyFX

I have the following bootstrap
public class NancyBootStrapper: DefaultNancyBootstrapper
{
protected override void ConfigureRequestContainer(TinyIoC.TinyIoCContainer container, NancyContext context)
{
base.ConfigureRequestContainer(container, context);
var ravenSession = container.Resolve< IRavenSessionProvider >().GetSession();
container.Register( ravenSession );
}
}
When my Nancy app tries to instantiate BlogService using the following constructor
public BlogService(IDocumentSession documentSession)
{
this.documentSession = documentSession;
}
the application blows up stating that it can't resolve document session, I have also tried the following within my test method (removing the constructor injection).
public void BuildCategories()
{
var container = TinyIoCContainer.Current;
documentSession = container.Resolve< IDocumentSession >();
documentSession.Store(new Category{Title = "test"});
documentSession.Store(new Category{Title = ".net"});
documentSession.SaveChanges();
}
This also blows up, pointing out that it can't resolve documentSession.
Now this is the first time I have used either NancyFX or TinyIoC so I could be doing something fundamentally wrong though I should mention that the documentSession does resolve within a Nancy module..
Can any one offer a fix or some suggestions?
When is the BlogService supposed to be instantiated? -My guess would be once for the application, in which case I believe you are registering the session in the wrong bootstrapper method, and should do it in ConfigureApplicationContainer.
I've been playing & digging into both NancyFx and the TinyIoC code bases and have figured out how to fix this issue... I don't like the fix... but hay it works :)
Basically, I am creating a RavenDB document session in the bootstrapper method configureRequestContainer as it is best practice to use the request as your unit of work scope.
Unfortunately anything that is auto wired by tinyIoC within configureApplicationContainer does not have any constructor injection performed using the child container being used by the Nancy request (this includes those that are marked as MultiInstance or as PerRequestSingleton.
To get around this, you need to re-register any components that depend on your per request components within the same child container.
As I said, I don't like the fix, but it is ultimately a fix :)