I am using WerbLogic 10.3.5 and Spring 3.0 to implement a JMS queue. I have the following Spring configuration:
<!-- JMS Configuration -->
<bean id="paymentlistener" class="com.myproject.service.impl.PaymentListener"/>
<bean id="paymentlistenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="concurrentConsumers" value="10"/>
<property name="connectionFactory" ref="paymentConnectionFactory"/>
<property name="destination" ref="paymentQueue"/>
<property name="messageListener" ref="paymentlistener"/>
</bean>
<bean id="paymentQueue" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiName" value="jms/paymentResponseHandlerQueue"/>
<property name="jndiTemplate" ref="jndiTemplate"/>
</bean>
<bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate">
<property name="environment">
<props>
<prop key="java.naming.factory.initial">weblogic.jndi.WLInitialContextFactory</prop>
<prop key="java.naming.provider.url">t3://localhost:7001</prop>
</props>
</property>
</bean>
<bean id="paymentConnectionFactory" class="org.springframework.jndi.JndiObjectFactoryBean">
<property name="jndiTemplate" ref="jndiTemplate"/>
<property name="jndiName" value="jms/paymentResponseHandlerConnectionFactory"/>
</bean>
<bean id="jmsDestinationResolver" class="org.springframework.jms.support.destination.JndiDestinationResolver">
<property name="jndiTemplate" ref="jndiTemplate"/>
<property name="cache" value="true"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="paymentConnectionFactory"/>
<property name="destinationResolver" ref="jmsDestinationResolver"/>
<property name="defaultDestination" ref="paymentQueue"/>
<property name="sessionAcknowledgeModeName" value="DUPS_OK_ACKNOWLEDGE"/>
<property name="sessionTransacted" value="true"/>
</bean>
My message creation code looks like this:
MessageCreator messageCreator = new MessageCreator() {
public Message createMessage(Session session) throws JMSException {
logger.debug("Session ack mode: " + session.getAcknowledgeMode());
return session.createObjectMessage(payment);
}
};
jmsTemplate.send("jms/paymentResponseHandlerQueue", messageCreator);
And my listener looks like this:
#Override
public void onMessage(Message message) { // , Session session
if (!(message instanceof ObjectMessage)) {
throw new IllegalStateException("The PaymentListener queue expects an object message");
}
ObjectMessage objectMessage = (ObjectMessage) message;
try {
logger.debug("Is Message redelivered:" + objectMessage.getJMSRedelivered ());
if (objectMessage.getObject() instanceof CreditCardPaymentDTO) {
logger.debug("Object is of type CreditCardPaymentDTO");
// The user that just logged in
CreditCardPaymentDTO payment = (CreditCardPaymentDTO) objectMessage.getObject();
otpCollectorDAO.savePayment(payment);
}
} catch (JMSException e) {
logger.error(e);
throw new RuntimeException(e);
} catch (PaymentResponseException e) {
logger.error(e);
throw new RuntimeException(e);
}
}
The message is being created correctly, and the onMessage() method of my listener is being called, but if the logic fails and I throw a RuntimeException(), the message does not get redelivered. I've tried many slight variations of the above code (eg. setting sessionAcknowledgeModeName=SESSION_TRANSACTED and explicitly rolling back the transaction), but the message is never re-queued. What is the trick to getting failed messages to redeliver?
I believe you need to set sessionTransacted to true for the DefaultMessageListenerContainer:
<bean id="paymentlistenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
...
<property name="sessionTransacted" value="true"/>
</bean>
You can find here relevant reference documentation.
Related
I have an activemq queue where messages are being sent to and the application uses Spring JmsTemplate receiveSelected(selector) to receive the messages synchronously. Message is processed before it is acknowledged. If the broker or the application shuts down before acknowledging the message while it is being processed the message needs to be resent or redelivered without getting lost. My understanding is with client_acknowledgement messages gets resent if not acknowledged also.
Configuration
<bean id="jmsConnection" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="broker" value="tcp://localhost:61616" />
<property name="redeliveryPolicy" ref="redeliveryPolicy" />
</bean>
Redelivery Policy:
<bean id="redeliveryPolicy" class="org.apache.activemq.RedeliveryPolicy">
<property name="initialRedeliveryDelay" value="1000" />
<property name="redeliveryDelay" value="10000" />
<property name="maximumRedeliveries" value="2" />
<property name="useExponentialBackOff" value="true" />
<property name="backOffMultiplier" value="5" />
</bean>
JmsTemplate:
<bean id="jmstemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory">
<bean class="org.springframework.jms.connection.CachingConnectionFactory">
<property name="targetConnectionFactory" ref="jmsConnection" />
</bean>
</property>
<property name="receiveTimeout" value="5000"/>
<property name="defaultDestinationName" value="messageQueue"/>
<property name="explicitQosEnabled" value="true"/>
<property name="sessionAcknowledgeMode" value="2"/>
<property name="sessionTransacted" value="false"/>
</bean>
Session call back to receive message:
Object getProcessedMessageObject {
return jmsTemplate.execute(session -> {
#Override
public Object doInJms (Session session) throws JMSException {
Object tmp = null;
MapMessage msg = (MapMessage) jmsTemplate.receiveSelected(selector);
try {
if (msg != null) {
MapMessage receivedMsg = msg;
tmp = processMsg(receivedMsg) if (tmp != null) {
msg.acknowledge();
}
}
} catch (JMSException) {
throw new RuntimeException();
} return tmp;
}
});
}
I am getting "Consumer is closed" when msg.acknowledge() is called. When I stop and restart my application the messages are not redelivered as they are not acknowledged. I'm trying to understand what I am missing and how to make it work.
How can i handle exceptions from Spring context .
Consider the context
<bean id="dataSource" class="oracle.jdbc.pool.OracleDataSource">
<property name="URL" value="jdbc:oracle:thin:#localhost:1521/xe" />
<property name="user" value="abc" />
<property name="password" value="abc" />
</bean>
<bean id="abc" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean" >
<property name="dataSource" ref="dataSource" />
<property name="persistenceUnitName" value="default" />
<property name="persistenceXmlLocation" value="classpath*:META-INF/abc-persistence.xml" />
</bean>
And am loading context in main method
public static void main(String[] args) {
ApplicationContext applicationContext = null;
try {
applicationContext = new ClassPathXmlApplicationContext("/META-INF/spring/test.xml");
} catch (Exception e) {
System.out.println("Exception caugtht " + e);
}
}
Lets say database is down .now when i load context exceptions are not caught in catch block. Is there any way to handle this ?
On production environment I got following error.
org.springframework.jdbc.CannotGetJdbcConnectionException: Could not get JDBC Connection; nested exception is java.sql.SQLException: Cannot create PoolableConnectionFactory (The Network Adapter could not establish the connection)
When I connect through toad, it said ORA-12541 no listener error. The issue resolved after restart of linstener.
But the actual issue may be that lot of connections opened by the web application and it is not closing that. Following are my code and configuration.
I am using EcllipseLink, dbcp2 for connections
<persistence-unit name="persistance-unit" transaction-type="RESOURCE_LOCAL">
<class>com.company.model.Characteristic</class>
...
<!--more classes -->
<exclude-unlisted-classes>true</exclude-unlisted-classes>
<properties>
<property name="eclipselink.weaving" value="false"/>
</properties>
</persistence-unit>
dbcp2 settings
<bean id="datasource" class="org.apache.commons.dbcp2.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}" />
<property name="password" value="${jdbc.password}" />
<property name="maxIdle" value="10" />
<property name="maxTotal" value="200" />
<property name="maxWaitMillis" value="60000" />
<property name="validationQuery" value="select 1 from dual" />
<property name="validationQueryTimeout" value="10" />
<property name="testOnBorrow" value="true" />
<property name="testWhileIdle" value="true" />
<property name="timeBetweenEvictionRunsMillis" value="30000" />
<property name="minEvictableIdleTimeMillis" value="60000" />
<property name="numTestsPerEvictionRun" value="5" />
<property name="defaultAutoCommit" value="true" />
</bean>
Connection Factory
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="persistenceUnitName" value="persistance-unit"/>
<property name="dataSource" ref="datasource"/>
<property name="jpaVendorAdapter">
<bean id="jpaAdapter" class="org.springframework.orm.jpa.vendor.EclipseLinkJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="database" value="ORACLE"/>
</bean>
</property>
</bean>
Transaction Management
Abstract GenericDao
public abstract class GenericDao<T> implements IGenericDao<T> {
#PersistenceContext
protected EntityManager entityManager
private Class<T> type
public GenericDao() {
Type t = getClass().getGenericSuperclass()
ParameterizedType pt = (ParameterizedType) t
type = (Class) pt.getActualTypeArguments()[0]
}
#Override
public T save(final T t) {
this.entityManager.persist(t)
return t
}
public def saveObj(def t) {
try {
this.entityManager.persist(t)
}catch(e){
log.info("Error occured: ", e)
}
return t
}
#Override
public void delete(final Object id) {
this.entityManager.remove(this.entityManager.getReference(type, id))
}
#Override
public T find(final Object id) {
return (T) this.entityManager.find(type, id)
}
#Override
public T update(final T t) {
return this.entityManager.merge(t)
}
#Override
public def executeNamedQuery(String namedQueryName, Map<String, Object> queryParams)
{
List results = null
try {
Query query = entityManager.createNamedQuery(namedQueryName)
if (queryParams) {
addPrametersToQuery(query, queryParams)
}
results = query.getResultList()
} catch (NoResultException e) {
} catch (Exception e) {
log.error("Error occured: ", e)
throw e
}
return results
}
}
Dao component using that Generic Dao
#Component("characteristicDao")
class CharacteristicDao extends GenericDao<Characteristic> implements ICharacteristicDao {
public def getCharacteristics(String name){
log.info("Entering getCharacteristics")
Characteristic characteristic = (PrmCharacteristic) executeQueryWithSingleResult("select b from Characteristic b where b.name=:name", [name:name])
log.info("Exiting getCharacteristics")
return characteristic
}
}
I use CharacteristicDao component to interact with database. Similarly I have other dao also. I have just given one example.
I use #Transactional in service for the transaction. Any master can help me please what I am doing wrong.
I have a problem the InOnly exchange pattern what I use with aciveMq.
I wrote a module what run in ServiceMix. It works correctly except that it send every message to dead letter queue (ActiveMQ.DLQ). If I check the message then dlqDeliveryFailureCause contains this message: java.lang.Throwable: Message Expired.
I checked the JMSExpiration = 0.
The route:
from("direct:" + reqOutQueue).id("reqInEnritch")
.log("Start dispatch")
.setExchangePattern(ExchangePattern.InOnly)
.recipientList().method(EsbDynamicRouter.class, "systemRoute").parallelProcessing();
The function, what gives back the endpoint list:
#RecipientList
public String[] systemRoute(#Body Object body) throws Exception
{
String[] result = null;
List<DispConfView> targetList;
int cikl = 0;
SystemQueueHelper systemInfo;
MessageHeaderHelper msgHeadHelp = new MessageHeaderHelper(body);
// The object contains the affected elements
targetList = dispHelp.getDispConfByMsgType(msgHeadHelp.getMsgType());
result = new String[targetList.size()];
for (DispConfView element : targetList)
{
// It builds the target andpoints
systemInfo = new SystemQueueHelper(element.getSystemCode(), null, msgHeadHelp.getDirection());
result[cikl] = systemInfo.getQueuName();
cikl++;
}
return result;
}
The list contains these values:
activemq:queue:ERP.req.in?exchangePattern=InOnly
activemq:queue:WF.req.in?exchangePattern=InOnly
As you see, I try to set the correct pattern, but every messages go to dead letter queue.
Please help, what I have to set up!
Thank you!
The solution:
It is settable in context file:
<!-- JMS configuration -->
<bean id="pooledJmsConnectionFactory" class="org.apache.activemq.pool.PooledConnectionFactory" init-method="start" destroy-method="stop">
<property name="maxConnections" value="1" />
<property name="connectionFactory" ref="jmsConnectionFactory" />
</bean>
<bean id="jmsConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<property name="brokerURL" value="failover:(tcp://localhost:61616)" />
<property name="redeliveryPolicy">
<bean class="org.apache.activemq.RedeliveryPolicy">
<property name="maximumRedeliveries" value="5"/>
</bean>
</property>
</bean>
<bean id="jmsConfig" class="org.apache.camel.component.jms.JmsConfiguration">
<property name="connectionFactory" ref="pooledJmsConnectionFactory"/>
<property name="cacheLevelName" value="CACHE_CONSUMER" />
<property name="disableReplyTo" value="true" />
</bean>
The "jmsConfig bean ""diasbleReplayTo" property solve the problem.
I have two Quartz (1.8.3) jobs, configured via Spring (2.5.6), one of them writes (send) to database, and one reads from it (check).
<bean id="scheduleFactory"
class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="Check"/>
<ref bean="Send"/>
</list>
</property>
</bean>
<bean id="Send" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="StatusMonitor" />
<property name="targetMethod" value="sendMessage" />
</bean>
</property>
<property name="cronExpression" value="0 0/1 * * * ?" />
</bean>
<bean id="Check" class="org.springframework.scheduling.quartz.CronTriggerBean">
<property name="jobDetail">
<bean class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
<property name="targetObject" ref="StatusMonitor" />
<property name="targetMethod" value="checkAndUpdateStatus" />
</bean>
</property>
<property name="cronExpression" value="30 0/1 * * * ?" />
</bean>
Transaction manager is set up:
<tx:annotation-driven transaction-manager="TransactionManager"/>
In both jobs I explicitly run read/write operations in transactions like this:
#Override
public synchronized void sendMessage() {
try {
TransactionTemplate tt = new TransactionTemplate(ptm);
tt.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus status) {
...
statusDAO.update(status);
...
}
});
log.info("Status was updated");
} catch (Exception e) {
...
}
}
where ptm is a TransactionManager bean, injected via Spring.
I see "Status was updated" record in logs, but when I read this record from transactional read method it is outdated sometimes. Moreover, when I use an SQL editor to read this record it is outdated too.
I don't understand, why transactions dont work in this case, do you have any ideas?
Thanks.
For anyone that might be interested. This worked for me
<bean name="applicationDataCollectorControllerJobDetail" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="org.mypckage.controller.jobs.ApplicationDataCollectorController" />
<property name="jobDataAsMap">
<map>
<!--<entry key="timeout" value="1" />-->
<entry key="genericService" value-ref="genericService" />
<entry key="applicationDataCollectorService" value-ref="applicationDataCollectorService" />
<entry key="transactionManager" value-ref="transactionManager" />
</map>
</property>
</bean>
--- in the scheduler bean---
#Override
protected void executeInternal(JobExecutionContext ctx) throws JobExecutionException {
getApplicationDataCollectorService().collectData(transactionManager);
}
----In the applicationDataCollectorService bean-----
public void collectData( org.springframework.transaction.jta.JtaTransactionManager transactionManager) {
try {
this.transactionManager = transactionManager;
testTransactionalSave();
} catch (Exception e) {
BUSY = false;
e.printStackTrace();
}
}
}
private void testTransactionalSave() throws Exception {
TransactionTemplate tt = new TransactionTemplate(transactionManager);
tt.execute(new TransactionCallbackWithoutResult() {
#Override
protected void doInTransactionWithoutResult(TransactionStatus ts) {
try {
ApplicationParameter appPara = null;
List<ApplicationParameter> appParaList = genericService.getListFromHQL("select o from ApplicationParameter as o order by o.id desc", false);
if (appParaList != null) {
if (appParaList.size() > 0) {
appPara = (ApplicationParameter) appParaList.get(0);
appPara.setLastBankStatementMailNum(appPara.getLastBankStatementMailNum() + 10);
appPara = (ApplicationParameter) genericService.mergeObject(appPara);
System.out.println(" num is now = " + appPara.getLastBankStatementMailNum());
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}
});
}
Note: dont forget to declare transactionManager as private properties in both beans with public setter and getter for spring to wire it up. Any Questions? yemiosigbesan#gmail.com