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.
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.
I have bean class as below -
public class Account {
private String strAccNumber = "";
private List<Account> accountList = new ArrayList<Account>();
// getter setter
....
...
#Override
public String toString() {
// code for PassThroughLineAggregator
String data="";
for (int j=0; j<accountList.size(); j++) {
Account bean = accountList.get(j);
data = data + bean.getStrAccNumber();
if (j<(accountList.size()-1)) {
data = data + "\n";
}
}
return data;
}
}
to write data I want to set accountList in my config file.
I'm using below code to set bean property.
<bean id="flatFileReader" class="org.springframework.batch.item.file.FlatFileItemReader"
scope="step">
<property name="resource" value="classpath:springBatch.csv" />
<property name="strict" value="false"></property>
<property name="linesToSkip" value="1" />
<property name="lineMapper">
<bean class="org.springframework.batch.item.file.mapping.DefaultLineMapper">
<property name="lineTokenizer">
<bean
class="org.springframework.batch.item.file.transform.DelimitedLineTokenizer">
<property name="names" value="ACC#" />
</bean>
</property>
<property name="fieldSetMapper">
<bean
class="com.abc.reader.AccountDetailsFieldSetMapper" />
</property>
</bean>
</property>
</bean>
<bean id="flatFileProcessor"
class="com.abc.processor.AccountItemProcessor"
scope="step"></bean>
<bean id="flatFileWriter"
class="com.abc.FlatFileItemWriterListener"
scope="step">
<property name="resource" value="classpath:springBatch1.csv" />
<property name="lineAggregator">
<bean class="org.springframework.batch.item.file.transform.DelimitedLineAggregator">
<property name="fieldExtractor">
<bean class="org.springframework.batch.item.file.transform.BeanWrapperFieldExtractor">
<property name="names" value="strAccNumber" />
</bean>
</property>
</bean>
</property>
</bean>
AccountItemProcessor -
public class AccountItemProcessor implements ItemProcessor<Account, List<Account>>{
#Override
public List<Account> process(Account accObj) throws Exception {
// logic..
}
After processing single record in the process step I want to write multiple items(list of item) , how can I write multiple item at a time using arraylist. In my case I want to write data from accountlist.
You'll want to implement your own LineAggregator. That is what provides the String to be written to the file. In your case, you'll write one that returns a String that is really multiple lines.
You can read more about the LineAggregator interface in the documentation here: http://docs.spring.io/spring-batch/apidocs/org/springframework/batch/item/file/transform/LineAggregator.html
I am trying to add multiple records into an Oracle 12c database using a simple spring jdbcTemplate call to batchUpdate() method. The code that makes the batchUpdate() call is in my DAO class. However the code that calls the service method is multi-threaded.
This is what my DAO class looks like (there are more like this):
public void batchInsertSessionSignature(final List<SessionSignature> sessionSignatures, DataSource dataSource) {
Assert.notNull(sessionSignatures, "Checking to see if the SessionSignature objects passed in are null");
if (dataSource != null) {
jdbcTemplate.setDataSource(dataSource);
}
if (!sessionSignatures.isEmpty()) {
log.debug("Inserting batch entries into lev_session_signature_temp of size: " + sessionSignatures.size());
int[] res = jdbcTemplate.batchUpdate(insertSessionSig, new BatchPreparedStatementSetter() {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
#Override
public void setValues(PreparedStatement pstmt, int i) throws SQLException {
SessionSignature ss = sessionSignatures.get(i);
String created_date = sdf.format(ss.getCreated_date());
pstmt.setLong(1, ss.getId());
pstmt.setString(2, ss.getHost());
pstmt.setString(3, ss.getSession_id());
pstmt.setString(4, ss.getIp_address());
pstmt.setString(5, ss.getAccountName());
try {
pstmt.setInt(6, sysInfoBean.getSysinfoID(ss.getSysinfo()));
pstmt.setDate(7, new Date(sdf.parse(created_date).getTime()));
} catch (ParseException e) {
e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
}
pstmt.setString(8, ss.getCountry());
pstmt.setInt(9, ss.getAccountID());
pstmt.setString(10, ss.getBusinessUnit());
}
#Override
public int getBatchSize() {
return sessionSignatures.size();
}
});
log.debug(res.length + " entries inserted into lev_session_signature_temp");
}
}
This is the service layer method that makes the call to the DAO classes (the method is thread safe)
#Transactional(propagation= Propagation.REQUIRES_NEW, rollbackFor=Exception.class)
public synchronized void writeTargetDaoEntries(TargetDao targetDao, DataSource dataSource, ArrayList<SessionSignature> tgtSessionSignatures,
ArrayList<Session> tgtSession, ArrayList<SearchGroup> tgtSearchGroup, ArrayList<Search> tgtSearches,
ArrayList<LuceneEvent> tgtEvents, ArrayList<EventAnalysisDaoUtil> eventAnalysisList) throws Exception {
targetDao.batchInsertSessionSignatures(tgtSessionSignatures, dataSource);
targetDao.batchInsertUserSessions(tgtSession, dataSource);
targetDao.batchInsertSearchGroups(tgtSearchGroup, dataSource);
targetDao.batchInsertSearches(tgtSearches, dataSource);
targetDao.batchInsertEvents(tgtEvents, dataSource);
targetDao.batchInsertEventAnalysis(eventAnalysisList, dataSource);
}
My spring configuration looks like this
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd">
<context:annotation-config/>
<context:property-placeholder location="app.properties"/>
<bean id="dataSourceQa" class="oracle.ucp.jdbc.PoolDataSourceFactory"
factory-method="getPoolDataSource" lazy-init="true">
<property name="connectionFactoryClassName"
value="oracle.jdbc.pool.OracleDataSource" />
<property name="user" value="${qaTargetId}" />
<property name="password" value="${qaTargetPswd}" />
<property name="URL" value="${qaTargetURL}" />
<property name="minPoolSize" value="2" />
<property name="maxPoolSize" value="10" />
</bean>
<bean id="dataSourceProd" class="oracle.ucp.jdbc.PoolDataSourceFactory"
factory-method="getPoolDataSource" lazy-init="true">
<property name="connectionFactoryClassName"
value="oracle.jdbc.pool.OracleDataSource" />
<property name="user" value="${prodSourceId}" />
<property name="password" value="${prodSourcePswd}" />
<property name="URL" value="${prodSourceURL}" />
<property name="minPoolSize" value="2" />
<property name="maxPoolSize" value="10" />
</bean>
<!-- JDBC Template Configuration -->
<bean id = "jdbcTemplate" class = "org.springframework.jdbc.core.JdbcTemplate">
<property name = "dataSource" ref = "dataSourceQa"></property>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSourceQa" />
</bean>
<bean id="taskExecutor"
class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5" />
<property name="maxPoolSize" value="10" />
<property name="WaitForTasksToCompleteOnShutdown" value="true" />
</bean>
<!-- DAO Bean Configuration -->
<bean id = "eventDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.EventDao" />
<bean id = "searchDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.SearchDao" />
<bean id = "searchGroupDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.SearchGroupDao" />
<bean id = "userSessionDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.UserSessionDao" />
<bean id = "sessionSignatureDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.SessionSignatureDao" />
<bean id = "eventAnalysisDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.EventAnalysisDao" />
<bean id = "dailySummaryDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.DailySummaryDao" />
<bean id = "keyGeneratorBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.KeyGenerator" />
<bean id = "machineLearningDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.MachineLearningDao" />
<bean id = "sourceDaoBean" abstract = "true" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.SourceDao" />
<bean id = "desktopSourceDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.DesktopSourceDao" />
<bean id = "mobileSourceDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.MobileSourceDao" />
<bean id = "webSourceDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.WebSourceDao" />
<bean id = "tempTableDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.TempTableDao" />
<bean id = "targetDaoBean" class = "com.uptodate.lucene.tool.analysis.transfer.oracleDao.TargetDao">
<constructor-arg ref = "eventDaoBean" />
<constructor-arg ref = "searchDaoBean" />
<constructor-arg ref = "searchGroupDaoBean" />
<constructor-arg ref = "userSessionDaoBean" />
<constructor-arg ref = "sessionSignatureDaoBean" />
<constructor-arg ref = "eventAnalysisDaoBean" />
</bean>
<!-- Parser Bean Configuration -->
<bean id = "userSessionParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.UserSessionParser" scope = "prototype" />
<bean id = "searchParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.SearchParser" scope = "prototype" />
<bean id = "searchGroupParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.SearchGroupParser" scope = "prototype" />
<bean id = "eventParserBean" abstract = "true" class = "com.uptodate.lucene.tool.analysis.transfer.parser.EventParser" scope = "prototype" />
<bean id = "mobileEventParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.MobileEventParser" scope = "prototype" />
<bean id = "webEventParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.parser.WebEventParser" scope = "prototype" />
<bean id = "logParserBean" class = "com.uptodate.lucene.tool.analysis.transfer.LogParser" scope = "prototype">
<constructor-arg ref = "keyGeneratorBean" />
<constructor-arg ref = "searchParserBean" />
<constructor-arg ref = "searchGroupParserBean" />
<constructor-arg ref = "userSessionParserBean" />
</bean>
<!-- Transfer Bean Configuration -->
<bean id = "dailySummaryGeneratorBean" class = "com.uptodate.lucene.tool.analysis.transfer.DailySummaryGenerator" />
<!-- Util Bean Configuration -->
<bean id = "sysInfoBean" class = "com.uptodate.lucene.tool.analysis.vo.SysInfo">
<property name = "dataSource" ref = "dataSourceProd" />
</bean>
I have tried adding support for transactions hoping that would help make sure the entries are committed to the database but that did not help. I can see in the code that all entries sent to the database were written to it but when I check the number of records in the database the same number is not reflected. I'm not sure if this is because of the multithreaded nature of the code that a batch of entires is overwritten somehow? Using 1 thread seems to work fine making me believe its a multithreading issue but I'm not sure where the problem is. Any help on this issue is appreciated.
I am new to Spring LDAP and would like to know the following. Is it possible to "compound" 2 or more searches in a single one, while at the same time using an iterator?
For example, my code right now looks something like this:
List result = ldapTemplate.search(baseDn, encode, searchControls2,
genericContextMapper);
BaseDn points to a certain branch of the LDAP tree, and genericContextMapper has an iterator that I am using throught my code to gradually fetch the results.
But what if I want to pass more than 1 BaseDN, each pointing to completely different LDAP branches, and I expect the iterator to start with the next BaseDN as soon as it is done with the previous one? Can I do this kind of chaining through Spring LDAP?
I know I can achieve this by creating my own custom class that just "sticks together" the results, but I was wondering if there is an easier out of the box solution for this.
Thanks!
You can try this
<bean id="authenticator" class="org.springframework.security.providers.ldap.authenticator.BindAuthenticator">
<constructor-arg ref="contextSource"/>
<property name="userSearch" ref="userSearch" />
</bean>
<bean id="userSearch" class="com.security.OUUserSearch">
<property name="userSearches" ref="userSearches" />
</bean>
<util:list id="userSearches">
<ref bean="userSearch1" />
<ref bean="userSearch2" />
</util:list>
<bean id="userSearch1" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg value="OU=test1" />
<constructor-arg value="(uid={0})" />
<constructor-arg ref="contextSource"/>
<property name="searchSubtree" value="true" />
</bean>
<bean id="userSearch2" class="org.springframework.security.ldap.search.FilterBasedLdapUserSearch">
<constructor-arg value="OU=test2" />
<constructor-arg value="(uid={0})" />
<constructor-arg ref="contextSource"/>
<property name="searchSubtree" value="true" />
And OUUserSearch is
public class OUUserSearch implements LdapUserSearch
{
List<LdapUserSearch> myUserSearches = null;
public DirContextOperations searchForUser(String theUsername)
{
DirContextOperations theResult = null;
for(LdapUserSearch aSearch : myUserSearches)
{
try
{
theResult = aSearch.searchForUser(theUsername);
if (theResult != null)
{
// we are done
break;
}
}
catch (Exception e)
{
String aMessage = e.getLocalizedMessage();
task.debug(aMessage);
}
}
if (theResult == null)
{
throw new UsernameNotFoundException("Unable to authenticate user (" + theUsername + ")");
}
return theResult;
}
public List<LdapUserSearch> getUserSearches() { return myUserSearches;}
public void setUserSearches(List<LdapUserSearch> theSearches) {myUserSearches = theSearches;}
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.