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.
Related
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 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.
I am looking for a guidance/solution with Spring batch integration. I have a directory to which external application will send xml files. My application should read the file content and move the file to another directory.
The application should be able to process the files in parallel.
Thanks in advance.
You can use Spring Integration ftp / sftp combined with Spring Batch:
1.Spring Integration Ftp Configuration :
<bean id="ftpClientFactory"
class="org.springframework.integration.ftp.session.DefaultFtpSessionFactory">
<property name="host" value="${host.name}" />
<property name="port" value="${host.port}" />
<property name="username" value="${host.username}" />
<property name="password" value="${host.password}" />
<property name="bufferSize" value="100000"/>
</bean>
<int:channel id="ftpChannel" />
<int-ftp:outbound-channel-adapter id="ftpOutbound"
channel="ftpChannel" remote-directory="/yourremotedirectory/" session-factory="ftpClientFactory" use-temporary-file-name="false" />
2.Create your reader and autowire a service to provide your items if needed :
#Scope("step")
public class MajorItemReader implements InitializingBean{
private List<YourItem> yourItems= null;
#Autowired
private MyService provider;
public YourItem read() {
if ((yourItems!= null) && (yourItems.size() != 0)) {
return yourItems.remove(0);
}
return null;
}
//Reading Items from Service
private void reloadItems() {
this.yourItems= new ArrayList<YourItem>();
// use the service to provide your Items
if (yourItems.isEmpty()) {
yourItems= null;
}
}
public MyService getProvider() {
return provider;
}
public void setProvider(MyService provider) {
this.provider = provider;
}
#Override
public void afterPropertiesSet() throws Exception {
reloadItems();
}
}
3. Create Your Own Item Processor
public class MyProcessor implements
ItemProcessor<YourItem, YourItem> {
#Override
public YourItem process(YourItem arg0) throws Exception {
// Apply any logic to your Item before transferring it to the writer
return arg0;
}
}
4. Create Your Own Writer :
public class MyWriter{
#Autowired
#Qualifier("ftpChannel")
private MessageChannel messageChannel;
public void write(YourItem pack) throws IOException {
//create your file and from your Item
File file = new File("the_created_file");
// Sending the file via Spring Integration Ftp Channel
Message<File> message = MessageBuilder.withPayload(file).build();
messageChannel.send(message);
}
5.Batch Configuration :
<bean id="dataSourcee"
class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="" />
<property name="url" value="" />
<property name="username" value="" />
<property name="password" value="" />
</bean>
<bean id="jobRepository"
class="org.springframework.batch.core.repository.support.JobRepositoryFactoryBean">
<property name="dataSource" ref="dataSourcee" />
<property name="transactionManager" ref="transactionManagerrr" />
<property name="databaseType" value="" />
</bean>
<bean id="transactionManagerrr"
class="org.springframework.batch.support.transaction.ResourcelessTransactionManager" />
<bean id="jobLauncher"
class="org.springframework.batch.core.launch.support.SimpleJobLauncher">
<property name="jobRepository" ref="jobRepository" />
</bean>
6.Another ApplicationContext file to Configure Your Job :
<context:annotation-config />
<bean id="provider" class="mypackage.MyService" />
<context:component-scan base-package="mypackage" />
<bean id="myReader" class="mypackage.MyReader"
<property name="provider" ref="provider" />
</bean>
<bean id="myWriter" class="mypackage.MyWriter" />
<bean id="myProcessor" class="mypackage.MyProcessor" />
<bean id="mReader"
class="org.springframework.batch.item.adapter.ItemReaderAdapter">
<property name="targetObject" ref="myReader" />
<property name="targetMethod" value="read" />
</bean>
<bean id="mProcessor"
class="org.springframework.batch.item.adapter.ItemProcessorAdapter">
<property name="targetObject" ref="myProcessor" />
<property name="targetMethod" value="process" />
</bean>
<bean id="mWriter"
class="org.springframework.batch.item.adapter.ItemWriterAdapter">
<property name="targetObject" ref="myWriter" />
<property name="targetMethod" value="write" />
</bean>
<batch:job id="myJob">
<batch:step id="step01">
<batch:tasklet>
<batch:chunk reader="mReader" writer="mWriter"
processor="mProcessor" commit-interval="1">
</batch:chunk>
</batch:tasklet>
</batch:step>
</batch:job>
<bean id="myRunScheduler" class="mypackage.MyJobLauncher" />
<task:scheduled-tasks>
<task:scheduled ref="myJobLauncher" method="run"
cron="0 0/5 * * * ?" />
<!-- this will maker the job runs every 5 minutes -->
</task:scheduled-tasks>
7.Finally Configure A launcher to launch your job :
public class MyJobLauncher {
#Autowired
private JobLauncher jobLauncher;
#Autowired
#Qualifier("myJob")
private Job job;
public void run() {
try {
String dateParam = new Date().toString();
JobParameters param = new JobParametersBuilder().addString("date",
dateParam).toJobParameters();
JobExecution execution = jobLauncher.run(job, param);
execution.stop();
} catch (Exception e) {
e.printStackTrace();
}
}