Camel, Spring, OSGI: Is there a way to specify the stop method? - spring

I'm running a Camel Spring OSGI application. The Camel context is initialized through Spring.
When the bundle stops, I need to do some clean-up activities, like de-registering the message listener. How do I do that? Is there a method I can override? I understand that an OSGI bundle must provide the activator start and stop methods but my understanding also is that the Camel/Spring/OSGI framework overrides these methods.
My beanx.xml:
<beans>
<camelContext id="camel" xmlns="http://camel.apache.org/schema/spring">
<routeBuilder ref="outboundBuilder" />
</camelContext>
</beans>
My java code:
public class MyRouteBuilder extends RouteBuilder {
public void configure() {
.....
}
}

Just to expand a little on the answer of Bilgin Ibryam which is correct.
Camel has the ability to apply a policy to a route. This Policy controls routes at runtime. This will allow you to do custom logic at certain events of the route life time.
Implementing a route policy.
It is rather simple declare a new class which extends RoutePolicySupport then override the methods you are interested in.
public class MyRoutePolicy extends RoutePolicySupport{
#Override
public void onStart(Route route) {
// TODO Auto-generated method stub
super.onStart(route);
}
#Override
public void onStop(Route route) {
// TODO Auto-generated method stub
super.onStop(route);
}
#Override
public void onExchangeBegin(Route route, Exchange exchange) {
// TODO Auto-generated method stub
super.onExchangeBegin(route, exchange);
}
}
Now use the route in your routebuilder configure() method like this:
RoutePolicy policy = new MyRoutePolicy();
from("timer://blah")
.routeId("Test1").routePolicy(policy)
.setBody().constant("A Message Like Hello World")
.to("mock:meh");
If you were just using a Spring XML with a route then add the following:
<bean id="policy" class="MyRoutePolicy"/>
<camelContext xmlns="http://camel.apache.org/schema/spring">
<route id="foo" routePolicyRef="MyRoutePolicy">
<from uri="timer://blah"/>
<setBody><constant>A Message Like Hello World</constant></setBody>
<to uri="mock:meh"/>
</route>
</camelContext>

You can use Camel Route policy and write your code to cleanup resource when the route is about to stop or be removed from the context.

Related

Use a custom JMS listener's "containerFactory" on a Camel route

I'd like to receive messages using a Camel route, but having the capability to somehow inject a custom "containerFactory".
Usually (without a Camel route), you'd do something like:
#JmsListener(destination = "${some.virtual-topic.queue}",
containerFactory = "customJmsListenerContainerFactory")
public void receiveMessage(String message) throws Exception {
// do something cool with the received message ...
}
Note how the "containerFactory" property of the "JmsListener" annotation above provides us with a way of using a non default "containerFactory". That works fine, but what if instead we'd like to use a Camel route for reading from the queue? Something like:
#Component
public class TestRoute extends RouteBuilder {
#Override
public void configure() throws Exception {
from("activemq:queue:{{some.virtual-topic.queue}}")
.bean(MessageFacade.class, "process");
}
}
In this latest case above, I've not been able to "inject" a custom JMS containerFactory. Does anybody knows if this is possible (in a non-hack way)? or if not then we'll have to rely on the standard listener.
See the documentation: https://camel.apache.org/components/latest/activemq-component.html
The options consumerType should be set to Custom and messageListenerContainerFactory should refer to the bean id of your container factory implementation.

Mocking bean endpoints with method option in uri

I have an application build using Apache Camel 2.15.3. And I'm wiring the routes using spring-xml for dependency injection. I'm trying to write a test where I mock an endpoint that is a bean and has a method option in the uri.
My routes looks like this:
<onException id="Exception">
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
<to uri="direct:fear"/>
</onException>
<route id="happyStory">
<from uri="direct:inTheBeginning"/>
<to uri="bean:enchantedKingdom?method=warn" />
<to uri="bean:fluffykins" />
</route>
<route id="scaryStory">
<from uri="direct:fear"/>
<onException>
<exception>java.lang.Exception</exception>
<handled>
<constant>true</constant>
</handled>
</onException>
<to uri="bean:monster"/>
<choice>
<when>
<simple>${header.succesfullywarned}</simple>
<to uri="bean:enchantedKingdom?method=hide"/>
</when>
<otherwise>
<to uri="bean:enchantedKingdom?method=panic" />
</otherwise>
</choice>
</route>
And I wan't to be able to say that when the bean method warn is called then the header "succesfullywarned" should be set in the message and then when the bean fluffykins is called there should be a exception that causes the message to get sent to "scaryStory" and in this case I wan't to assert that the bean method 'panic' is called.
This is the test:
#RunWith(CamelSpringJUnit4ClassRunner.class)
#ContextConfiguration({"/META-INF/spring/route-stories.xml","/META-INF/spring/beans.xml"})
#MockEndpointsAndSkip("(bean:fluffykins|bean:monster|bean:enchantedKingdom?method=warn|bean:enchantedKingdom?method=hide|bean:enchantedKingdom?method=panic)")
public class StoryHappyRouteTest extends CamelSpringTestSupport {
private String url = "direct:inTheBeginning";
#Autowired
private ApplicationContext applicationContext;
#Override
protected AbstractApplicationContext createApplicationContext() {
return (AbstractApplicationContext)applicationContext;
}
#Test
public void test(){
MockEndpoint warn = getMockEndpoint("mock:bean:enchantedKingdom?method=warn");
MockEndpoint fluffy = getMockEndpoint("mock:bean:fluffykins");
MockEndpoint monster = getMockEndpoint("mock:bean:monster");
MockEndpoint hide = getMockEndpoint("mock:bean:enchantedKingdom?method=hide");
MockEndpoint panic =
getMockEndpoint("mock:bean:enchantedKingdom?method=panic");
fluffy.whenAnyExchangeReceived(new Processor() {
#Override
public void process(Exchange exchange) throws Exception {
System.out.println("Bunny!");
throw new NullPointerException();
}
});
template.sendBody(url,"");
warn.assertExchangeReceived(0);
fluffy.assertExchangeReceived(0);
monster.assertExchangeReceived(0);
panic.assertExchangeReceived(0);
}
}
It works fine for all the beans except the enchantedKingdom bean, that contains multiple methods that are used in the route. A mock is not used in this case but the real bean method is called, which is not what I wan't. And the test fails since since it is not the mock that gets called in the route.
How can I get the test to use a mock for the endpoits with uri 'bean:enchantedKingdom?method=warn', 'bean:enchantedKingdom?method=hide' and 'bean:enchantedKingdom?method=panic'?
I would change the test approach and not try to mock calls to beans.
Instead, create a mocked bean instance and use that in your test.
Define a mocked bean (by code or mocking library)
public class MockEnchantedKingdom {
public boolean panicCalled = false;
public void panic() {
// do things
panicCalled = true;
}
}
Then declare this bean in spring files used for testing purposes
#ContextConfiguration(
{"/META-INF/spring/route-stories.xml",
"/META-INF/spring/beans-test1.xml"})
And in your test code get the bean and assert what you need
// context is CamelContext you should have access to it
MockEnchantedKingdom enchantedKingdom = (MockEnchantedKingdom) context.getRegistry().lookupByName("enchantedKingdom");
Assert.asserttrue(enchantedKingdom.panicCalled);
Create a different beans-test*.xml for different tests, and you may create the mocks using Mockito or any other library.
The route code is always the same and you can control behaviour of beans in each test.

CamelContext not picking up Spring annotated Routes

I have just started to use Camel in one of my projects. I am trying to configure Camel with Spring, but having issues doing that.
I don't want to use xml configuration but rather go with Spring based Annotations for configuring Routes and Processors.
My App is a stand alone Spring Application, which will be run as Jar.
To keep the app running, I've a empty scheduled method which runs every x min.
Below are the dependencies in my build.gralde
// Spring //
compile('org.springframework:spring-core:5.0.0.RC2')
compile('org.springframework:spring-context:5.0.0.RC2')
compile('org.springframework:spring-beans:5.0.0.RC2')
compile('org.springframework:spring-context-support:5.0.0.RC2')
// Apache //
// Camel //
compile('org.apache.camel:camel-core:2.19.1')
compile('org.apache.camel:camel-spring:2.19.1')
snapshot of beans.xml
<context:annotation-config/>
<tx:annotation-driven/>
<context:component-scan base-package="my.package" />
<camelContext id="aggregatorCamelContext" autoStartup="true" xmlns="http://camel.apache.org/schema/spring">
<package>
my.package.camel
</package>
</camelContext>
Sample RouteBuilder
#Component
public class SampleRoute extends RouteBuilder {
#Autowired
MyClass myObject;
#Override
public void configure() throws Exception {
from("file:filelist")
.process(myObject)
.to("file:processedList");
}
}
To keep the app alive ( I know bit hacky, but suffices for now )
#Component
#EnableScheduling
public class KeepitAlive {
#Scheduled(fixedRate = 1000l)
public void run(){
System.out.println("KeepitAlive.run "+ Thread.currentThread().getName() );
}
}
Main Class. I have tried both the methods, Initializing Spring context as well as Camel Main, but to no luck
public class MyApplication {
public static void main(String[] args) throws Exception {
/*AbstractXmlApplicationContext context =
new ClassPathXmlApplicationContext("path/to/beans.xml");*/
Main main = new Main();
main.setApplicationContextUri("path/to/beans.xml");
main.start();
}
}
If I put my Route within camelContext declaration itself, it works absolutely fine,
<route>
<from uri="file:filelist"/>
<to uri="file:processedlist"/>
</route>
I've also looked into Camel Spring Integration documentation, but it also contains xml based configuration.
Could anybody please guide me in right direction.
You are using Camel's own package scanning via
<package>
my.package.camel
</package>
You should use <contextScan> if you want Camel to find the Spring #Component Camel routes.
See the Camel spring documentation for more: http://camel.apache.org/spring.html
Figured it out ultimately. Need to extend SpringRouteBuilder instead of RouteBuiler above in SampleRoute class.
Anybody struggling with issues, I suggest once go through Camel in Action book.
Somehow I missed it in the beginning which costed me lot of time figuring out trivial things that this book covers.

Spring MVC HandlerInterceptor ignored in Java config

I'm trying to convert a Spring project from XML to Java config and have run into the following issue with HandlerInterceptors:
XML Config (works):
<mvc:annotation-driven />
<mvc:interceptors>
<bean class="com.mycompany.MyHandlerInterceptor" />
</mvc:interceptors>
Java Config (interceptor is never called)
#Configuration
public class MvcConfig extends WebMvcConfigurationSupport {
#Override
protected void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new MyHandlerInterceptor());
}
// ...
}
According to the documentation, these two configurations should be equivalent, however in the Java config example the neither the pre or post handle methods are ever called?
What am I missing?
Thanks.
This was my own fault. I had overridden requestMappingHandlerMapping() in my MVC Java config and did not set the interceptors property on the custom HandlerMapping class.
#Bean
#Override
public RequestMappingHandlerMapping requestMappingHandlerMapping() {
CustomRequestHandlerMapping handlerMapping = new CustomRequestHandlerMapping();
handlerMapping.setOrder(0);
handlerMapping.setInterceptors(getInterceptors()); // <-- This was missing
return handlerMapping;
}

How to access Spring RequestContext from a Freemarker TemplateDirectiveModel

I'm using Spring MVC with Freemarker as view technologie. I have a TemplateDirectiveModel object which needs to access Spring's RequestContext within the execute method. Currently I do it like this:
public class MyDirective implements TemplateDirectiveModel
{
public void execute(Environment env, Map params, TemplateModel[] loopVars,
TemplateDirectiveBody body) throws TemplateException, IOException
{
StringModel model = (StringModel) env.getGlobalVariable("springMacroRequestContext");
RequestContext requestContext = (RequestContext) model.getWrappedObject();
}
}
But I can't believe that this is the right way to do it. I have the feeling I missed something important. Maybe there are special classes and annotations for handling Freemarker direcives in Spring? Maybe I can let Spring inject something into the directive class with which I can access Springs request scope?
You could subclass FreeMarkerConfigurer, overriding its postProcessConfiguration(Configuration config)method.
Your implementation would just put a request-aware dependency in the configuration, as a shared variable for example (as preconised by the FM documentation).
Should do the trick, Spring-style...
There is an easier way to do this. If you are already using spring's FreeMarkerConfigurer, you can hand it a map of variables:
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer"
p:templateLoaderPath="/some_path_here">
<property name="freemarkerVariables">
<map>
<entry key='macroName' value-ref="templateModelRef" />
</map>
</property>
</bean>
<bean id="templateModelRef" class="...class..extends TemplateModel">
<property name="someResource" value-ref="resourceRef"/>
</bean>
Now at least in a class that extends TemplateDirectiveModel's execute method you have access to that injected property.
public class MyDirective extends TemplateDirectiveModel {
private MyResource someResource;
#Override
public void execute(Environment env, Map params, TemplateModel[] loopVars,TemplateDirectiveBody body) throws TemplateException, IOException {
StringModel sharedVariable = (StringModel)env.getConfiguration().getSharedVariable("beanName");
MyClass sweetness = (MyClass)sharedVariable.getWrappedObject();
}
}
Now in your .ftl you can use:
<#macroName />
and it will have spring dependencies auto injected.

Resources