Spring JMS is working against me, as the title say I get a NoClassDefFoundError, below you find the code.
import javax.jms.ConnectionFactory;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.Queue;
import javax.jms.Session;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.core.JmsTemplate102;
import org.springframework.jms.core.MessageCreator;
public class JMSQueueSender {
private JmsTemplate jmsTemplate;
private Queue queue;
public void setConnectionFactory(ConnectionFactory cf) {
this.jmsTemplate = new JmsTemplate102(cf, false);
}
public void setQueue(Queue queue) {
this.queue = queue;
}
public void simpleSend() throws Exception {
this.jmsTemplate.send(this.queue, new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
return session.createTextMessage("hello queue world");
}
});
}
}
This is also the example code, I have a working code with IBM, but I'm trying to change it for a Spring JMS, but it isn't working. Can someone help me.
I guess you need to add spring-tx.jar (or org.springframework.transaction-*.jar with new style of names) to the classpath.
Try checking your JAVA_HOME and CLASSPATH settings.
Related
I am using Spring Boot version 2.4.2 and doing Spring Cloud Stream and Spring Integration using #InboundChannelAdapter .
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.messaging.Source;
import org.springframework.integration.annotation.InboundChannelAdapter;
#EnableBinding(value = Source.class)
public class TransactionPublisher {
#InboundChannelAdapter(channel = Source.OUTPUT)
public String sendTransactionDetails() {
return "{name:\"T1\", amount: \"1000\", transactionFor : \"Purchase\"}";
}
}
RabbitmqReceiverApplication.java
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.stream.annotation.EnableBinding;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.cloud.stream.messaging.Sink;
#EnableBinding(value = Sink.class)
#SpringBootApplication
public class RabbitmqReceiverApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitmqReceiverApplication.class, args);
}
#StreamListener(Sink.INPUT)
public void log(String message) {
System.out.println(message);
}
}
What could be the alternative ? how to refactor the above code then? A lot of things are happening with the pace getting difficult to understand whats coming and whats going off ?
See the documentation.
e.g. On the consumer side...
#Bean
Consumer<String> log() {
return str -> {
System.out.println(str);
};
}
The binding name is log-in-0.
On the producer side, it's a Supplier<String> bean with binding name sendTransactionDetails-out-0.
I'm having a few issues with Spring Integration and the control bus. I need to turn auto-start off on an InboundChannelAdapter. However when I do this I can't get the ControlBus to start the channel adapter.
I've searched for an answer online, but most of the examples use XML configuration.
Here is the entirety of my code:
package com.example.springintegrationdemo;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.integration.annotation.InboundChannelAdapter;
import org.springframework.integration.annotation.Poller;
import org.springframework.integration.annotation.ServiceActivator;
import org.springframework.integration.channel.DirectChannel;
import org.springframework.integration.config.EnableIntegration;
import org.springframework.integration.config.ExpressionControlBusFactoryBean;
import org.springframework.integration.core.MessageSource;
import org.springframework.integration.file.FileReadingMessageSource;
import org.springframework.messaging.MessageChannel;
import org.springframework.messaging.MessageHandler;
import org.springframework.messaging.support.GenericMessage;
import java.io.File;
#SpringBootApplication
#EnableIntegration
public class SpringIntegrationDemoApplication {
#Bean
public MessageChannel fileChannel() {
return new DirectChannel();
}
#Bean(name = "fileMessageSource")
#InboundChannelAdapter(channel = "fileChannel", poller = #Poller(fixedDelay = "1000"),autoStartup = "false")
public MessageSource<File> fileMessageSource() {
FileReadingMessageSource fileReadingMessageSource = new FileReadingMessageSource();
fileReadingMessageSource.setDirectory(new File("lz"));
return fileReadingMessageSource;
}
#Bean
#ServiceActivator(inputChannel = "fileChannel")
public MessageHandler messageHandler() {
MessageHandler messageHandler = message -> {
File f = (File) message.getPayload();
System.out.println(f.getAbsolutePath());
};
return messageHandler;
}
#Bean
MessageChannel controlChannel() {
return new DirectChannel();
}
#Bean
#ServiceActivator(inputChannel = "controlChannel")
ExpressionControlBusFactoryBean controlBus() {
ExpressionControlBusFactoryBean expressionControlBusFactoryBean = new ExpressionControlBusFactoryBean();
return expressionControlBusFactoryBean;
}
#Bean
CommandLineRunner commandLineRunner(#Qualifier("controlChannel") MessageChannel controlChannel) {
return (String[] args)-> {
System.out.println("Starting incoming file adapter: ");
boolean sent = controlChannel.send(new GenericMessage<>("#fileMessageSource.start()"));
System.out.println("Sent control message successfully? " + sent);
while(System.in.available() == 0) {
Thread.sleep(50);
}
};
}
public static void main(String[] args) {
SpringApplication.run(SpringIntegrationDemoApplication.class, args);
}
}
The message is sent to the control bus component successfully, but the inbound channel adapter never starts.
I would appreciate any help.
Thanks,
Dave
See here: https://docs.spring.io/spring-integration/docs/current/reference/html/configuration.html#annotations_on_beans
The fileMessageSource bean name is exactly for the FileReadingMessageSource. A SourcePollingChannelAdapter created from the InboundChannelAdapter has this bean name: springIntegrationDemoApplication.fileMessageSource.inboundChannelAdapter.
The #EndpointId can help you to simplify it.
In other words: everything is OK with your config, only the problem that you don't use the proper endpoint id to start the SourcePollingChannelAdapter.
I have a spring boot application that uses spring-JMS. Is there any way to tell the test method to wait the jms lister util it finishes executing without using latches in the actual code that will be tested?
Here is the JMS listener code:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.stereotype.Component;
import javax.jms.Message;
import javax.jms.QueueSession;
#Component
public class MyListener {
#Autowired
MyProcessor myProcessor;
#JmsListener(destination = "myQueue", concurrency = "1-4")
private void onMessage(Message message, QueueSession session) {
myProcessor.processMessage(message, session);
}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.jms.Message;
import javax.jms.QueueSession;
#Component
public class MyProcessor {
public void processMessage(Message msg, QueueSession session) {
//Here I have some code.
}
}
import org.apache.activemq.command.ActiveMQTextMessage;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import javax.jms.JMSException;
import javax.jms.Message;
import javax.jms.QueueSession;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertNull;
#SpringBootTest
#ExtendWith(SpringExtension.class)
#ActiveProfiles("test")
public class IntegrationTest {
#Autowired
private JmsTemplate JmsTemplate;
#Test
public void myTest() throws JMSException {
Message message = new ActiveMQTextMessage();
jmsTemplate.send("myQueue", session -> message);
/*
Here I have some testing code. How can I tell the application
to not execute this testing code until all JMS lister threads
finish executing.
*/
}
}
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.broker.BrokerService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.util.SocketUtils;
import javax.jms.ConnectionFactory;
#EnableJms
#Configuration
#Profile("test")
public class JmsTestConfig {
public static final String BROKER_URL =
"tcp://localhost:" + SocketUtils.findAvailableTcpPort();
#Bean
public BrokerService brokerService() throws Exception {
BrokerService brokerService = new BrokerService();
brokerService.setPersistent(false);
brokerService.addConnector(BROKER_URL);
return brokerService;
}
#Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory(BROKER_URL);
}
#Bean
public JmsTemplate jmsTemplate(ConnectionFactory connectionFactory) {
JmsTemplate jmsTemplate = new JmsTemplate(connectionFactory);
return jmsTemplate;
}
}
Note: Is it applicable to solve this without adding testing purpose code to the implementation code (MyListener and MyProcessor).
Proxy the listener and add an advice to count down a latch; here's one I did for a KafkaListener recently...
#Test
public void test() throws Exception {
this.template.send("so50214261", "foo");
assertThat(TestConfig.latch.await(10, TimeUnit.SECONDS)).isTrue();
assertThat(TestConfig.received.get()).isEqualTo("foo");
}
#Configuration
public static class TestConfig {
private static final AtomicReference<String> received = new AtomicReference<>();
private static final CountDownLatch latch = new CountDownLatch(1);
#Bean
public static MethodInterceptor interceptor() {
return invocation -> {
received.set((String) invocation.getArguments()[0]);
return invocation.proceed();
};
}
#Bean
public static BeanPostProcessor listenerAdvisor() {
return new ListenerWrapper(interceptor());
}
}
public static class ListenerWrapper implements BeanPostProcessor, Ordered {
private final MethodInterceptor interceptor;
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
public ListenerWrapper(MethodInterceptor interceptor) {
this.interceptor = interceptor;
}
#Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof Listener) {
ProxyFactory pf = new ProxyFactory(bean);
NameMatchMethodPointcutAdvisor advisor = new NameMatchMethodPointcutAdvisor(this.interceptor);
advisor.addMethodName("listen");
pf.addAdvisor(advisor);
return pf.getProxy();
}
return bean;
}
}
(but you should move the countDown to after the invocation proceed()).
A method annotated with #JmsListener deletes the message after it finishes, so a good option is to read the queue for existing messages and assume the queue is empty after your method is done. Here is the piece of code for counting the messages from the queue.
private int countMessages() {
return jmsTemplate.browse(queueName, new BrowserCallback<Integer>() {
#Override
public Integer doInJms(Session session, QueueBrowser browser) throws JMSException {
return Collections.list(browser.getEnumeration()).size();
}
});
}
Following is the code for testing the countMessages() method.
jmsTemplate.convertAndSend(queueName, "***MESSAGE CONTENT***");
while (countMessages() > 0) {
log.info("number of pending messages: " + countMessages());
Thread.sleep(1_000l);
}
// continue with your logic here
I've based my solution on the answer given by Gary Russell, but rather put the CountDownLatch in an Aspect, using Spring AOP (or the spring-boot-starter-aop variant).
public class TestJMSConfiguration {
private static final Logger LOGGER = LoggerFactory.getLogger(TestJMSConfiguration.class);
public static final CountDownLatch countDownLatch = new CountDownLatch(1);
#Component
#Aspect
public static class LatchCounterAspect {
#Pointcut("execution(public void be.infrabel.rocstdm.application.ROCSTDMMessageListener.onMessage(javax.jms.TextMessage))")
public void onMessageMethod() {};
#After(value = "onMessageMethod()")
public void countDownLatch() {
countDownLatch.countDown();
LOGGER.info("CountDownLatch called. Count now at: {}", countDownLatch.getCount());
}
}
A snippet of the test:
JmsTemplate jmsTemplate = new JmsTemplate(this.embeddedBrokerConnectionFactory);
jmsTemplate.convertAndSend("AQ.SOMEQUEUE.R", message);
TestJMSConfiguration.countDownLatch.await();
verify(this.listenerSpy).putResponseOnTargetQueueAlias(messageCaptor.capture());
RouteMessage outputMessage = messageCaptor.getValue();
The listenerSpy is a #SpyBean annotated field of the type of my MessageListener. The messageCaptor is a field of type ArgumentCaptor<MyMessageType> annotated with #Captor. Both of these are coming from mockito so you need to run/extend your test with both MockitoExtension (or -Runner) along with the SpringExtension (or -Runner).
My code puts an object on an outbound queue after processing the incoming message, hence the putResponseOnTargetQueueAlias method. The captor is to intercept that object and do my assertions accordingly. The same strategy could be applied to capture some other object in your logic.
I have 2 applications, the first app starts a ActiveMQ broker ( https://spring.io/guides/gs/messaging-jms/ ).
At the second app I want to subcribe a topic from the first app.
How can I do this without starting a ActiveMQ Server?
Possible solution:
Server Application Project
import java.time.LocalDateTime;
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.apache.activemq.broker.BrokerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.core.JmsTemplate;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.ui.ModelMap;
#SpringBootApplication
#EnableJms
#EnableScheduling
public class JsmServerApplication {
#Autowired
JmsTemplate jmsTemplate;
#Bean
public BrokerService broker() throws Exception {
BrokerService ret = new BrokerService();
ret.addConnector("tcp://0.0.0.0:4444"); // allow remote connections
ret.setBrokerName("primary-broker");
ret.setUseJmx(true);
return ret;
}
#Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory("tcp://localhost:4444");
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jacksonJmsMessageConverter());
return factory;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
public static void main(String[] args) {
SpringApplication.run(JsmServerApplication.class, args);
}
#Scheduled(cron = "*/5 * * * * ?")
public void run() {
ModelMap msg = new ModelMap("now", LocalDateTime.now().toString());
System.out.println("Sending: " + msg);
jmsTemplate.convertAndSend("messages", msg);
}
}
Client Application Project
import javax.jms.ConnectionFactory;
import org.apache.activemq.ActiveMQConnectionFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.jms.annotation.EnableJms;
import org.springframework.jms.annotation.JmsListener;
import org.springframework.jms.config.DefaultJmsListenerContainerFactory;
import org.springframework.jms.support.converter.MappingJackson2MessageConverter;
import org.springframework.jms.support.converter.MessageConverter;
import org.springframework.jms.support.converter.MessageType;
import org.springframework.ui.ModelMap;
#SpringBootApplication
#EnableJms
public class JsmClientApplication {
#Bean
public ConnectionFactory connectionFactory() {
return new ActiveMQConnectionFactory("tcp://localhost:4444");
}
#Bean
public DefaultJmsListenerContainerFactory jmsListenerContainerFactory(ConnectionFactory connectionFactory) {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(connectionFactory);
factory.setMessageConverter(jacksonJmsMessageConverter());
return factory;
}
#Bean
public MessageConverter jacksonJmsMessageConverter() {
MappingJackson2MessageConverter converter = new MappingJackson2MessageConverter();
converter.setTargetType(MessageType.TEXT);
converter.setTypeIdPropertyName("_type");
return converter;
}
#JmsListener(destination = "messages", containerFactory = "jmsListenerContainerFactory")
public void msg(ModelMap msg) {
System.out.println(msg);
}
public static void main(String[] args) {
SpringApplication.run(JsmClientApplication.class, args);
}
}
Is it a correct approch?
Solved with this:
http://javasampleapproach.com/java-integration/activemq-work-spring-jms-activemq-topic-publisher-subcribers-pattern-using-springboot
You can use the MessageConsumer to consume the data like the code below
public static void main(String[] args) throws JMSException {
// Getting JMS connection from the server
ConnectionFactory connectionFactory = new ActiveMQConnectionFactory(url);
Connection connection = connectionFactory.createConnection();
connection.start();
Session session = connection.createSession(false,
Session.AUTO_ACKNOWLEDGE);
Topic topic = session.createTopic("topic");
MessageConsumer consumer = session.createConsumer(topic);
MessageListener listner = new MessageListener() {
public void onMessage(Message message) {
try {
//do operations
} catch (JMSException e) {
}
}
};
consumer.setMessageListener(listner);
connection.close();
}
Since you are using the ActiveMQConnectionFactory, you can set the broker as below
BrokerService broker = new BrokerService();
broker.addConnector("tcp://localhost:4444);
broker.setPersistent(false);
ActiveMQConnectionFactory cf = new ActiveMQConnectionFactory("vm://localhost?broker.persistent=false");
If you do not have any restrictions in not using ActiveMQ, You can use Kafka for doing the same. Kafka provides you a highly scalable and distributed Message Bus with simple API.
https://kafka.apache.org/quickstart
I am not sure about the constraints but I just wanted to give you a feel of Kafka. However, the above code should help you in understanding the concept of subscribing and consuming messages from a topic.
See this answer for how to listen on a tcp port instead of the vm:// transport.
Out of curiosity I'd like to build a simple websockets tyrus server chat project with javac and libraries in -classpath.
Yes, I know that this is not the standard way (mvn is), but I'm doing it as a proof of concept. I manage to get project built but it throws a NullPointerException
Code is:
ChatMain.java:
package chat;
import java.io.IOException;
import java.util.Collections;
import java.util.Map;
import javax.websocket.DeploymentException;
import org.glassfish.tyrus.server.Server;
public class ChatMain {
public static void main(String[] args) throws DeploymentException,
IOException {
Map<String, Object> properties = Collections.emptyMap();
Server server = new Server("localhost", 8080, "/ws", properties,
ChatEndPoint.class);
System.out.println(server);
try {
server.start();
System.in.read();
} finally {
server.stop();
}
}
}
ChatEndPoint.java:
package chat;
import java.io.IOException;
import java.util.Map;
import java.util.HashMap;
import java.util.Collections;
import javax.websocket.OnClose;
import javax.websocket.OnMessage;
import javax.websocket.OnOpen;
import javax.websocket.Session;
import javax.websocket.server.ServerEndpoint;
import org.json.*;
#ServerEndpoint("/chat")
public class ChatEndPoint {
private static Map<String, Session> peers = Collections.synchronizedMap(new HashMap<String, Session>());
#OnOpen
public void onOpen(Session peer) {
System.out.println("onOpen");
}
#OnMessage
public void onMessage(String message, Session peer) throws IOException {
System.out.println("onMessage");
JSONObject json = new JSONObject(message);
String nick = (String) json.get("nick");
boolean newClient = json.has("action") && ((String) json.get("action")).equals("add");
synchronized(peers) {
// Iterate over the connected sessions
// and broadcast the received message
for (Map.Entry<String, Session> entry : peers.entrySet()) {
if (newClient) {
JSONObject json1 = new JSONObject(json, new String[] { "action" }).put("nick", entry.getKey());
peer.getBasicRemote().sendText(json1.toString());
}
entry.getValue().getBasicRemote().sendText(message);
}
}
if (newClient)
peers.put(nick, peer);
}
#OnClose
public void onClose(Session peer) {
System.out.println("onClose");
peers.values().remove(peer);
}
}
javac line:
javac -cp chat/javax.websocket-api-1.1.jar:chat/tyrus-server-1.12.jar:chat/tyrus-spi-1.12.jar:chat/tyrus-core-1.12.jar:chat/grizzly-framework-2.3.22.jar:chat/grizzly-http-server-2.3.22.jar:chat/tyrus-container-grizzly-server-1.12.jar:chat/json.jar: chat/ChatMain.java
java line:
java -cp chat/javax.websocket-api-1.1.jar:chat/tyrus-server-1.12.jar:chat/tyrus-spi-1.12.jar:chat/tyrus-core-1.12.jar:chat/grizzly-framework-2.3.22.jar:chat/grizzly-http-server-2.3.22.jar:chat/tyrus-container-grizzly-server-1.12.jar:chat/json.jar: chat.ChatMain
runtime error:
Exception in thread "main" java.lang.NullPointerException
at org.glassfish.tyrus.container.grizzly.server.GrizzlyServerContainer$1.stop(GrizzlyServerContainer.java:228)
at org.glassfish.tyrus.server.Server.stop(Server.java:231)
at chat.ChatMain.main(ChatMain.java:23)
I understand that this can be done
Interestingly, more libraries are needed. If I include all libraries in websocket-ri-archive-1.12.zip bundle, it works well. Definitely, a builder is the way to go