Apache Camel: set an object dependency before operation with Spring - spring

In this example, I am trying to set the object dependency before calling businessLogic. I am receiving a nullpointer because that 'consumer' object is not set.
Here is the basis of the example and mostly trying to use the Spring DSL.
http://camel.apache.org/polling-consumer
Section: Timer based polling consumer
Here is my camel/spring config:
<bean id="simpleOutboxMessageConsumer" class="org.berlin.camel.esb.logs.mq.SimplePrintMessageConsumer"/>
<!-- Continue with spring dsl for ESB -->
<camelContext id="myCamel" xmlns="http://camel.apache.org/schema/spring">
<!-- Define a MQ consumer template -->
<consumerTemplate id="consumer" />
....
</camelContext>
<route id="fromOutboxAndConsume">
<from uri="timer://foo?period=30000" />
<to uri="bean:simpleOutboxMessageConsumer?method=businessLogic" />
</route>
Java code
#Component
public class SimplePrintMessageConsumer {
private static final Logger logger = Logger.getLogger(SimplePrintMessageConsumer.class);
private int count;
#Autowired
private ConsumerTemplate consumer;
public void setConsumer(final ConsumerTemplate consumer) {
this.consumer = consumer;
}
public void businessLogic() {
logger.info("Launching business logic to consume outbox, blocking until we get a message >>>");
while (true) {
// Consume the message
final String msg = consumer.receiveBody("activemq:queue.outbox", 3000, String.class);
logger.info("Printing message found from queue: " + msg);
if (msg == null) {
// no more messages in queue
break;
}
}
}
}
There is a nullpointer at the usage of the consume object. I am thinking that spring is not just autowiring that bean properly. Even if I didn't use spring, how would I pass the consumer template object to this bean?

This should work
<bean id="simpleOutboxMessageConsumer" class="....SimplePrintMessageConsumer">
<property name="consumer" ref="consumer"/>
</bean>
Remove the #AutoWire , I am checking on why the #Autowire is not working by the way

Related

Spring Boot Quartz, Integration Inbound channel adapter and trigger bindings

We have Quartz Job + File Integration adapter using XML configurations and is working fine, however since trying to move to Spring Boot and these configurations to annotations
Below is the XML configuration for which I am looking for equivalent annotations and bindings
<bean id="scheduler"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="schedulerContextAsMap">
<map>
<entry key="inboundadapterendpoint"><ref bean="incomingfiles" /></entry>
<entry key="inboundendpointtrigger"><ref bean="incomingfiles-trigger"/></entry>
</map>
</property>
</bean>
<bean id="inboundendpointtrigger" class="abc.xyz.Trigger" />
<file:inbound-channel-adapter id="incomingfiles" channel="xyz" directory="" scanner="recursiveScanner" auto-startup="false" prevent-duplicates="false">
<integration:poller task-executor="fileChannelTaskExecutor" trigger="incomingfiles-trigger" max-messages-per-poll="1">
</integration:poller>
</file:inbound-channel-adapter>
How do we get the Inbound Adapter Bean & poller trigger
created using annotations below is injected during scheduler factory creation in spring Boot quartz config
#Bean
#InboundChannelAdapter(poller = #Poller(trigger="customTrigger")
public MessageSource<File> fileReadingMessageSource() {
}
Thanks in advance for any help or suggestions regarding the same
Artem Bilan, thanks very much for the response.
Follow up question post trying out the code provided in response
#Configuration
public class QuartzConfig {
#Autowired
private CustomTrigger inboundEndPointTrigger;
#Autowired
private AbstractEndpoint incomingFiles;
#Bean
public SchedulerFactoryBean schedulerFactoryBean() {
System.out.println("incomingFiles value is " + incomingFiles);
}
}
#Bean(name = "incomingFiles")
#InboundChannelAdapter(autoStartup = "false", value = "xyz", poller = #Poller(trigger = "inboundEndPointTrigger", maxMessagesPerPoll = "2", errorChannel = "abc"))
public MessageSource<File> fileReadingMessageSource() {
}
Output of above is reference for errorLogger instead of Inbound Channel Adapter.
incomingFiles value is bean '_org.springframework.integration.errorLogger'
How do i bind the exact Inbound Adapter with name incomingFiles to scheduler factory ?
Update after trying out with #EndPointId...
#Bean
#EndPointId("incomingFiles")
#InboundChannelAdapter(autoStartup = "false", value = "xyz", poller = #Poller(trigger = "inboundEndPointTrigger", maxMessagesPerPoll = "2", errorChannel = "abc"))
public MessageSource<File> fileReadingMessageSource() {
}
System.out.println("incomingFiles value is " + incomingFiles); print in QuartzConfig class above is still giving a reference to Logger object
incomingFiles value is bean '_org.springframework.integration.errorLogger'
Found the response in below SO (Spring Cloud Stream + Quartz ) on how the bean will be created for Inbound Channel Adapter.
Since the AbstractEndPoint is returning the logger reference instead of InboundChannelAdapter ,
is it ok to get the Inbound Adapter channel bean reference via application context in scheduler factory ? are there any issues with this ?
try {
ApplicationContext applicationContext = (ApplicationContext) context.getScheduler().getContext().get("applicationContext");
AbstractEndpoint abstractEndPoint = (AbstractEndpoint) applicationContext
.getBean("fileConfig.fileReadingMessageSource.inboundChannelAdapter");
} catch(SchedulerException ex) {
}
fileConfig is the Spring File integration configuration class name where InboundChannelAdapter is defined..
I'm not sure why you have closed your old question - you could just edit it properly and we would have a link to other question. But anyway, the answer is like this:
Any XML bean definition could be declared in Java & annotation config with a #Bean method definition.
So, that <bean id="inboundendpointtrigger" class="abc.xyz.Trigger" /> would be in Java like this:
#Bean
Trigger customTrigger() {
return new abc.xyz.Trigger();
}
You already use its bean name as a reference in the #Poller.
The same you do for the SchedulerFactoryBean and configure its setSchedulerContextAsMap() in that #Bean method. You have already a customTrigger() bean for reference and do get access to the SourcePollingChannelAdapter endpoint based on the #InboundChannelAdapter, you need to inject into a #Bean method. Kinda this:
#Bean
SchedulerFactoryBean schedulerFactory(Trigger trigger, SourcePollingChannelAdapter endpoint) {
}
Don't forget to use an #InboundChannelAdapter(autoStartup = "false") as Gary recommends in other SO question.

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.

Can I use both configuring SI with annotation in java file and xml?

Last year, spring integration released 4.0 version for us to configure using annotation without configuring in XML files. But I want to use this feature using the existing XML configurations.
So I wrote the code using spring boot and integration annotation
#Configuration
#ComponentScan(basePackages ={"com.strongjoe.blue"},excludeFilters=#ComponentScan.Filter(type=FilterType.REGEX, pattern={"com.strongjoe.blue.agent.Bootstrap*"}))
#IntegrationComponentScan
#ImportResource( {"${context.agent.path}context-bean-*.xml", // Context Configuration
"${project.agent.path}context-properties.xml" } ) // Project Based Chain configuration
public class AgentStarter implements CommandLineRunner{
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Lazy
#Bean
#ServiceActivator(inputChannel="blue-hub-start-channel", outputChannel="blue-hub-route-channel")
public <T> BlueMessage<T> startJob(BlueMessage<T> msg) {
logger.debug("BluE Hub Agent started :{} [phrase:{}]", System.currentTimeMillis() , "prototype-version");
return msg;
}
#Lazy
#Bean
#ServiceActivator(inputChannel="blue-hub-end-channel")
public <T> BlueMessage<T> endJob(BlueMessage<T> msg) {
logger.debug("BluE Hub Agent ended :{} [phrase:{}]", System.currentTimeMillis() , "prototype-version");
return msg;
}
#Bean
#Transformer(inputChannel="blue-normalized-channeel", outputChannel="blue-output-channel")
public org.springframework.integration.transformer.Transformer JsonToMap( ) {
return new JsonToObjectTransformer( List.class );
}
#MessageEndpoint
public static class Echo {
#ServiceActivator(inputChannel="blue-output-channel")
public void stringEcho(Message message) {
}
}
#Autowired
Gateway gateway;
public static void main(String[] args) {
SpringApplication app = new SpringApplication(AgentStarter.class);
app.setWebEnvironment(false);
app.run(args).close();
}
#Override
public void run(String... args) throws Exception {
System.err.println("blue-hub-agent started..");
System.out.println(gateway.sendReceive("gitlab"));
}
And I wrote the definition about every channel I use in the xml.
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:int="http://www.springframework.org/schema/integration"
xmlns:int-ws="http://www.springframework.org/schema/integration/ws"
xmlns:int-http="http://www.springframework.org/schema/integration/http"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/integration http://www.springframework.org/schema/integration/spring-integration-4.0.xsd
http://www.springframework.org/schema/integration/ws http://www.springframework.org/schema/integration/ws/spring-integration-ws.xsd
http://www.springframework.org/schema/integration/xml http://www.springframework.org/schema/integration/xml/spring-integration-xml.xsd
http://www.springframework.org/schema/integration/http http://www.springframework.org/schema/integration/http/spring-integration-http-4.0.xsd">
<int:channel id="blue-normalized-channel" />
<int:channel id="blue-header-channeel" />
<int:channel id="blue-request-channel" />
<int:channel id="blue-output-channel" />
<int:channel id="blue-gitlab-request-prepare-channel" />
<int:channel id="blue-hub-start-command-channel" />
<int:channel id="blue-hub-start-channel"/>
<int:channel id="blue-hub-end-channel" />
But I got error.
Caused by: org.springframework.messaging.MessageDeliveryException: Dispatcher has no subscribers for channel 'application:8090.blue-hub-start-channel'.
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:81)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:255)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:223)
The reason will be, I think,
that spring bean in XML file and spring bean with the annotation has different context. So I think that even if blue-hub-start-channel is subscribed by the service activator named "startJob", it got error.
How can I solve this problem?
Annotating #ServiceActivator on #Bean is not for POJO Messaging. See the documentation.
When you annotate #Beans this way, you have to provide an appropriate object (MessageHandler in this case).
For POJO style annotated methods, the annotation must be on a method in a #Bean method (like you have on this one...
#MessageEndpoint
public static class Echo {
#ServiceActivator(inputChannel="blue-output-channel")
public void stringEcho(Message message) {
}
}
... and then declare a #Bean for Echo.
You can put all your #ServiceActivator methods (and #Transformers) in the same #MessageEndpoint.

How to annotate Jersey POJO when Impl is remote?

I have 2 JVM's.
JettyJVM
Runs http requests and has an interface CarFacade that is backed using RmiProxyFactoryBean to the CarFacadeImpl running in the CoreJVM
<bean class="org.springframework.remoting.rmi.RmiProxyFactoryBeanFactory">
<property name="serviceInterface" value="org.foo.CarFacade"/>
<property name="serviceUrl" value="rmi://#{HOST}:1099/CarFacade"/>
</bean>
CoreJVM
Runs core business logic in a spring container and has CarFacadeImpl
<bean id="carFacade" class="org.foo.impl.CarFacadeImpl"></bean>
<bean class="org.springframework.remoting.rmi.RmiServiceExporter">
<property name="service" ref="carFacade"></property>
<property name="serviceInterface" value="org.foo.CarFacade"></property>
<property name="serviceName" value="CarFacade"></property>
<property name="replaceExistingBinding" value="true"></property>
<property name="registryPort" value="1099"></property>
</bean>
This setup works currently for flex/blazds and my services are exposed nicely.
Is there any way I can also expose this via Jersey?
I tried with the annotations on the Impl (preferred) but the component scan doesn't find the annotations (obviously as the interface doesn't have them)
So I tried with the annotations on the Interface but jersey says it can't instantiate the interface.
// CarFacadeImpl.java - when I had the annotations on the class in the CoreJVM
#Path("car")
public class CarFacadeImpl implements CarFacade {
#GET
public String getName() {
return "CarFacade";
}
}
// CarFacade.java - When I had the annotations on the interface in JettyJVM
#Path("car")
public class CarFacade {
#GET
String getName();
}
I would really like to not have to write an additional layer just to expose via rest.
I have tried the examples from here http://www.webappsolution.com/wordpress/2012/03/23/one-java-service-pojo-for-amfxmljson-with-spring-blazeds-jersey-jax-rs/ and they work without the RMI call in between.
I found an answer, at least for Jersey 2.16. It does require the annotations to be on the interface, but this is far better than creating a whole new layer
Override the default path scanning registration and register using something similar to this:
// Jersey ResourceConfig
final ResourceConfig rc = new ResourceConfig();
// Get my Spring context
final ApplicationContext context = new ClassPathXmlApplicationContext("clientContext.xml");
// Use Springs class path scanner to find #Path annotated classes
ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(false) {
#Override
protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
return beanDefinition.getMetadata().isIndependent();
}
};
scanner.addIncludeFilter(new AnnotationTypeFilter(Path.class));
// For each class found in package (and sub packages)
for (BeanDefinition bd : scanner.findCandidateComponents("example")) {
try {
// Get the class
Class clazz = HttpServer.class.getClassLoader().loadClass(bd.getBeanClassName());
// Get the proxy
Object bean = context.getBean(clazz);
if (bean != null) {
// Register the proxy with the interface
rc.register(bean, clazz);
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}

Exception handling for Spring 3.2 "#Scheduled" annotation

How to customize the exception handling for #Scheduled annotation from spring ?
I have Cron jobs which will be triggered in the server (Tomcat 6) and when any exceptions occur I need to do some handling.
Spring version 3.2
Tomcat Server 6
If you want to use Java Config you will need to create configuration implementing SchedulingConfigurer
#EnableScheduling
#Configuration
class SchedulingConfiguration implements SchedulingConfigurer {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ThreadPoolTaskScheduler taskScheduler;
SchedulingConfiguration() {
taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setErrorHandler(t -> logger.error("Exception in #Scheduled task. ", t));
taskScheduler.setThreadNamePrefix("#scheduled-");
taskScheduler.initialize();
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler);
}
}
You can modify error handler for your needs. Here I only log a message.
Don't forget to call taskScheduler.initialize();. Without it you'll get:
java.lang.IllegalStateException: ThreadPoolTaskScheduler not initialized
You could implement and register an ErrorHandler for the ThreadPoolTaskScheduler that is used for your scheduling annotations.
<task:annotation-driven scheduler="yourThreadPoolTaskScheduler" />
<bean id="yourThreadPoolTaskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="poolSize" value="5" />
<property name="errorHandler" ref="yourScheduledTaskErrorHandler" />
</bean>
<bean id="yourScheduledTaskErrorHandler"
class="com.example.YourScheduledTaskErrorHandler"/>
Why not wrap your business logic and do a simple try catch in your #schedule method. Then you can log or take whatever action is necessary for failure cases.
#Scheduled(cron = "${schedulerRate}")
public void scheduledJob() {
try {
businessLogicService.doBusinessLogic();
} catch (Exception e) {
log.error(e);
}
}

Resources