I am using Apache Camel with Spring to send messages from my Java service. I need to reset JMS connection in case of any error occurred at exchange. I am using below code to achieve my objective.
try
{
producerTemplate.sendBody(endPoint, bytes);
}
catch (final RuntimeCamelException exception)
{
LOGGER.error("Exception occured in sendBody", exception.getMessage(), exception);
handleError(); // handle error here.
}
In camel context, I have defined CachingConnectionFactory with exception listener and made reconnectOnException=true
<bean id="testConnectionFactory" class="org.apache.qpid.jms.JmsConnectionFactory">
<property name="username" value="${user.name}" />
<property name="password" value="${user.password}" />
<property name="clientID" value="${host.address}" />
<property name="remoteURI"
value="amqp://${host.address}:${host.port}?jms.clientID=${host.address}?jms.username=${user.name}&jms.password=${user.password}&jms.redeliveryPolicy.maxRedeliveries=${message.retry.count}&amqp.saslMechanisms=PLAIN" />
</bean>
<bean id="testCachingConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="exceptionListener" ref="testCachingConnectionFactory" />
<property name="targetConnectionFactory" ref="testConnectionFactory" />
<property name="reconnectOnException" value="true" />
</bean>
In my case, JMSSecurityException is thrown from try block at below line
producerTemplate.sendBody(endPoint, bytes)
execution goes inside catch block but OnException() of SingleConnectionFactory is never called even though exceptionListener is defined. The idea is to call ultimately resetConnection() (inside OnException) to reset JMS connection.
Implement ExceptionListenerand add the exception listener definition as a property to your spring connection factory testCachingConnectionFactory.
For example create an exception listener class (component) JmsExceptionListener:
public class JmsExceptionListener implements ExceptionListener {
#Override
public void onException(JMSException exception) {
// what ever you wanna do here!
}
}
Then add a bean definition for JmsExceptionListener:
<bean id="jmsExceptionListener" class="JmsExceptionListener"></bean>
And then add the definition as an exception-listener property:
<property name="exceptionListener" ref="jmsExceptionListener"/>
instead of what you are using in your configuration:
<property name="exceptionListener" ref="testCachingConnectionFactory" />
Related
Below is the config
<jms:outbound-channel-adapter id="someId" channel="inputChannel"
connection-factory="${connection.factory}" destination="queue">
<jms:request-handler-advice-chain>
<bean class="org.springframework.integration.handler.advice.ExpressionEvaluatingRequestHandlerAdvice">
<property name="onSuccessExpression" value="T(Boolean).TRUE"/>
<property name="successChannelName" value="afterSuccessDeliveryMessageChannel"/>
<property name="onFailureExpression" value="T(Boolean).FALSE"/>
<property name="failureChannelName" value="failureChannel"/>
</bean>
<bean id="retryWithBackoffAdviceSession"
class="org.springframework.integration.handler.advice.RequestHandlerRetryAdvice">
<property name="retryTemplate">
<bean class="org.springframework.retry.support.RetryTemplate">
<property name="retryPolicy">
<bean class="org.springframework.retry.policy.SimpleRetryPolicy">
<property name="maxAttempts" value="5"/>
</bean>
</property>
</bean>
</property>
<property name="recoveryCallback">
<bean class="org.springframework.integration.handler.advice.ErrorMessageSendingRecoverer">
<constructor-arg ref="failureChannel"/>
</bean>
</property>
</bean>
</jms:request-handler-advice-chain>
</jms:outbound-channel-adapter>
I am retrying message 5 times and then using recoveryCallBack logging message to some DB.
It works fine retry 5 times and call failureChannel channel but once it calls the failureChannel then again it pass to afterSuccessDeliveryMessageChannel.
I am not sure what I am doing wrong here?
I am expecting once it failed it should go failedChannel NOT again back to afterSuccessDeliveryMessageChannel.
Your problem is like this:
Your Retry advice sends to the failureChannel from the recoveryCallback and exits, successfully.
Then when we look into the ExpressionEvaluatingRequestHandlerAdvice code, we see this logic:
try {
Object result = callback.execute();
if (this.onSuccessExpression != null) {
evaluateSuccessExpression(message);
}
return result;
}
So, since there is no exception calling callback, it goes to the successChannel configured.
To make it fail and go to the failureChannel configured, you should consider to not use that recoveryCallback. Then RequestHandlerRetryAdvice will throw an exception which is going to be caught by the ExpressionEvaluatingRequestHandlerAdvice and sent to that failureChannel. There won't be onSuccessExpression evaluation since we will end up with an exception.
I have a standalone application using Spring to connect to TIBCO (queues). Occasionally, for various reasons, TIBCO connection is closed by the server. Most of the things are recovering from this. However, sometimes JmsTemplate is not able to send response because of the error below. I have a retry in place but the same error keeps coming (see trace below).
Details that may be important:
I am using DefaultMessageListenerContainer to get the request and send the response in that receiving thread. Also, I am using the same connection factory for both DefaultMessageListenerContainer and JmsTemplate.
Caused by: org.springframework.jms.IllegalStateException: Session is closed; nested exception is javax.jms.IllegalStateException: Session is closed
at org.springframework.jms.support.JmsUtils.convertJmsAccessException(JmsUtils.java:279)
at org.springframework.jms.support.JmsAccessor.convertJmsAccessException(JmsAccessor.java:169)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:487)
at org.springframework.jms.core.JmsTemplate.send(JmsTemplate.java:559)
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:682)
at org.springframework.jms.core.JmsTemplate.convertAndSend(JmsTemplate.java:670)
at org.springframework.integration.jms.JmsSendingMessageHandler.send(JmsSendingMessageHandler.java:149)
at org.springframework.integration.jms.JmsSendingMessageHandler.handleMessageInternal(JmsSendingMessageHandler.java:116)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:127)
... 83 more
Caused by: javax.jms.IllegalStateException: Session is closed
at com.tibco.tibjms.TibjmsxSessionImp._createProducer(TibjmsxSessionImp.java:1067)
at com.tibco.tibjms.TibjmsxSessionImp.createProducer(TibjmsxSessionImp.java:5080)
at org.springframework.jms.core.JmsTemplate.doCreateProducer(JmsTemplate.java:1114)
at org.springframework.jms.core.JmsTemplate.createProducer(JmsTemplate.java:1095)
at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:591)
at org.springframework.jms.core.JmsTemplate$3.doInJms(JmsTemplate.java:562)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:484)
... 89 more
Communication with the TIBCO queue is done using Spring framework. Here is the configuration. A message is received by DefaultMessageListenerContainer, processed and JmsTemplate is used to send back the response. Connection factory is shared between receiver and sender (can this be an issue?).
<bean id="connectionFactory"
class="org.springframework.jms.connection.SingleConnectionFactory">
<constructor-arg ref="tibcoJNDI" />
<property name="targetConnectionFactory">
<bean class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate" ref="tibcoJNDI"/>
<property name="jndiName" value="${tibco.queueConnectionFactory}" />
</bean>
</property>
<property name="reconnectOnException" value="true"/>
</bean>
<bean id="client.req.msg.lstnr" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="autoStartup" value="false"/>
<property name="connectionFactory" ref="connectionFactory"/>
<property name="destination" ref="ext_client.request.queue"/>
<property name="sessionAcknowledgeMode" value="3"/>
<property name="concurrentConsumers" value="6"/>
<property name="receiveTimeout" value="60000"/>
</bean>
<jms:outbound-channel-adapter
jms-template="ext.outbound.jms.template"
channel="jms.to.ext.clnt.reply"/>
<bean id="ext.outbound.jms.template" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
<property name="defaultDestination" ref="ext_client.reply.queue"/>
<property name="timeToLive" value="10800000" />
<property name="explicitQosEnabled" value="true" />
</bean>
One more detail that might help. I just noticed that the first exception is actually different. There is a "Connection is closed" exception first followed by multiple "Session is closed" exception (on retry).
Caused by: javax.jms.JMSException: Connection is closed
at com.tibco.tibjms.TibjmsxLink.sendRequest(TibjmsxLink.java:322)
at com.tibco.tibjms.TibjmsxLink.sendRequest(TibjmsxLink.java:286)
at com.tibco.tibjms.TibjmsxLink.sendRequestMsg(TibjmsxLink.java:261)
at com.tibco.tibjms.TibjmsxSessionImp._createProducer(TibjmsxSessionImp.java:1075)
at com.tibco.tibjms.TibjmsxSessionImp.createProducer(TibjmsxSessionImp.java:5080)
at org.springframework.jms.core.JmsTemplate.doCreateProducer(JmsTemplate.java:1114)
at org.springframework.jms.core.JmsTemplate.createProducer(JmsTemplate.java:1095)
at org.springframework.jms.core.JmsTemplate.doSend(JmsTemplate.java:591)
at org.springframework.jms.core.JmsTemplate$3.doInJms(JmsTemplate.java:562)
at org.springframework.jms.core.JmsTemplate.execute(JmsTemplate.java:484)
... 89 more
It seems that Spring does not handle properly this case. I solved this issue by overriding JmsTemplate (code below) and handling the exception myself (cleaning the session and the connection). I hope this helps.
public <T> T execute(SessionCallback<T> action, boolean startConnection) throws JmsException {
try {
return super.execute(action, startConnection);
} catch (JmsException jmse) {
logger.error("Exception while executing in JmsTemplate (will cleanup session & connection): ", jmse);
Object resourceHolder =
TransactionSynchronizationManager.getResource(getConnectionFactory());
if (resourceHolder != null && resourceHolder instanceof JmsResourceHolder) {
((JmsResourceHolder)resourceHolder).closeAll();
}
throw jmse;
}
}
This answer might help https://stackoverflow.com/a/24494739/208934 In particular:
When using JMS you shouldn't really cache the JMS Session (and
anything hanging of that such as a Producer). The reason being is that
the JMS Session is the unit of work within JMS and so should be a
short lived object. In the Java EE world that JMS Session might also
be enlisted with a global transaction for example and so needs to be
scoped correctly.
It sounds like you're reusing the same session for multiple operations.
I have seen this problem in some of my tests and telling Spring to use a pool of connections usually fixes it. If you were using active-mq then it can be done with the property spring.activemq.pooled=true. I'm not 100% how Tibco accomplishes the same thing.
You might get get same results by defining a PooledConnectionFactory bean that will override the default.
I have a DefaultMessageListenerContainer, which is (in my opinion) not scaling up. The Container is defined to listen on a queue, where 100 messages are located in.
I would expect, that the Container is going to any lengths, that the messages would be consumed as fast as it is possible (by observing the maxConcurrentConsumers configuration). So i would assume, that there are 7 concurrentConsumers. (beginning by 2 concurrentConsumers at container-startup) Some logging-information:
activeConsumerCount: 5
concurrentConsumers: 2
scheduledConsumerCount: 5
idleConsumerLimit: 1
idleTaskExecLimit: 1
maxConcurrentConsumers: 7
My Spring-config (a part of it):
<bean id="abstractMessageListenerContainer" class="my.package.structure.LoggingListenerContainer" abstract="true">
<property name="connectionFactory" ref="jmscfCee" />
<property name="maxConcurrentConsumers" value="7"/>
<property name="receiveTimeout" value="100000" />
<property name="concurrentConsumers" value="2" />
</bean>
<bean class="my.package.structure.LoggingListenerContainer" parent="abstractMessageListenerContainer">
<property name="destinationName" value="MY.QUEUE" />
<property name="messageListener" ref="myMessageListener" />
</bean>
<bean id="myMessageListener" class="my.package.structure.ListenerClass"></bean>
My Logging Container
public class LoggingListenerContainer extends DefaultMessageListenerContainer{
private static final Logger logger = Logger
.getLogger(LoggingListenerContainer.class);
#Override
protected void doInvokeListener(MessageListener listener, Message message)
throws JMSException {
logger.info("activeConsumerCount: " + this.getActiveConsumerCount());
logger.info("concurrentConsumers: " + this.getConcurrentConsumers());
logger.info("scheduledConsumerCount: " + this.getScheduledConsumerCount());
logger.info("idleConsumerLimit: " + this.getIdleConsumerLimit());
logger.info("idleTaskExecLimit: " + this.getIdleTaskExecutionLimit());
logger.info("maxConcurrentConsumers: " + this.getMaxConcurrentConsumers());
super.doInvokeListener(listener, message);
}
My Listener Class:
public class ListenerClass implements MessageListener {
public void onMessage(Message msg) {
//Do some business function
}
}
Could someone be so kind to correct my configuration or give me some tipps concerning my configuration or explain me the approach of the Container? (if i had misunderstood something)
I'm locally testing with ActiveMQ (in Production with WebSphere MQ) - if it's relevant for scalability topics.
EDIT:
<bean id="jmscfCee" class="org.apache.activemq.spring.ActiveMQConnectionFactory">
<property name="brokerURL">
<value>${jmscfCee.hostName}</value>
</property>
</bean>
<bean id="jmscfCeeCachingConnectionFactory"
class="org.springframework.jms.connection.CachingConnectionFactory ">
<constructor-arg ref="jmscfCee" />
<property name="sessionCacheSize" value="10" />
</bean>
It depends. I had a similar issue with ActiveMQ a few years back, whereby its default behaviour is heavily opimized towards high volumes (many thousands) of small messages. By default each consumer will pre-fetch messages in batches of 1000, so if you have small numbers of messages you'll probably find they have all ended up in the pre-fetch buffer of one consumer, leaving the other consumers idle.
You can tune this behaviour using a prefetch policy, either on the connection URI or in the Spring configuration if that's how you're building your connection factory.
<amq:connectionFactory id="connectionFactory" brokerURL="vm://localhost">
<property name="prefetchPolicy">
<amq:prefetchPolicy all="1" />
</property>
</amq:connectionFactory>
The version of ActiveMQ I was using at the time didn't support a prefetch limit of 0 (i.e. don't prefetch, just go to the broker every time) but the documentation suggests that this is now allowed.
I use spring 3.0.5 and with hibernate.
Interceptor is working.
Send domain id to jms queue.
Consumer recive it and search domain, but is faster than database commit and i get null.
How to be shure that interceptor will be called after db commit ?
appCtx.xml
<tx:annotation-driven order="10" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
<aop:aspectj-autoproxy />
<bean id="domainProducerHandler" depends-on="domainEventService"
class="org.test.service.DomainProducerHandler" factory-method="aspectOf">
<property name="domainEventService" ref="domainEventService" />
<property name="order" value="1" />
</bean>
===================service class=====================
#SendDomainEvent
#Transactional
public ProtoMessage sendDonation(String aa) {
Domain domainObj = new Domain();
domainRepository.saveAndFlush(domainObj);
return domain;
}
==================interceptor class===================**
#AfterReturning(
pointcut="#annotation(org.test.service.SendDomainEvent)",
returning="retVal")
public void processDomainReturn(Object retVal) {
....
try {
domainEventService.publishToQueue(endDonationSuccessEvent);
} catch (Exception e) {
log.error("error during send endDonationSuccessEvent: " + e);
}
}
Interceptor class implements Ordered interface. I set order parameter to tx:annotation-driven order="10" but it doesn't work.
What do you mean by "order parameter"? The supported ways of ordering advice in Spring AOP are with the #Ordered annotation or the org.springframework.core.Ordered interface, which also defines constants for the highest and lowest priorities.
I am writing Apache CXF web services and use Spring for loading my beans. My only bean is calling external process (MATLAB) from Java. My beans definition looks as below:
<bean id="matlabEngine" class="org.burch.pca.matlab.MatlabEngine"
init-method="start" scope="singleton">
<constructor-arg value="${matlab.engine.path}"></constructor-arg>
</bean>
<bean
class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchContextAttributes" value="true" />
<property name="contextOverride" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:/pca-engine.properties</value>
</list>
</property>
</bean>
Piece of my MatlabEngine bean is as below:
/**
* Path to MATLAB engine.
*/
private String pathToEngine;
public MatlabEngine(String pathToEngine) throws MatlabConnectionException, MatlabInvocationException{
super();
setPathToEngine(pathToEngine);
}
/**
* Starts engine and goes to path defined by argument
* #param pathToEngine
* #throws MatlabConnectionException
* #throws MatlabInvocationException
*/
public void start() throws MatlabConnectionException, MatlabInvocationException{
//Create a factory
RemoteMatlabProxyFactory factory = new RemoteMatlabProxyFactory();
//Get a proxy, launching MATLAB in the process
proxy = factory.getProxy();
//Display welcoming messages in MATLAB Command Window
proxy.eval(MatlabCommandsRegistry.disp(MATLAB_ENGINE_WELCOME_1));
proxy.eval(MatlabCommandsRegistry.disp(MATLAB_ENGINE_WELCOME_2));
if(pathToEngine!= null && !"".equals(pathToEngine)){
logM("Switching to engine directory...");
String goToEngineRootDir = MatlabCommandsRegistry.cd(pathToEngine);
proxy.eval(goToEngineRootDir);
logM("Sucessfully changed engine dir to "+pathToEngine);
}
}
When I deploy web services in Tomcat, it brings up MATLAB process nicely (bean gets loaded).
However, when I create client request to web service endpoint with this code:
public static void main(String args[]) throws Exception {
JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();
factory.getInInterceptors().add(new LoggingInInterceptor());
factory.getOutInterceptors().add(new LoggingOutInterceptor());
factory.setServiceClass(UploadService.class);
factory.setAddress("http://localhost:8080/auth-ws-1.0.0/services/upload");
UploadService client = (UploadService) factory.create();
UploadEntity resume=new UploadEntity();
resume.setFileName("Image490");
resume.setFileType("jpg");
//Work arround data handler....
DataSource source = new FileDataSource(new File("C:\\Users\\Pictures\\thumb.png"));
DataHandler dataHandle = new DataHandler(source);
ByteArrayOutputStream stream = new ByteArrayOutputStream();
dataHandle.writeTo(stream);
resume.setPayload(stream.toByteArray());
client.uploadFile(resume);
System.exit(0);
}
my server brings out new instance of MATLAB process (bean gets loaded again - very heavy and undesirable). What could I do to have only one bean which will be used to serve all processing and all requests? I am new to Spring, and I am thinking that my problem is that I am dealing with multiple contexts here. I want them to share a single instance of a singleton bean but don't know how to manage this.
Thank you for your time!
You should enable singleton mode for you bean.
Look at this: http://static.springsource.org/spring/docs/1.2.x/reference/beans.html#beans-factory-modes
Bean defenition may looks like this:
<!-- Spring property loading bean -->
<bean
class="org.springframework.web.context.support.ServletContextPropertyPlaceholderConfigurer" singleton="true">
<property name="systemPropertiesModeName" value="SYSTEM_PROPERTIES_MODE_OVERRIDE" />
<property name="searchContextAttributes" value="true" />
<property name="contextOverride" value="true" />
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:/pca-engine.properties</value>
</list>
</property>
</bean>
I think you should manage MATLAB process lifecircle wisely to decrease resource loading.