Message without handler issue for Apache Qpid in Spring Boot - java

I am trying to use Apache Qpid through Spring Boot application using Jms Qpid client. I am able to configure it but when I am trying to receive message from the queue, the logger is printing:
Dispatcher(918480905)Received a message(878303980)[1] from queue 1 )without a handler - rejecting(requeue)...
Here is my code:
JmsConfiguration.java
#Configuration
public class JmsConfiguration {
#Primary
#Bean
public Context createContext()
{
Properties properties=new Properties();
System.setProperty("IMMEDIATE_PREFETCH", "true");
Context context=null;
try {
properties.load(this.getClass().getResourceAsStream("application.properties"));
context = new InitialContext(properties);
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return context;
}
#Primary
#Bean
public ConnectionFactory createConnectionFactory(Context context)
{
ConnectionFactory connectionFactory=null;
try {
connectionFactory = (ConnectionFactory) context.lookup("qpidConnectionFactory");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
return connectionFactory;
}
#Primary
#Bean
public Connection jmsConnection(ConnectionFactory connectionFactory) throws Exception
{
Connection connection = connectionFactory.createConnection();
connection.start();
return connection;
}
#Primary
#Bean
public Queue jmsQueue(Context context) throws Exception
{
Queue queue = (Queue) context.lookup("myqueue");
return queue;
}
}
application.properties
java.naming.factory.initial = org.apache.qpid.jndi.PropertiesFileInitialContextFactory
connectionfactory.qpidConnectionFactory = amqp://guest:guest#clientid/?brokerlist='tcp://localhost:5672?maxprefetch='0''
queue.myqueue = queue1
ScheduledTask.java It just run send and receive messages in intervals.
#Component
public class ScheduledTasks
{
Connection connection;
Queue queue;
#Autowired
public ScheduledTasks(Connection connection, Queue queue) {
this.connection=connection;
this.queue=queue;
}
MessageListener messageListener = new MessageListener() {
#Override
public void onMessage(Message message) {
System.out.println("Received id is------>");
System.out.println(message);
}
};
#Scheduled(fixedDelay = 2000)
public void sendMessage() throws Exception
{
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
Message message=session.createTextMessage();
MessageProducer messageProducer=session.createProducer(queue);
message.setStringProperty("value", "BOOM");
messageProducer.send(message);
session.commit();
messageProducer.close();
//connection.close();
System.out.println("---------------Message Sent");
}
//#JmsListener(destination="queue1")
#Scheduled(initialDelay=5000, fixedDelay = 5000)
public void receiveMessage() throws Exception
{
Session session = connection.createSession(true, Session.SESSION_TRANSACTED);
MessageConsumer messageConsumer = session.createConsumer(queue);
// if(messageConsumer.getMessageListener()==null)
// messageConsumer.setMessageListener(messageListener);
Message message = messageConsumer.receive(3000);
if(message!=null)
System.out.println("----------------->"+message.getStringProperty("value"));
session.commit();
messageConsumer.close();
//connection.close();
System.out.println("--------------->Got Message");
}
}

You create an instance implementing MessageListener but you don't do anything with it.
In Spring you should use DefaultMessageListenerContainer or SimpleMessageListenerContainer from spring-jms and create it as a Spring Bean in the JmsConfiguration class. After setting connection details (ConnectionFactory, Queue, sessionTransacted etc.) you also need to set the JMS MessageListener implementing class.

Related

How to bind RabbitListeners to CloudAMQP?

I am currently encountering issues in implementing RabbitMQ messaging between my two applications (web/worker). My RabbitMQ service is hosted on CloudAMQP (Heroku addon). However, any #RabbitListener I declare seems to attempt connecting to localhost rather than the cloud service.
Upon adding the following component into my worker app:
#Service
public class TaskConsumer {
#RabbitListener(queues = "worker.rpc.requests", containerFactory = "rabbitListenerContainerFactory")
public String fetch(String p) {
return p;
}
}
I encounter the following error:
2021-07-05 14:38:23.006 INFO 18840 --- [ntContainer#0-3] o.s.a.r.c.CachingConnectionFactory : Attempting to connect to: [localhost:5672]
2021-07-05 14:38:32.145 WARN 18840 --- [ntContainer#0-3] o.s.a.r.l.SimpleMessageListenerContainer : Consumer raised exception, processing can restart if the connection factory supports it. Exception summary: org.springframework.amqp.AmqpConnectException: java.net.ConnectException: Connection refused: connect
2021-07-05 14:38:32.145 INFO 18840 --- [ntContainer#0-3] o.s.a.r.l.SimpleMessageListenerContainer : Restarting Consumer#32c8d668: tags=[[]], channel=null, acknowledgeMode=AUTO local queue size=0
How may I bind the RabbitListener such that it will connect to the AMQP environment instead? Here is my configuration:
#Configuration
#EnableRabbit
public class RabbitConfig {
protected final String workerQueueName = "worker.rpc.requests";
protected final String routingKeyName = "rpc";
protected final String directExcName = "worker.exchange";
#Bean
public ConnectionFactory connectionFactory() {
final URI ampqUrl;
try {
ampqUrl = new URI(getEnvOrThrow("CLOUDAMQP_URL"));
} catch (URISyntaxException e) {
throw new RuntimeException(e);
}
final CachingConnectionFactory factory = new CachingConnectionFactory();
factory.setUsername(ampqUrl.getUserInfo().split(":")[0]);
factory.setPassword(ampqUrl.getUserInfo().split(":")[1]);
factory.setHost(ampqUrl.getHost());
factory.setPort(ampqUrl.getPort());
factory.setVirtualHost(ampqUrl.getPath().substring(1));
try {
factory.getRabbitConnectionFactory().setUri(ampqUrl);
} catch (URISyntaxException e) {
e.printStackTrace();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
} catch (KeyManagementException e) {
e.printStackTrace();
}
return factory;
}
#Bean
public SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory() {
SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();
factory.setConnectionFactory(connectionFactory());
SimpleMessageListenerContainer container = factory
.createListenerContainer();
factory.setConcurrentConsumers(50);
factory.setMaxConcurrentConsumers(100);
container.setStartConsumerMinInterval(3000);
container.setQueues(queue());
factory.setMaxConcurrentConsumers(5);
return factory;
}
#Bean
public RabbitTemplate rabbitTemplate() {
RabbitTemplate template = new RabbitTemplate(connectionFactory());
template.setRoutingKey(this.workerQueueName);
template.setDefaultReceiveQueue(this.workerQueueName);
return template;
}
#Bean
public Queue queue() {
return new Queue(this.workerQueueName);
}
#Bean
public DirectExchange direct() {
return new DirectExchange(this.directExcName);
}
#Bean
public Binding binding(DirectExchange direct,
Queue autoDeleteQueue1) {
return BindingBuilder.bind(autoDeleteQueue1)
.to(direct)
.with(this.routingKeyName);
}
/**
* Required for executing adminstration functions against an AMQP Broker
*/
#Bean
public AmqpAdmin amqpAdmin() {
return new RabbitAdmin(connectionFactory());
}
private static String getEnvOrThrow(String name) {
final String env = getenv(name);
if (env == null) {
throw new IllegalStateException("Environment variable [" + name + "] is not set.");
}
return env;
}
}

Spring JMS onException retry

i have a requirement where i need to send a email but if the email server is down or any error occurs while sending a email that need to be retry for a specific number of times
below is my bean properties
#Bean(destroyMethod = "")
public JndiTemplate jndiTemplate() {
Properties environment = new Properties();
environment.put(Context.INITIAL_CONTEXT_FACTORY, env.getProperty("XXXXXX"));
environment.put(Context.PROVIDER_URL, env.getProperty("XXXXXX"));
JndiTemplate jndiTemplate = new JndiTemplate();
jndiTemplate.setEnvironment(environment);
return jndiTemplate;
}
#Bean(destroyMethod = "")
public JndiObjectFactoryBean jmsConnFactory() {
JndiObjectFactoryBean jmsConnFactory = new JndiObjectFactoryBean();
jmsConnFactory.setJndiTemplate(jndiTemplate());
jmsConnFactory.setJndiName(env.getProperty("XXXXX"));
return jmsConnFactory;
}
#Bean(destroyMethod = "")
public JndiObjectFactoryBean jmsDestanation() {
JndiObjectFactoryBean destination = new JndiObjectFactoryBean();
destination.setJndiTemplate(jndiTemplate());
destination.setJndiName(env.getProperty("XXXXXX"));
return destination;
}
#Bean
public JmsTemplate jmsTemplate() {
JmsTemplate jmsTemplate = new JmsTemplate();
jmsTemplate.setDefaultDestination(jmsDestanation());
jmsTemplate.setConnectionFactory(jmsConnFactory());
return jmsTemplate;
}
#Bean
public JmsReceiver jmsReciver() {
return new JmsReceiver();
}
#Bean
public JmsExceptionListener jmsExceptionListener(){
return new JmsExceptionListener();
}
#Bean
public JmsErrorHandleListener jmsErrorHandleListener(){
return new JmsErrorHandleListener();
}
#Bean
public DefaultMessageListenerContainer jmsQueueListner() {
DefaultMessageListenerContainer listner = new DefaultMessageListenerContainer();
listner.setDestination(jmsDestanation());
listner.setConnectionFactory(jmsConnFactory());
listner.setMessageListener(jmsReciver());
listner.setExceptionListener(jmsExceptionListener());
listner.setErrorHandler(jmsErrorHandleListener());
return listner;
}
and below is my Listener class and error class
public class JmsReceiver implements MessageListener {
#Autowired
JavaMailSender jMailsender;
#Override
public void onMessage(Message message) {
TextMessage text = (TextMessage) message;
ObjectMapper objectMapper = new ObjectMapper();
MimeMessage mimeMessage = jMailsender.createMimeMessage();
try {
JmsMessage inMessage = objectMapper.readValue(text.getText(), JmsMessage.class);
//this is failing and go to the JmsErrorHandleListener
jMailsender.send(mimeMessage);
} catch (JMSException | IOException | MessagingException ex) {
logger.error("Exception on reading message ",ex);
}
}
}
public class JmsErrorHandleListener implements ErrorHandler {
#Override
public void handleError(Throwable t) {
/// not sure how to retry from hear becoz the message was allready read
/// some how i need to inform the weblogic this message was not read yet
}
}
when the message arrives to the onMessage it will throw an error then executes the JmsErrorHandleListener but since the message is already read im not sure how to call the send method again and again
try with below config, spring DMLC manage Exception's to retry MessageListener execution, if jMailsender.send(mimeMessage); fails JmsReceiver.onMessage will be retried 5s later indefintely, see DMLC backoff property
#Bean
public org.springframework.jms.listener.adapter.MessageListenerAdapter jmsReciver() {
return new org.springframework.jms.listener.adapter.MessageListenerAdapter(receiver());
}
#Bean
public JmsReceiver receiver() {
return new JmsReceiver();
}
public class JmsReceiver {
#Autowired
JavaMailSender jMailsender;
#Override
public void onMessage(Message message) throws JMSException {
TextMessage text = (TextMessage) message;
ObjectMapper objectMapper = new ObjectMapper();
MimeMessage mimeMessage = jMailsender.createMimeMessage();
try {
JmsMessage inMessage = objectMapper.readValue(text.getText(), JmsMessage.class);
//this is failing and go to the JmsErrorHandleListener
jMailsender.send(mimeMessage);
} catch (Throwable ex) {
logger.error("Exception on reading message ",ex);
throw new JMSException(ex.getMessage());
}
}
}

How to create an ActiveMQ Consumer without spring?

I've created an app that must consumes an activemq topic, but in this project we don't need to use spring xml. I only created one class called ActiveMQConsumer that implements MessageListener and overrides onMessage method, but nothing happens... Is this approach ok? or something missing? I'm currently connecting through tcp.
public class ActiveMQConsumer implements MessageListener {
public ActiveMQConsumer() throws JMSException {
try {
ConnectionFactory factory = new ActiveMQConnectionFactory(CATALOG_BROKER_URL.getValue());
Connection connection = factory.createConnection();
connection.start();
Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
Destination destination = session.createTopic(CATALOG_TOPIC_NAME.getValue());
MessageConsumer consumer = session.createConsumer(destination);
consumer.setMessageListener(this);
} catch (JMSException e) {
System.out.println("Error");
}
}
#Override
public void onMessage(final Message message) {
LOGGER.info("Start consuming message from Catalog");
try {
if (message instanceof TextMessage) {
TextMessage txtMessage = (TextMessage) message;
System.out.println("Message: " + txtMessage.getText());
} else {
System.out.println("Invalid Message !");
}
} catch (JMSException e) {
System.out.println("Exception" + e);
}
}
}
I've solved my problem using an ContextListener to call an runnable class. Just put it into web.xml and done.

unable to browse queues using jms QueueBrowser

In the jboss admin-console page I can view the current number of items in my queue.
However I'm getting empty enumeration from queueBrowser.getEnumeration().
Below is my code to browse the queue:
public class JMSQueueBrowser {
private final Log log = LogFactory.getLog(getClass());
private QueueConnectionFactory connectionFactory;
private Queue queue;
private QueueBrowser qBrowser;
private QueueSession qSession;
private QueueConnection qConn;
public JMSQueueBrowser() {
initialize();
}
private void initialize() {
try {
InitialContext initialContext = new InitialContext();
connectionFactory = (QueueConnectionFactory)initialContext.lookup("java:comp/env/jms/MyQCF");
queue = (Queue)initialContext.lookup("queue/sampleQueue");
qConn = (QueueConnection) connectionFactory.createConnection();
qConn.start();
qSession = qConn.createQueueSession(false, Session.AUTO_ACKNOWLEDGE);
qBrowser = qSession.createBrowser(queue);
initialContext.close();
} catch (NamingException e) {
log.error(e.getMessage());
} catch (JMSException e) {
log.error(e.getMessage());
}
}
public void browseQueue() {
try {
log.info("---------Queue Name: "+queue.getQueueName()+"-----------");
log.info("---------Queue Has Elements: "+qBrowser.getEnumeration().hasMoreElements()+"-----------");
} catch (JMSException e) {
log.error(e.getMessage());
}
}}
The log is always being the same as following:
INFO JMSQueueBrowser - ---------Queue Name: sampleQueue-----------
INFO JMSQueueBrowser - ---------Queue Has Elements: false----------
The library used for JMS Queue is jbossall-client.jar.
Any answer will be appreciated. Thanks in advance.

Glassfish & JMS: Why do published messages not arrive at subscribers?

I have a Glassfish 3.1.2 server running on a remote machine (JDK 1.6.0_30). The following code is the stand-alone client running in a Java SE environment, connecting to the JMS using a JNDI lookup. The client is publisher and subscriber at the same time.
I created the JMS connection pool and topic as follows:
./asadmin create-jms-resource --restype javax.jms.ConnectionFactory jms/TopicConnectionFactory
./asadmin create-jms-resource --restype javax.jms.Topic jms/TopicUpdate
I start two instances of this client. The messages seem to be delivered - no errors - but the messages do not arrive at the subscribers ...
What I am doing wrong ?
Any help appreciated - many thanks in advance!
public class JMS implements MessageListener {
private TopicConnectionFactory factory;
private TopicConnection connection;
private Topic topic;
private void subscribe() {
try {
System.setProperty("org.omg.CORBA.ORBInitialHost", "192.168.1.6");
System.setProperty("org.omg.CORBA.ORBInitialPort", "3700");
InitialContext ctx = new InitialContext();
factory = (TopicConnectionFactory)ctx.lookup("jms/TopicConnectionFactory");
topic = (Topic)ctx.lookup("jms/TopicUpdate");
connection = factory.createTopicConnection();
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
TopicSubscriber subscriber = session.createSubscriber(topic);
subscriber.setMessageListener(this);
connection.start();
while(true) {
Thread.sleep(5000);
sendMessage();
}
} catch (InterruptedException ex) {
Logger.getLogger(JMS.class.getName()).log(Level.SEVERE, null, ex);
} catch (NamingException ex) {
Logger.getLogger(JMS.class.getName()).log(Level.SEVERE, null, ex);
} catch (JMSException ex) {
Logger.getLogger(JMS.class.getName()).log(Level.SEVERE, null, ex);
}
}
private void sendMessage() {
try {
TopicSession session = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
TopicPublisher publisher = session.createPublisher(topic);
TextMessage message = session.createTextMessage();
message.setText("Message from client.");
publisher.send(message);
session.close();
System.out.println("Message sent.");
} catch (JMSException ex) {
Logger.getLogger(JMS.class.getName()).log(Level.SEVERE, null, ex);
}
}
#Override
public void onMessage(Message msg) {
System.out.println("Message received.");
}
public JMS() {
subscribe();
}
public static void main(String[] args) {
new JMS();
}
}
When you use true as the first argument when creating a session, the acknowledge mode is ignored and you're assumed to be transacted. try it with the first argument as false.
Just so it's clear, modify this line of code:
TopicSession session = connection.createTopicSession(true, Session.AUTO_ACKNOWLEDGE);
to be :
TopicSession session = connection.createTopicSession(false, Session.AUTO_ACKNOWLEDGE);
In your send message method.
It's good idea to have publisher and subscriber different.I
Here is code how to subscribe using Spring JMS template.
public class MsgReader implements
SessionAwareMessageListener<Message> {
#Override
public void onMessage(Message message, Session session) throws JMSException {
if (message instanceof TextMessage) {
try {
System.out.println(((TextMessage) message).getText());
} catch (JMSException ex) {
throw new RuntimeException(ex);
}
} else {
throw new IllegalArgumentException(
"Message must be of type TextMessage");
}
}
}
Spring Bean file.
Finally load beans.
public class SpringJMSTest {
/**
* #param args
*/
public static void main(String[] args) {
ApplicationContext context = new FileSystemXmlApplicationContext(new String[]{"/resource/consumerBean.xml"});
}
}
Now you will start receiving messages in console.

Categories

Resources