Unable to create producer to send messages on ActiveMQ - java

I am learning JMS and different types of brokers out there. I am currently using ActiveMQ (Artemis) for a dummy project.
I currently have Artemis running on the default settings. I can go to the management console and see the queues and topics.
I am now creating 2 Java Spring-based apps; one for producing and one for consuming.
I have seen few tutorials out there, but I'm getting a NPE, which I'm not sure - why, as I believe I am autowiring the bean correctly.
These are my classes:
Main class:
#SpringBootApplication
public class SpringJmsApplication {
public static void main(String[] args) {
SpringApplication.run(SpringJmsApplication.class, args);
SendMessage send = new SendMessage("This is the test message");
}
}
Sender:
public class Sender {
private static final Logger LOGGER =
LoggerFactory.getLogger(Sender.class);
#Autowired
private JmsTemplate jmsTemplate;
public void send(String message) {
LOGGER.info("sending message='{}'", message);
jmsTemplate.convertAndSend("helloworld.q", message);
}
}
Sender Config:
#Configuration
public class SenderConfig {
#Value("${artemis.broker-url}")
private String brokerUrl;
#Bean
public ActiveMQConnectionFactory senderActiveMQConnectionFactory() {
return new ActiveMQConnectionFactory(brokerUrl);
}
#Bean
public CachingConnectionFactory cachingConnectionFactory() {
return new CachingConnectionFactory(
senderActiveMQConnectionFactory());
}
#Bean
public JmsTemplate jmsTemplate() {
return new JmsTemplate(cachingConnectionFactory());
}
#Bean
public Sender sender() {
return new Sender();
}
}
SendMessage Service:
public class SendMessage {
#Autowired
Sender sender;
public SendMessage(String message){
this.sender.send(message);
}
}
So essentially the error is stemming from the SendMessage class, it's unable to autowire the sender bean but I'm not sure why this error is happening because the Sender bean is being created in the SenderConfig class hence surely Spring should've added it to it Spring container/Bean factory/application context?
This is the stacktrace:
Exception in thread "main" java.lang.NullPointerException
at com.codenotfound.jms.SendMessage.<init>(SendMessage.java:11)
at com.codenotfound.SpringJmsApplication.main(SpringJmsApplication.java:16)

Your problem doesn't stem from SendMessage class, this class seems OK.
Your NPE is caused by the way you obtain an instance of SendMessage class, namely, you're not really obtaining the #Bean, managed by Spring Container; rather, you're creating it manually with a new keyword, as:
SendMessage send = new SendMessage("This is the test message");
This allocates a completely new object in the Heap, which is not going to end up in the Spring Container, hence → is not going to be managed by Spring, hence → its field sender will not be #Autowired.

Here is the culprit in your main class.
SendMessage send = new SendMessage("This is the test message");
You are creating object yourself instead of getting from the context, Spring DI won't be applied to the objects created by ourself.
Solution is, mark the SendMessage as spring managed bean by annotating with #Component and get it from context.

Related

How can I use #RabbitListener in multiple classes?

I have a Spring application using RabbitMQ (spring-boot-starter-amqp).
I wanted to know if it's possible to use the #RabbitListener annotation across different classes.
I currently have two classes: Receiver and DeadLetterQueue
Receiver.java:
#Component
#Slf4j
public class Receiver {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#RabbitListener(queues = "queueName")
public void receiveMessage(String message) throws Exception {
logger.info("Received <{}>" + message.toString());
throw new Exception("Error with the message");
}
DeadLetterQueue.java:
#Component
#Slf4j
public class DeadLetterQueue {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#RabbitListener(queues = "otherQueueName")
public void processFailedMessages(String message) {
logger.info("Received failed message<{}>:" + message.toString());
}
}
RabbitMqConfig.java:
#Data
#Configuration
#ConfigurationProperties(prefix = "rabbitmq")
public class RabbitMqConfig {
private String host;
private int port;
private String username;
private String password;
private String queue;
private String exchange;
private String dlq;
private String dlx;
private String routingKey;
#Bean
Queue incomingQueue() {
return QueueBuilder.durable(queue)
.withArgument("x-dead-letter-exchange", dlx)
.build();
}
#Bean
FanoutExchange deadLetterExchange() {
return new FanoutExchange(dlx);
}
#Bean
Queue deadLetterQueue() {
return QueueBuilder.durable(dlq).build();
}
#Bean
Binding deadLetterBinding() {
return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange());
}
#Bean
DirectExchange incomingQueueExchange() {
return new DirectExchange(exchange);
}
#Bean
Binding incomingQueueBinding() {
return BindingBuilder.bind(incomingQueue()).to(incomingQueueExchange()).with(queue);
}
When I use the RabbitMQ management tool to post a message to the FanoutExchange, the processFailedMessages inside the DeadLetterQueue class doesn't log anything to the console. However if this method is inside of the Receiver class, everything works fine. Which led me to the assumption that #RabbitListener couldn't work across multiple classes unless there is some configuration which I am missing.
Other information:
I'm using Docker to run the RabbitMQ Server
Strangely, if I put the receiveMessage() method inside the DeadLetterQueue class, the expectations fail.
So: Is it possible to define queues in multiple classes with #RabbitListener?
Yes, you can have as many #RabbitListeners as you want, either in the same class or in multiple classes, as long as those classes are used in Spring beans.
Since you have #Component on both, it should work just fine, unless the DeadLetterQueue is in a package that is not scanned by Spring Boot.
Boot only looks at the packages and subpackages where the main #SpringBootApplication is located.
You can enable DEBUG logging for org.springframework to log all the bean creation during application initialization.
I'm using Docker to run the RabbitMQ Server
The location of the broker is irrelevant.

Can Spring 3 supports WebSocket [duplicate]

I'm using Spring 3.2.5 without full new JSR-356 WebSockets support.
I would like to have singleton-bean reference in my #ServerEndpoint WebSocket server, which is instantiated by servlet container itself, not in Spring context.
What is the clean way to do it?
My current solution: I made #Service singleton bean with instance in static field:
#Service
public class WebSocketSupportBean {
private volatile static WebSocketSupportBean instance = null;
public static WebSocketSupportBean getInstance() {
return instance;
}
public WebSocketSupportBean() {
instance = this;
}
and just getting it in #ServerEndpoint by static method, disconnecting user if null returned (if bean not jet created during server startup but user connects):
You can setup websockets with spring framework 3.x
I developed a small proof-of-concept application to demonstrate how, based on Rossen Stoyanchev's SpringConfiguration released with spring-core 4.0.
The application sets up a websocket server endpoint with uri /wstest which will use a #Autowired spring bean to select a greeting word and reply to a websocket message.
The websocket connection is initiated and messages sent by an html page (index.html) running in a browser that supports websockets.
The Endpoint registration is made by a ServletContextListener at context initialization and when the endpoint is instantiated it will be wired with spring:
#WebListener
public class MyApplication implements ServletContextListener {
private final static String SERVER_CONTAINER_ATTRIBUTE = "javax.websocket.server.ServerContainer";
#Override
public void contextInitialized(ServletContextEvent sce) {
ServletContext container = sce.getServletContext();
final ServerContainer serverContainer = (ServerContainer) container.getAttribute(SERVER_CONTAINER_ATTRIBUTE);
try {
serverContainer.addEndpoint(new MyEndpointConfig(MyEndpoint.class, "/wstest"));
} catch (DeploymentException e) {
e.printStackTrace();
}
}
}
And the Endpoint is:
#Component
public class MyEndpoint extends Endpoint {
#Autowired
MyService myService;
#Override
public void onOpen(Session session, EndpointConfig config) {
session.addMessageHandler(new MyMessageHandler(session));
}
class MyMessageHandler implements MessageHandler.Whole<String> {
final Session session;
public MyMessageHandler(Session session) {
this.session = session;
}
#Override
public void onMessage(String message) {
try {
String greeting = myService.getGreeting();
session.getBasicRemote().sendText(greeting + ", got your message (" + message + "). Thanks ! (session: " + session.getId() + ")");
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
Checkout the full source and ready to run example on my Github page.
You have to add bean definition in the configuration of spring.
The solution i found to integrate JSR 356 websocket #ServerEndpoint is to turn off the Servlet container's scan for WebSocket endpoints by spring which can be done by registering #Bean in your Spring Configuration. By this spring not overrides normal JSR 356 websocket by spring STOMP websocket which is the part of the websocket.
#ServerEndpoint(value="/chatMessage")
public class ChatEndpoint{
// Normal websocket body goes here.
}
Adding Beans in your Configuration as:
#Configuration
public class WebsocketConfig{
#Bean
public ChatEndpoint chatEndpoint(){
return new ChatEndpoint();
}
// main one is ServerEndpointExporter which prevents Servlet container's scan for WebSocket
#Bean
public ServerEndpointExporter endpointExporter(){
return new ServerEndpointExporter();
}
}
This all done for you. But you should remove configurator = SpringConfigurator.class from #ServerEndpoint.
I am using Spring Websocket 4.0.0 and it works fine.
You can also see this Link.
If you alright then follow this Link also for concept.
Note that, Normally you should make websocket configuration separately from the main configuration of your spring.
Try
#ServerEndpoint(value = "/ws", configurator = SpringConfigurator.class)
And add maven dependency
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-websocket</artifactId>
</dependency>
You can make your #ServerEndpoint object extend SpringBeanAutowiringSupport. Then just make it aware of beans that gets constructed within a Spring-based web application this way:
#PostConstruct
public void init() {
SpringBeanAutowiringSupport.processInjectionBasedOnCurrentContext(this);
}
This way #Autowired annotation will worl correctly:
#Autowired MyService myService;
try this,it works for me
#Component
#ServerEndpoint(value = "/instantMessageServer",configurator = SpringConfigurator.class)
public class InstantMessageServer{
private static IChatService chatService;
#Autowired
public InstantMessageServer(IChatService chatService){
this.chatService = chatService;
}
public InstantMessageServer(){}
}
I found this solution on https://spring.io/blog/2013/05/23/spring-framework-4-0-m1-websocket-support
but there is one more glitch,the class annotated with #ServerEndpoint cant acquire httpsession with SpringConfigurator,there is no a override of method modifyhandler in it.Maybe we create a seperate Configurator extends SpringConfigurator and override that method would be a workaroud.
It is better to build a real-time web application with spring-websocket and messaging api,I think.
public class ModifiedServerEndpointConfigurator extends SpringConfigurator{
#Override
public void modifyHandshake(ServerEndpointConfig sec, HandshakeRequest request, HandshakeResponse response) {
HttpSession httpSession = (HttpSession) request.getHttpSession();
sec.getUserProperties().put(HttpSession.class.getName(),httpSession);
super.modifyHandshake(sec, request, response);
}
}

Spring Integration manually publish message to channel

I'm in the process of learning how to use the Java Spring Framework and started experimenting with Spring Integration. I'm trying to use Spring Integration to connect my application to an MQTT broker both to publish and subscribe to messages but I'm having trouble finding a way to manually publish messages to an outbound channel. If possible I want to build it using notations in the java code exclusively rather than xml files defining beans and other related configuration.
In every example I've seen the solution to manually publishing a message seems to be to use a MessagingGateway Interface and then use the SpringApplicationBuilder to get the ConfigurableApplicationContext to get a reference to the gateway interface in the main method. The reference is then used to publish a message. Would it be possible to use AutoWired for the interface instead? In my attempts I just get a NullPointer.
My aim is to build a game where I subscribe to a topic to get game messages and then whenever the user is ready to make the next move, publish a new message to the topic.
Update:
This is one of the examples I've been looking at of how to setup an outbound channel: https://docs.spring.io/spring-integration/reference/html/mqtt.html
Update 2 after answer from Gary Russel:
This is some example code I wrote after looking at examples which gets me a NullPointer when using #AutoWired for the Gateway when running gateway.sendToMqtt in Controller.java. What I want to achieve here is to send an mqtt message manually when a GET request is handled by the controller.
Application.java
#SpringBootApplication
public class Application {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
Controller.java
#RestController
#RequestMapping("/publishMessage")
public class Controller {
#Autowired
static Gateway gateway;
#RequestMapping(method = RequestMethod.GET)
public int request(){
gateway.sendToMqtt("Test Message!");
return 0;
}
}
MqttPublisher.java
#EnableIntegration
#Configuration
public class MqttPublisher {
#Bean
public MqttPahoClientFactory mqttClientFactory(){
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setServerURIs("tcp://localhost:1883");
return factory;
}
#Bean
#ServiceActivator(inputChannel = "mqttOutboundChannel")
public MessageHandler mqttOutbound(){
MqttPahoMessageHandler messageHandler =
new MqttPahoMessageHandler("clientPublisher", mqttClientFactory());
messageHandler.setAsync(true);
messageHandler.setDefaultTopic("topic");
return messageHandler;
}
#Bean
public MessageChannel mqttOutboundChannel(){
return new DirectChannel();
}
#MessagingGateway(defaultRequestChannel = "mqttOutboundChannel")
public interface Gateway {
void sendToMqtt(String data);
}
}
Update:
Not sure if this is the proper logging but it is what I get from adding:
logging.level.org.springframework.web=Debug
logging.level.org.hibernate=Error
to application.properties.
https://hastebin.com/cuvonufeco.hs
Use a Messaging Gateway or simply send a message to the channel.
EDIT
#SpringBootApplication
public class So47846492Application {
public static void main(String[] args) {
SpringApplication.run(So47846492Application.class, args).close();
}
#Bean
public ApplicationRunner runner(MyGate gate) {
return args -> {
gate.send("someTopic", "foo");
Thread.sleep(5_000);
};
}
#Bean
#ServiceActivator(inputChannel = "toMqtt")
public MqttPahoMessageHandler mqtt() {
MqttPahoMessageHandler handler = new MqttPahoMessageHandler("tcp://localhost:1883", "foo",
clientFactory());
handler.setDefaultTopic("myTopic");
handler.setQosExpressionString("1");
return handler;
}
#Bean
public MqttPahoClientFactory clientFactory() {
DefaultMqttPahoClientFactory factory = new DefaultMqttPahoClientFactory();
factory.setUserName("guest");
factory.setPassword("guest");
return factory;
}
#Bean
public MqttPahoMessageDrivenChannelAdapter mqttIn() {
MqttPahoMessageDrivenChannelAdapter adapter =
new MqttPahoMessageDrivenChannelAdapter("tcp://localhost:1883", "bar", "someTopic");
adapter.setOutputChannelName("fromMqtt");
return adapter;
}
#ServiceActivator(inputChannel = "fromMqtt")
public void in(String in) {
System.out.println(in);
}
#MessagingGateway(defaultRequestChannel = "toMqtt")
public interface MyGate {
void send(#Header(MqttHeaders.TOPIC) String topic, String out);
}
}

Loading Beans based on hostname

I am writing services in Spring boot that get their configurations from Spring cloud. These services are multi-tenant and the tenant is based on the host name.
what I have now is
public class MyController {
#Autowired
public MyController(MyServiceFactory factory) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#RequestHeader header, #PathVariable id) {
return factory.getMyService(header).handle(id);
}
}
where MyServiceFactory looks something like...
public class MyServiceFactory {
private final HashMap<String, MyService> serviceRegistry = new HashMap<>();
public MyService getMyService(String key) {
return serviceRegistry.get(key);
}
MyServiceFactory withService(String key, MyService service) {
this.serviceRegistry.put(key, service);
return this;
}
}
then in a configuration file
#Configuration
public ServiceFactoryConfiguration {
#Bean
public MyServiceFactory getMyServiceFactory() {
return new MyServiceFactory()
.withService("client1", new MyService1())
.withService("client2", new MyService2());
}
}
While what I have now works, I don't like that I need to create a factory for every dependency my controller may have. I'd like to have my code look something like this...
public class MyController {
#Autowired
public MyController(MyService service) {
...
}
#RequestMapping("some/path/{id}")
ResponseEntity<SomeEntity> getSomeEntity(#PathVariable id) {
return service.handle(id);
}
}
with a configuration file like
#Configuration
public class MyServiceConfiguration() {
#Bean
#Qualifier("Client1")
public MyService getMyService1() {
return new MyService1();
}
#Bean
#Qualifier("Client2")
public MyService getMyService2() {
return new MyService2();
}
}
I can get the code that I want to write if I use a profile at application start up. But I want to have lots of different DNS records pointing to the same (pool of) instance(s) and have an instance be able to handle requests for different clients. I want to be able to swap out profiles on a per request basis.
Is this possible to do?
Spring profiles would not help here, you would need one application context per client, and that seems not what you want.
Instead you could use scoped beans.
Create your client dependent beans with scope 'client' :
#Bean
#Scope(value="client",proxyMode = ScopedProxyMode.INTERFACES)
#Primary
MyService myService(){
//does not really matter, which instance you create here
//the scope will create the real instance
//may be you can even return null, did not try that.
return new MyServiceDummy();
}
There will be at least 3 beans of type MyService : the scoped one, and one for each client. The annotation #Primary tells spring to always use the scoped bean for injection.
Create a scope :
public class ClientScope implements Scope {
#Autowired
BeanFactory beanFactory;
Object get(String name, ObjectFactory<?> objectFactory){
//we do not use the objectFactory here, instead the beanFactory
//you somehow have to know which client is the current
//from the config, current request, session, or ThreadLocal..
String client=findCurrentClient(..);
//client now is something like 'Client1'
//check if your cache (HashMap) contains an instance with
//BeanName = name for the client, if true, return that
..
//if not, create a new instance of the bean with the given name
//for the current client. Easiest way using a naming convention
String clientBeanName=client+'.'+name;
Object clientBean=BeanFactory.getBean(clientBeanName);
//put in cache ...
return clientBean;
};
}
And your client specific beans are configured like this :
#Bean('Client1.myService')
public MyService getMyService1() {
return new MyService1();
}
#Bean('Client2.myService')
public MyService getMyService2() {
return new MyService2();
}
Did not test it but used it in my projects. Should work.
tutorial spring custom scope

How to create multiple channels with different receivers? Spring Redis pub/sub

I'm using Redis Publish Subscribe through Spring Data, but I'm having problems to add more than 1 Channel.
Currently I'm following the typical examples where the MessageListenerContainer is configured by adding a MessageListenerAdapter which has associated a Receiver class, as following:
The previous works perfectly and I'm able to push and receive messages.
However one I try to add a second listener adapter for creating a "channel with a differente receiver and I'm getting a NullPointerException.
The error is attached below. Is there a different way to adding a new adapter? In general I would like to add channels dynamically.
It is possible to add multiple channels associated with one specific receiver by providing a PatternTopic list at addMessageListener method.
Thanks for your help
I believe there is an important bug with Spring Redis when adding MessageListenerAdapter.
If the Receiver class does not extend from MessageListener (and so, implements onMessage) the inner method MethodInvoker() from MessageListenerAdapter class specifically ask if the Receiver is instance of MessageListener (see last line of image below).
To solve this problem just extend from MessageListener and then you can add the additional adapters directly.
It's a shame that spring-data-redis team do not enable issues in their github page to publish this bug. https://github.com/spring-projects/spring-data-redis
If anyone is still looking, use below configuration for Spring Boot 1.5.X
RedisConfig class for multiple channels:
#Configuration
public class RedisConfig {
#Bean
RedisMessageListenerContainer container(RedisConnectionFactory connectionFactory,
#Qualifier("notificationListenerAdapter") MessageListenerAdapter notificationListenerAdapter,
#Qualifier("countListenerAdapter") MessageListenerAdapter countListenerAdapter) {
RedisMessageListenerContainer container = new RedisMessageListenerContainer();
container.setConnectionFactory(connectionFactory);
container.addMessageListener(notificationListenerAdapter, new PatternTopic("notification"));
container.addMessageListener(countListenerAdapter, new PatternTopic("count"));
return container;
}
#Bean("notificationListenerAdapter")
MessageListenerAdapter notificationListenerAdapter(RedisReceiver redisReceiver) {
return new MessageListenerAdapter(redisReceiver, "receiveNotificationMessage");
}
#Bean("countListenerAdapter")
MessageListenerAdapter countListenerAdapter(RedisReceiver redisReceiver) {
return new MessageListenerAdapter(redisReceiver, "receiveCountMessage");
}
#Bean
RedisReceiver receiver() {
return new RedisReceiver();
}
#Bean
StringRedisTemplate template(RedisConnectionFactory connectionFactory) {
return new StringRedisTemplate(connectionFactory);
}
}
RedisReceiver to receive messages from channels.
Note: Make sure the method name matches the method names defined above.
public class RedisReceiver {
private static final Logger LOGGER = LoggerFactory.getLogger(RedisReceiver.class);
public void receiveNotificationMessage(String message) {
LOGGER.info("Message Received from notification channel: <" + message + ">");
}
public void receiveCountMessage(String message) {
LOGGER.info("Message Received from count channel: <" + message + ">");
}
}
Test the flow:
public class TestMessages {
private static final Logger LOG = LoggerFactory.getLogger(TestMessages.class);
private final StringRedisTemplate redisTemplate;
public TestMessages(StringRedisTemplate redisTemplate) {
this.redisTemplate = redisTemplate;
}
public void sendNotification(String message) {
redisTemplate.convertAndSend("notification", message);
}
public void sendCount(String message) {
redisTemplate.convertAndSend("count", message);
}
}

Categories

Resources