Why does this Apache Camel intercept not work? - java

I have a main route builder:
public class MainRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
from("activemq:a.out").to("activemq:b.in");
from("activemq:b.in").bean(MainMessageConsumer.class);
}
}
I have a second "intercept" route builder:
public class InterceptRouteBuilder extends RouteBuilder {
#Override
public void configure() throws Exception {
interceptSendToEndpoint("activemq:a.out").to("activemq:c.in").skipSendToOriginalEndpoint();
from("activemq:c.in").bean(InterceptMessageConsumer.class);
}
}
Both of which are registered to the CamelContext (MainRouteBuilder is registered first, and InterceptRouteBuilder second). However, when I send a message to "activemq:a.out" via:
public class App {
#Produce(uri="activemq:a.out")
private Producer producer;
public void run() {
producer.request("hello");
}
}
The message still arrives on MainMessageConsumer instead of being intercepted. What am I doing wrong?

The interceptor only applies for all routes in the same route builder class. If you want it to work on both, then create a base class, and put the interceptor there, and let the other routes extend your base class, and call its super in the configure method (eg OO inheritance)

Seems to be that if you create your producer using the #Produce annotation, then it won't be intercepted. Whereas if I put:
#Bean
public ProducerTemplate producerTemplate() {
return camelContext().createProducerTemplate();
}
In my application config, and use that instead then it does get intercepted. Not sure if this is the expected behaviour?

Related

Query regarding unit testing of camel multiple routes within the same class

I'm having issues unit testing multiple camel routes within the same class.I have a class which contains several route definitions. e.g. as below:
public class A extends RouteBuilder{
#Override
public void configure() throws Exception {
method1();
method2();
method3();
...
}
private void method1() {
// contains route definitions
final RouteDefinition route = from("direcct:");
route.id("ID1");
route.setHeader("",simple(""));
route.to("direct:B");
route.end();
}
// similarly other methods
}
i want to unit test each method. so have extended CamelTestSupport. can't override the methods in configure when creating routebuilder since the method are private.
this is what i am doing currently
public class A extends CamelTestSupport {
private A route;
#Override
protected RoutesBuilder createRouteBuilder() throws Exception {
route = new A();
//can't use below configure since methods are private
//#Override public void configure() throws Exception {
// method1();
//}
//logic for mocking and setting endpoints using mockito
return route;
}
#Test
public void method1_test() throws Exception {
}
}
I can test one method while commenting others(camelcontext loads and starts all the routes in a class).If i don't comment the test fails.
Also for testing other methods within the same test class i cant use the same createRoutebuilder.So below are the questions:
How will i test other methods within the same test class
Is there a way to start and stop routeid so i can test only those routes(tries startroute method but since all the routes are already started it doesn't do anything)
Is there any workaround so that individual methods can be tested
Using camel 3.6, spring boot2 and junit 5

How could I take an instance of class in Spring Boot?

It is a class which instance is connected to the external service and it is listening constantly of it.
#Component
public class Service extends PollingBot {
#Value("${token}")
private String token;
#Override
public void onUpdateReceived(Update update) {
if (update.hasMessage()) {
}
}
public void sendMessageToUser(String message) {
try {
execute(sendMessage);
} catch (ApiException e) {
}
}
}
You could see that there is a method called sendMessageToUser which send message. It could not be static because execute method not allow static context. This method could not be separeted to other class. /
So, I have to call this method from other class. However I don't want to create additional instance of Service class otherwise I have two instances which are listen for updates, but I want it is sole class instance doing so.
I have tried to run a Application Context and run method from it, but it was not worked.
So, my question is very simple. How could I run this class non-static(!) method from other class?
By default all spring managed beans are singleton. You need to use #Autowired to inject the bean into other and then you can call the methods of that bean.
#Autowired
private Service service;
public void sendMessage(String message){
service.sendMessageToUser(message);
}
You can use #Autowired annotation to call a method of a bean class(component) in Spring. Also, as mentioned by default beans are singleton in spring so you don't need to worry about creating a single instance explicitly every time.
Try to use the below code in the calling class:
#Autowired
private Service service;
public void sendText() {
service.sendMessage(message);
}

Google Guice - Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private

all
I have some questions regarding Google Guice. Any help is appreciated.
I have a handler interface and an implementation of it.
public interface Handler {
void handle();
}
public class HandlerImpl implements Handler {
private Filter filterOne;
#Override
void handler() {
filterOne.foo();
}
}
Filter is another interface:
public interface Filter {
void foo();
}
There are multiple implementation of it.
public class FilterOne implements Filter {
void foo() {
}
}
public class FilterTwo implements Filter {
void foo() {
}
}
Then in my Guice module:
public class HandlerModule extends AbstractModel {
#Override
protected void configure() {
bind(Handler.class).to(HandlerImpl.class);
}
#Provides
#Singleton
public Handler provideHandler(#Named("filterOne")filter filterOne) {
return new HandlerImpl(filterOne);
}
#Provides
#Singleton
#Named("filterOne")
public Filter provideFilterOne() {
return new FilterOne();
}
#Provides
#Singleton
#Named("filterTwo")
public Filter provideFilterTwo() {
return new FilterTwo();
}
}
With above implementation, I'm always getting the error - Could not find a suitable constructor in HandlerImpl. Classes must have either one (and only one) constructor annotated with #Inject or a zero-argument constructor that is not private.
I'm using #Named to differ two filters. Am I using it wrong?
Is it because I have two implementations of Filter and the Guice cannot tell which one to use when try to provide the HandlerImpl?
You are binding Handler.class twice:
In configure() method: bind(Handler.class).to(HandlerImpl.class);
As a provider:
#Singleton
public Handler provideHandler(#Named("filterOne")filter filterOne) {
return new HandlerImpl(filterOne);
}
The first of these bindings can't work, since HandlerImpl doesn't have a constructor annotated with #Inject. If you fix it though, it still won't work - you can't provide multiple bindings for the same key.
TL;DR: remove the bind(Handler.class).to(HandlerImpl.class); from the configure() method

Bean life cycle - afterPropertiesSet()

I am trying to create CustomWebServiceMessageReceiverHandlerAdapter which extends org.springframework.ws.transport.http.WebServiceMessageReceiverHandlerAdapter.
WebServiceMessageReceiverHandlerAdapter extends abstract WebServiceMessageReceiverObjectSupportwhich implements InitializingBean.
I have a problem, because I don’t understand why I have to call
afterPropertiesSet() in custom handler. I get an error without calling this method: “factory message is required”. But, this method is calling in abstract class, so my custom handler should run afterPropertiesSet() from abstract class. If you know the solution, let me know. Thanks a lot.
edit: This is my CustomWebServiceMessageReceiverHandlerAdapter :
public class CustomWebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverHandlerAdapter {
#Override
protected void handleInvalidXmlException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object handler,
InvalidXmlException ex) throws Exception {
//code
}
#Override
public void afterPropertiesSet() {
}
}
WebServiceMessageReceiverHandlerAdapter and WebServiceMessageReceiverObjectSupport are from Spring Framework
public class WebServiceMessageReceiverHandlerAdapter extends WebServiceMessageReceiverObjectSupport{}
There is no afterPropertiesSet()
public abstract class WebServiceMessageReceiverObjectSupport implements InitializingBean {
private WebServiceMessageFactory messageFactory;
/** Returns the {#code WebServiceMessageFactory}. */
public WebServiceMessageFactory getMessageFactory() {
return messageFactory;
}
/** Sets the {#code WebServiceMessageFactory}. */
public void setMessageFactory(WebServiceMessageFactory messageFactory) {
this.messageFactory = messageFactory;
}
#Override
public void afterPropertiesSet() throws Exception {
Assert.notNull(messageFactory, "messageFactory is required");
}
And now, when I am removing afterPropertiesSet() from my custom handler an exception is thrown. In my opinion, I don't understand something about life cycle of bean.
I'm unsure about your specific case. In general if a bean implements InitializingBean and thus the afterPropertiesSet this method is called after instantiation of the bean instance and after Spring injected all #Autowired properties/values.
In your specific case you need to ensure that messageFactory property of your (via inheritance) class is set. Typically this is done by Spring, if you provide a suitable setter for autowiring:
#Autowired
#Override
public void setMessageFactory(WebServiceMessageFactory messageFactory) {
super.setMessageFactory(messageFactory);
}
If you overrride afterPropertiesSet without calling super.afterPropertiesSet() creation of the bean will work as the assertion of the super implementation is skipped. But you will likely encounter problems further down the line as the messageFactory property is not properly initialized.

Spring #Async ignored

I am having troubles invoking a method asynchronously in Spring, when the invoker is an embedded library receiving notifications from an external system. The code looks as below:
#Service
public class DefaultNotificationProcessor implements NotificationProcessor {
private NotificationClient client;
#Override
public void process(Notification notification) {
processAsync(notification);
}
#PostConstruct
public void startClient() {
client = new NotificationClient(this, clientPort);
client.start();
}
#PreDestroy
public void stopClient() {
client.stop();
}
#Async
private void processAsync(Notification notification) {
// Heavy processing
}
}
The NotificationClient internally has a thread in which it receives notifications from another system. It accepts a NotificationProcessor in its constructor which is basically the object that will do the actual processing of notifications.
In the above code, I have given the Spring bean as the processor and attempted to process the notification asynchronously by using #Async annotation. However, it appears the notification is processed in the same thread as the one used by NotificationClient. Effectively, #Async is ignored.
What am I missing here?
#Async (as well as #Transactional and other similar annotations) will not work when the method is invoked via this (on when #Async is used for private methods*), as long as you do not use real AspectJ compiletime or runtime weaving.
*the private method thing is: when the method is private, then it must been invoked via this - so this is more the consequence then the cause
So change your code:
#Service
public class DefaultNotificationProcessor implements NotificationProcessor {
#Resource
private DefaultNotificationProcessor selfReference;
#Override
public void process(Notification notification) {
selfReference.processAsync(notification);
}
//the method must not been private
//the method must been invoked via a bean reference
#Async
void processAsync(Notification notification) {
// Heavy processing
}
}
See also the answers for: Does Spring #Transactional attribute work on a private method? -- this is the same problem

Categories