Spring Boot - JMS Connection to initialize later - spring

I am connecting to JMS queue using t3 protocol using DefaultMessageListenerContainer and it is working fine if the t3 protocol is UP.
But If the t3 URL is down, then the Listener cannot re-register the Bean once the t3 URL is UP. I have override the Listener to make a call but not able to re-register the Bean after application started.
#Bean
public QueueConnectionFactory queueConnectionFactory() {
Context m_context = getInitialContext();
QueueConnectionFactory queueConnectionFactory = new JMSConnectionFactory();
try {
System.out.println("Connection Factory");
queueConnectionFactory = (QueueConnectionFactory) m_context
.lookup("myconnectionfactory");
} catch (Exception e) {
System.err.println("Exception Connection Factory goes down");
}
return queueConnectionFactory;
}
#Bean
public Context getInitialContext() {
try {
Properties h = new Properties();
System.out.println("getInitialContext ");
h.put(Context.INITIAL_CONTEXT_FACTORY, "weblogic.jndi.WLInitialContextFactory");
h.put(Context.PROVIDER_URL, "t3://100.21.101.12:7001");
return new InitialContext(h);
} catch (Exception e) {
System.error.println("Error at getInitialContext");
}
return null;
}
#Bean
public Queue jmsQueue() {
Context m_context = getInitialContext();
Queue jmsQueue = new MQQueue();
try {
System.out.println("jmsQueue ");
jmsQueue = (Queue) m_context.lookup("myqueue");
} catch (Exception e) {
System.error.println("Error at JMS");
}
return jmsQueue;
}
#Bean
public DefaultMessageListenerContainer messageListener() {
DefaultMessageListenerContainer listener = new DefaultMessageListenerContainer();
try {
System.out.println("messageListener ");
listener.setConcurrentConsumers("4");
listener.setConnectionFactory((ConnectionFactory) queueConnectionFactory());
listener.setDestination((Destination) jmsQueue());
listener.setMessageListener(queueListener());
} catch (Exception e) {
System.out.println("Exception >>"+e);
}
return listener;
}
2022-08-05 15:16:24.615 ERROR 56480 --- [ssageListener-5] .b.e.s.DefaultMessageListenerContainer : Could not refresh JMS Connection for destination from DefaultMessageListener'queue:///' - retrying using FixedBackOff{interval=5000, currentAttempts=52, maxAttempts=unlimited}. Cause: null
Is there any way to do this bean to get register using the Listener.

That is an error from the listener container, not the registration of the connection factory bean.
You should be able to set the containers to not start automatically, using
spring.jms.listener.auto-startup=false
https://docs.spring.io/spring-boot/docs/current/reference/html/application-properties.html#application-properties.integration.spring.jms.listener.auto-startup
Then you can start them manually (in a try block) via the JmsListenerEndpointRegistry bean.

Related

Does DefaultMessageListenerContainer stop , shutdown close database connections?

I have a DefaultMessageListenerContainer which is processing a message from the queue.
While the message is being processed -- stop , shutdown methods are called on DefaultMessageListenerContainer. Does this close database connections?
Looks like it is closing the database connections and hence the message being processed is getting interrupted from completely processing.
I see these errors :
o.s.jdbc.support.SQLErrorCodesFactory : Error while extracting database name
Closed Connection; nested exception is java.sql.SQLRecoverableException: Closed Connection
could these be because the DefaultMessageListenerContainer was stopped and shutdown ?
My code is as follows . startStopContainer is where I am trying to stop and shutdown container. I want to shutdown container only if listener completed processing the current message. I added logic to figure out if listener completed processing .
Is the below logic the only way or is there a better way to figure out if listener completed processing. Please suggest. Thank you.
public class MyMessageConsumerFacade {
private ConnectionFactory connectionFactory() {
ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory();
connectionFactory.setBrokerURL(url);
connectionFactory.setUserName(userName);
connectionFactory.setPassword(password);
return connectionFactory;
}
#Bean
public MessageListenerContainer listenerContainer() {
DefaultMessageListenerContainer container = new DefaultMessageListenerContainer();
container.setConnectionFactory(connectionFactory());
container.setDestinationName(queueName);
container.setMessageListener(new MyJmsListener());
return container;
}
}
public class MyJmsListener implements MessageListener {
public boolean onMessageCompleted;
public void onMessage(Message message) {
onMessageCompleted = false;
processMessage(message);
onMessageCompleted = true;
}
}
private String startStopContainer(ExecutionContext etk) {
String response = "success";
AnnotationConfigApplicationContext context = null;
DefaultMessageListenerContainer myNewContainer = null;
if (context == null) {
context = new AnnotationConfigApplicationContext(MyMessageConsumerFacade.class);
}
if (myNewContainer == null) {
myNewContainer = context.getBean(DefaultMessageListenerContainer.class);
}
MyCaseMessageJmsListener messageJmsListener = (MyCaseMessageJmsListener) myNewContainer.getMessageListener();
if (!myNewContainer.isRunning()) {// container not running
myNewContainer.start();
}
//due to some business logic we need to stop listener every 5 minutes, so sleep for 5 minutes and then stop
try {
Thread.sleep(300000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
if (myNewContainer.isRunning()) {
myNewContainer.stop();
}
//Before shutting down container , make sure listener processed all msgs completely
if(!messageJmsListener.isOnMessageCompleted) {
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(messageJmsListener.isOnMessageCompleted) {
myNewContainer.shutdown();
}
if (context != null) {
context.destroy();
}
return response;
}
Is there a better way than this?
No; the container knows nothing about JDBC or any connections thereto.
Stopping the container only stops the JMS consumer(s) the consumers from receiving messages; shutDown() on the container closes the consumer(s).
Something else is closing your JDBC connection.

IBM-MQ (ibm.mq.allclient:jar:9.2.2.0) Spring Boot dynamic listener fails with too many MQ connections with compcode '2' ('MQCC_FAILED') reason 2537

Error : MQ call failed with compcode '2' ('MQCC_FAILED') reason '2537' ('MQRC_CHANNEL_NOT_AVAILABLE')
Start-up of the application we fetch the queue name dynamically and create a JMS listener programmatically. Messages are processed within the transaction and if the transaction fails it rolls back to the IBM-MQ queue
public DefaultJmsListenerContainerFactory mqJmsListenerContainerFactory() throws JMSException {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(ibmConnectionFactory);
factory.setDestinationResolver(new DynamicDestinationResolver());
factory.setSessionTransacted(true);
factory.setConcurrency(requestConcurrency);
return factory;
}
for (IBMmqQueue aTradeQueue : requestMQQueueNames) {//we have dynamic queue names
if(StringUtils.isEmpty(aTradeQueue.getTradeRequestName())){
continue;
}
SimpleJmsListenerEndpoint endpoint = new SimpleJmsListenerEndpoint();
endpoint.setId(aTradeQueue.getTradeRequestName());
endpoint.setDestination(aTradeQueue.getTradeRequestName());
endpoint.setMessageListener(message -> {
TransactionStatus status = jmsTransactionManager.getTransaction(null);
try {
// This starts a new transaction scope. "null" can be used to get a default transaction model
ibmmqConsumer.tradeListener(message, aTradeQueue.getTradeRequestName());
jmsTransactionManager.commit(status);
} catch (Exception e) {
jmsTransactionManager.rollback(status);
try {
LOG.error("Failed to publish to Active-MQ & rolled back to IBM-MQ : " + message.getJMSMessageID());
} catch (JMSException ex) {
ex.printStackTrace();
}
}
});
try {
registrar.setContainerFactory(this.mqJmsListenerContainerFactory());
} catch (JMSException e) {
e.printStackTrace();
}
registrar.registerEndpoint(endpoint);
}

JMS Message Persistence on ActiveMQ

I need to ensure redelivery of JMS messages when the consumer fails
The way the producer is set up now - DefaultJmsListenerContainerFactory and Session.AUTO_ACKNOWLEDGE
I'm trying to build a jar and try in here to save the message into the server, once the app is able to consume, the producer in the jar will produce the message to the app.
Is that a good approach to do so?! any other way/recommendation to improve this?
public void handleMessagePersistence(final Object bean) {
ObjectMapper mapper = new ObjectMapper();
final String beanJson = mapper.writeValueAsString(bean); // I might need to convert to xml instead
// parameterize location of persistence folder
writeToDriver(beanJson);
try {
Producer.produceMessage(beanJson, beanJson, null, null, null);
} catch (final Exception e) {
LOG.error("Error producing message ");
}
}
here what I have to writ out the meesage:
private void writeToDriver(String beanJson) {
File filename = new File(JMS_LOCATION +
LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS")) + ".xml");
try (final FileWriter fileOut = new FileWriter(filename)) {
try (final BufferedWriter out = new BufferedWriter(fileOut)) {
out.write(beanJson);
out.flush();
}
} catch (Exception e) {
LOG.error("Unable to write out : " + beanJson, e);
}
}

Using a Connection Name List in a JMS Load Balanced Environment

Our JMS infrastructure is load balanced. As a result of this, I am attempting to use a connectionNameList when configuring the connection factory. The idea here is that any JMS message that arrives on either of the primary or secondary queue manager will get picked up and processed. However, it only appears that messages are being picked up by the primary.
Here is my listener annotation:
#JmsListener(destination = "${request-queue}", containerFactory = "DefaultJmsListenerContainerFactory")
public void onMessage(Message msg) {
System.out.println(msg.toString());
}
Here is the JMS listener container factory:
#Bean(name = "DefaultJmsListenerContainerFactory")
public DefaultJmsListenerContainerFactory createJmsListenerContainerFactory() {
DefaultJmsListenerContainerFactory factory = new DefaultJmsListenerContainerFactory();
factory.setConnectionFactory(buildConnectionFactory());
factory.setConcurrency(numberOfListeners);
factory.setSessionAcknowledgeMode(Session.AUTO_ACKNOWLEDGE);
factory.setSessionTransacted(false);
factory.setErrorHandler(queueErrorHandler);
factory.setBackOff(getBackOffStrategy());
return factory;
}
And here is the connection factory:
#Bean(name = "MQConnectionFactory")
public ConnectionFactory buildConnectionFactory() {
try {
MQConnectionFactory mqcf = new MQConnectionFactory();
mqcf.setConnectionNameList(mq1.daluga.com(2171),mq2.daluga.com(2171));
mqcf.setChannel(channel);
mqcf.setTransportType(WMQConstants.WMQ_CM_CLIENT);
return mqcf;
} catch (Exception e) {
throw new RuntimeException(message, e);
}
}
I suspect something in my configuration is just not right. Is there anything obvious that folks see that might cause messages not to be picked up from the secondary queue manager?
Thanks!

jms sending message on any server

I want to write generic code for sending message on any jms server. so for that i thought if i have jndi.properties file then we can place server configuration in this file and we can access this file through the code but i am able to do this only for 'ActiveMQ Server'. Now i am facing problems to send the message on any other server like glassfish server or jboss server. can somebody help me to do this task.
Here is my code :
public class Producer
{
public Producer() throws JMSException, NamingException,IOException
{
InputStream is = getClass().getResourceAsStream("my.jndi.properties");
Properties jndiParamaters = new Properties();
jndiParamaters.load(is);
Context jndi = new InitialContext(jndiParamaters);
ConnectionFactory conFactory = (ConnectionFactory) jndi.lookup("connectionFactory");
Connection connection;
connection = conFactory.createConnection();
try
{
connection.start();
Session session = connection.createSession(false,Session.AUTO_ACKNOWLEDGE);
Destination destination = (Destination) jndi.lookup("Myqueue");
MessageProducer producer = session.createProducer(destination);
TextMessage message = session.createTextMessage("Hello World!");
producer.send(message);
System.out.println("Sent message '" + message.getText() + "'");
}
finally
{
connection.close();
}
}
public static void main(String[] args) throws JMSException
{
try
{
BasicConfigurator.configure();
new Producer();
}
catch (Exception e)
{
e.printStackTrace();
}
}
}
thanks
Have you tried using the Spring JMS Template? http://static.springsource.org/spring/docs/2.0.x/reference/jms.html
It provides an abstraction layer to JMS and could probably help you when your implementation changes.

Resources