Creating message queue in spring boot using apache camel - spring

I'm very newbie to this messaging queue and just started learning some basic stuffs in this.
So for our spring boot application we followed an architecture like contoller talks to service & service talks to repository so here i have to create one controller that will accept a class DTO as a json and post these information to the message queue specified in the apache camel.
I'm following this link ! for my reference that works well but when i tried to implement it in my project , it saying me an error listed below.
Error
Exception encountered during context initialization - cancelling
refresh attempt:
org.springframework.beans.factory.UnsatisfiedDependencyException:
Error creating bean with name 'trackerQueueController': Unsatisfied
dependency expressed through field 'camelContext'; nested exception is
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'org.apache.camel.CamelContext' available:
expected at least 1 bean which qualifies as autowire candidate.
Dependency annotations:
{#org.springframework.beans.factory.annotation.Autowired(required=true)}
I have created an controller,routes & processor as below:
Controller
#RestController
#RequestMapping("/deviceinfo")
public class TrackerQueueController {
#Autowired
CamelContext camelContext;
#Autowired
private
ProducerTemplate producerTemplate;
#PostMapping()
public void startCamel(#RequestBody FieldUpdate fieldUpdate) {
producerTemplate.sendBody("activemq:topic:in", fieldUpdate);
}
}
Routes
#Component
public class TrackerQueueRoutes extends RouteBuilder {
#Override
public void configure() throws Exception {
from("activemq:topic:in")
.process(new TrackerProcessor() {
#Override
public void process(Exchange exchange) throws
Exception {
log.info("I'm in");
FieldUpdate body =
exchange.getIn().getBody(FieldUpdate.class);
log.info("Hello from camel processed message!
Received payload: {}" , body.getSerialNumber());
exchange.getOut().setHeader(Exchange.HTTP_RESPONSE_CODE,
HttpStatus.ACCEPTED);
}
});
}
}
Processor
public class TrackerProcessor implements Processor {
#Override
public void process(Exchange exchange) throws Exception {
}
}
Can any one provide me some tutorial link that fulfil my need or any ideas.

As Claus Ibsen suggested in the comments, you have to add these dependencies to your POM file
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring-boot-starter</artifactId>
<version>[camel-version]</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
camel-spring-boot-starter automatically starts a CamelContext for you, discovers routes etc
spring-boot-starter-web keeps your application running by listening for web requests. Otherwise it would immediately shut down after startup because there is nothing to execute.
Since your Camel route class is correctly annotated (#Component) and subclassed (extends RouteBuilder), it should be auto-discovered by the Camel SpringBoot starter.
See the Camel-SpringBoot docs for all these topics and more.

Related

SimpMessagingTemplate no qualifying bean of type

I'm trying to use the SimpMessagingTemplate class in my project to send some data via websocket.
The problem is that I always have an error, here is the complete error:
Error creating bean with name 'webSocketUserNotificationManagerImpl' defined in URL [...]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'org.springframework.messaging.simp.SimpMessagingTemplate' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {}
And here is the webSocketuserNotificationManagerImpl service:
#Service
public class WebSocketUserNotificationManagerImpl implements UserNotificationManager {
/**
* Template to use to send back a message to the user via Web-socket.
*/
private SimpMessagingTemplate template;
public WebSocketUserNotificationManagerImpl(SimpMessagingTemplate template) {
this.template = template;
}
private ObjectMapper jsonMapper = new ObjectMapper();
#Override
public void sendMessageToUser(String jobName, String eventType, Result result) {
....
}
public void sendDeleteMessageToUser(DeleteRequestsSocketMessage message, Principal principal) {
this.template.convertAndSendToUser(principal.getName().toLowerCase(),
"/status/delete", message);
}
}
I have this dependency in my pom:
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-messaging</artifactId>
</dependency>
What is wrong, why the SimpMessagingTemplate bean doesn't exist ?
Okay so the bean SimpMessagingTemplate is not found in your application context .So make sure that the bean is getting created by spring automatically in the application context at startup and the annotations are provided as necessary for the creation like :
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer {
}
Try using constructor injection like :
#Autowired
public WebSocketUserNotificationManagerImpl(SimpMessagingTemplate template) {
this.template = template;
}
public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer
has been deprecated.
You can add dependency –
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-websocket</artifactId>
</dependency>
Then implement WebSocketMessageBrokerConfigurer

Spring bean #Scope "websocket" without #EnableWebSocketMessageBroker annotation

I have an spring boot app that is exchanging messages over binary websocket. I.e. NO STOMP, AMQP etc. or any other messaging protocol!!! Now I need to mark one of my classes with the Scope of "websocket". Like that befow:
#Service("session")
#Scope(scopeName = "websocket", proxyMode = ScopedProxyMode.TARGET_CLASS)
public class Session {
...
}
I have read the documentation here that quote:
" WebSocket-scoped beans can be injected into controllers and any
channel interceptors registered on the "clientInboundChannel". "
I would like to emphasis the word "and" in that sentence.
Well i do have a controller, but i do not have any channelInterceptor. I am injecting this as:
#Controller("entryPoint")
public class EntryPoint {
#Autowired
private ApplicationContext applicationContext;
private ApplicationEventPublisher applicationEventPublisher;
#Autowired
private Session session;
...
#PostConstruct
public void init() {
// Invoked after dependencies injected
logger.info("EntryPoint init method i.e. #PostConstruct invoked");
}
...
}
Now the first thig i found to be interesting is that i need the annotation #EnableWebSocketMessageBroker i.e. it seems that #EnableWebSocket is not enough , but then comes the question why. I should be able to define that scope independinglly whether i am using messaging protocol or not. At least that is what i belive.
anyway without it i am getting the error
java.lang.IllegalStateException: No Scope registered for scope name 'websocket'
I said ok, lets create a dummy config file for the message broker which is bringing additional dependencies such as :
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-core</artifactId>
</dependency>
<dependency>
<groupId>io.projectreactor</groupId>
<artifactId>reactor-net</artifactId>
</dependency>
<dependency>
<groupId>io.netty</groupId>
<artifactId>netty-all</artifactId>
<version>4.1.13.Final</version>
</dependency>
as a config like
#Configuration
#EnableWebSocketMessageBroker
public class WebSocketMessageBrokerConfig implements WebSocketMessageBrokerConfigurer {
#Override
public void registerStompEndpoints(StompEndpointRegistry registry) {
registry.addEndpoint("/testEndPoint");
}
#Override
public void configureClientInboundChannel(ChannelRegistration inboundChannelRegistration) {
}
#Override
public void configureClientOutboundChannel(ChannelRegistration outboundChannelRegistration) {
}
#Override
public boolean configureMessageConverters(List<MessageConverter> messageConverters) {
return true;
}
#Override
public void configureWebSocketTransport(WebSocketTransportRegistration webSocketTransportRegistration) {
webSocketTransportRegistration.setMessageSizeLimit(45678910);
webSocketTransportRegistration.setSendBufferSizeLimit(9101112);
webSocketTransportRegistration.setSendTimeLimit(123456789);
}
#Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> arg0) {
System.out.println("WEB SOCKET ARGUMENT RESOLVER");
}
#Override
public void addReturnValueHandlers(
List<HandlerMethodReturnValueHandler> arg0) {
System.out.println("WEB SOCKET RETURN VALUE HANDLER");
}
#Override
public void configureMessageBroker(MessageBrokerRegistry config) {
StompBrokerRelayRegistration stompBrokerRelayRegistration = config.enableStompBrokerRelay(
"/topic/",
"/queue/errors",
"/exchange/amp.direct/testaError/",
"/exchange/amp.direct/testCreateAccount/"
);
stompBrokerRelayRegistration.setRelayHost("127.0.0.6");
stompBrokerRelayRegistration.setRelayPort(61613);
stompBrokerRelayRegistration.setSystemLogin("guest");
stompBrokerRelayRegistration.setSystemPasscode("guest");
stompBrokerRelayRegistration.setAutoStartup(true);
stompBrokerRelayRegistration.setSystemHeartbeatSendInterval(5000);
stompBrokerRelayRegistration.setSystemHeartbeatReceiveInterval(4000);
config.setApplicationDestinationPrefixes("/app");
}
}
which brings the error message:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'scopedTarget.serverSyncSession': Scope 'websocket' is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound SimpAttributes found. Your code is probably not processing a client message and executing in message-handling methods invoked by the SimpAnnotationMethodMessageHandler?
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:355) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
...
Caused by: java.lang.IllegalStateException: No thread-bound SimpAttributes found. Your code is probably not processing a client message and executing in message-handling methods invoked by the SimpAnnotationMethodMessageHandler?
at org.springframework.messaging.simp.SimpAttributesContextHolder.currentAttributes(SimpAttributesContextHolder.java:82) ~[spring-messaging-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.messaging.simp.SimpSessionScope.get(SimpSessionScope.java:36) ~[spring-messaging-4.3.7.RELEASE.jar:4.3.7.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:340) ~[spring-beans-4.3.7.RELEASE.jar:4.3.7.RELEASE]
... 45 common frames omitted
While the message is clear i.e. my code is indeed not thread bound and is executed in a messageHandelr just not in the SimpAnnotationMethodMessageHandler, but in a BinaryMessageHandler class that extends BinaryWebSocketHandler. Why should i not be able to place a scope on a bean it is clearlly used by the websockets. Why do we need all annotation #EnableWebSocketMessageBroker at all and all following dependencies?
It is not really clear to me what else do i need to do in order to get my bean with the right scope. Somehow i have the feeling that this will get me again to some messaging dependency that i am really trying to avoid.
My question is does anyone have some hint for me what do I need to do in BinaryMessageHandler in order to tell spring to thread that session bean with the scope "wesocket". Is there a way to achive that without the annotation #EnableWebSocketMessageBroker?
any feedback is appreciated.

POJO Injection in Spring similar to CDI

I have some java objects coming from external library which I need to inject in my spring project. Problem is the classes from library is not aware of any spring api's
If I inject the beans from library to Service using #Autowired I am getting org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type
Following is my service class
#Path("/test")
public class TestService {
#Autowired
SomeOtherClass service;
#GET
public Response get(){
return Response.ok(service.someMethod()).build();
}
}
and following is my class from library which is not aware of spring
public class SomeOtherClass {
public String someMethod(){
return "Data from library";
}
}
When I invoke my service I get exception as
org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.example.SomeOtherClass' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true)}
Is there are way in spring to inject a plain Java Object similar to that of injection in **CDI**?
There is one option to define applicationcontext.xml and define SomeOtherClass in xml and use getBean, but I don't want to do that. Is there any other option?
Note:
Following options cannot be considered because I have100's of classes coming from library
Cannot use applicationcontext.xml
Cannot #Configuration #Bean to produce beans.
You could use the #Configuration and #Bean annotations as follows -
Create a new class:
#Configuration
public class AppConfig {
#Bean
SomeOtherClass someOtherClassBean(){ return new SomeOtherClass();}
}
Now the auto wiring shall work.
What it does, is actually creating a bean and letting Spring know about it.
Maybe try adding the beans programatically to the IoC container:
Add Bean Programmatically to Spring Web App Context
You need to find all the classes you want to instantiate and use one of the methods in the linked question.
You can use reflection to add Bean definitions programatically.
#Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
Reflections ref = new Reflections(new ConfigurationBuilder()
.setScanners(new SubTypesScanner(false /* don't exclude Object.class */), new ResourcesScanner())
.setUrls(ClasspathHelper.forPackage(PACKAGE_NAME))
.filterInputsBy(new FilterBuilder().include(FilterBuilder.prefix(PACKAGE_NAME))));
ref.getSubTypesOf(Object.class).stream()
.forEach(clazz -> {
logger.info("Defining pojo bean: {} -> {}", Introspector.decapitalize(clazz.getSimpleName()), clazz.getCanonicalName());
registry.registerBeanDefinition(Introspector.decapitalize(clazz.getSimpleName()),
BeanDefinitionBuilder.genericBeanDefinition(clazz).getBeanDefinition());
});
}
Subsequently, these beans can be #Autowired elsewhere. See Gist: https://gist.github.com/ftahmed/a7dcdbadb8bb7dba31ade463746afd04

Spring Test + Mockito.mock - Spring fails because it tries to load the mocked bean #Autowired dependencies

I can't find out why the following simple scenario is failing: I have a Spring application with a filter that loads a Spring bean from the application context:
public class MyFilter implements Filter{
private IPermissionService permissionService;
public void init(FilterConfig filterConfig) throws ServletException {
WebApplicationContext ac = null;
try{
ac = WebApplicationContextUtils.getRequiredWebApplicationContext(filterConfig.getServletContext());
permissionService = ac.getBean(PermissionServiceImpl.class);
PermissionServiceImpl has an #Autowired attribute dataSource so in my TestNG test, I mock it in the Spring applicationContext:
#Configuration
public class MyFilterSpringTestConfig{
#Bean
public IPermissionService permissionService(){
return Mockito.mock(PermissionServiceImpl.class);
}
MyTest:
#Test
#WebAppConfiguration
#ContextConfiguration(classes=MyFilterSpringTestConfig.class)
public class MyFilterSpringTest extends BaseSpringFilterTest{
...
The problem is that on Spring initialization I get an exception complaining that PermissionServiceImpl's dataSource dependency is not satisfied. Since I wrapped it with a mock, why is it still failing? How could I fix it?
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [javax.sql.DataSource] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {#org.springframework.beans.factory.annotation.Autowired(required=true), #org.springframework.beans.factory.annotation.Qualifier(value=myDataSource)}
When mocking a class using Mockito (or any other mocking framework) that class is still an instance of the original class. With that comes that it also contains all the annotations and class information with it.
So when you create a mock of the class it still detects all annotations on it and tries to full fill that. I.e. #Autowire other instances.
Either don't use auto wiring or don't mock the class but the interface (which doesn't contain that information).

Apache-camel with spring-boot

I'm trying to set up a project with the stack on title, the JMS that we're using is ActiveMQ. So, here is the configuration that I'm doing:
#SpringBootApplication
public class Application {
private static Logger logger = Logger.getLogger(Application.class);
#Value("${broker.component.name}")
private String brokerComponetName;
#Value("${broker.dead.letter.queue}")
private String brokerDeadLetterQueue;
#Value("${broker.in.queue}")
private String brokerInQueue;
#Value("${broker.out.queue}")
private String brokerOutQueue;
#Value("${broker.url}")
private String brokerUrl;
#Value("${broker.user}")
private String brokerUser;
#Value("${broker.password}")
private String brokerPassword;
public static void main(String[] args) throws Exception {
logger.info("starting loader");
SpringApplication.run(Application.class, args);
}
#Bean
public SpringCamelContext camelContext(ApplicationContext applicationContext) throws Exception {
SpringCamelContext camelContext = new SpringCamelContext(applicationContext);
camelContext.addComponent(brokerComponetName, JmsComponent.jmsComponent(connectionFactory()));
camelContext.addRoutes(new RouteBuilder() {
public void configure() throws ConfigurationException {
errorHandler(deadLetterChannel(brokerDeadLetterQueue)
.onRedelivery(new FailureProcessor())
.useOriginalMessage()
.maximumRedeliveries(5)
.redeliveryDelay(5000)
.retryAttemptedLogLevel(LoggingLevel.INFO));
from(brokerInQueue)
.process(new MessageProcessor())
.to(brokerOutQueue);
}
});
return camelContext;
}
#Bean
public ConnectionFactory connectionFactory() throws ConfigurationException {
System.out.println("BROKER URL: " + brokerUrl);
return new ActiveMQConnectionFactory(brokerUser,
brokerPassword, brokerUrl);
}
I already tried to add #EnableJms to Application with no success. The stack error is the follow:
org.springframework.beans.factory.BeanCreationException: Error
creating bean with name 'jmsListenerContainerFactory' defined in class
path resource
[org/springframework/boot/autoconfigure/jms/JmsAnnotationDrivenConfiguration.class]:
Bean instantiation via factory method failed; nested exception is
org.springframework.beans.BeanInstantiationException: Failed to
instantiate
[org.springframework.jms.config.DefaultJmsListenerContainerFactory]:
Factory method 'jmsListenerContainerFactory' threw exception; nested
exception is java.lang.NoSuchMethodError:
org.springframework.jms.config.DefaultJmsListenerContainerFactory.setAutoStartup(Z)V
Thank's in advanced and sorry about any mistake in question.
Apparently it's a bug of spring boot 1.3.3:
DefaultJmsListenerContainerFactory does not contains the required method.
Try to upgrade to spring boot 1.4.0 (in spite it is not in RELEASE version at the moment).
That should solve the bug.
I encountered the same Exception and read the answers here. Because I'm using Spring boot 1.5.1-RELEASE, I found the answers not to be applicable and continued my search.
What I found to be the cause on my end was not properly reading the manual:
http://camel.apache.org/activemq.html. The introduction states:
To use this component make sure you have the activemq.jar or activemq-core.jar on your classpath along with any Camel dependencies such as camel-core.jar, camel-spring.jar and camel-jms.jar.
So what solved the problem was 2 extra Maven entries.
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-spring</artifactId>
<version>${apache.camel.version}</version>
</dependency>
<dependency>
<groupId>org.apache.camel</groupId>
<artifactId>camel-jms</artifactId>
<version>${apache.camel.version}</version>
</dependency>
I hope it helps someone.
If somebody meet with this issue I found solution.
We need to downgrade version of spring-boot to 1.2.3.RELEASE, because camel-jms:2.16.2 uses spring components in version 4.1.9.

Resources