We have an OMS component using camel listen and poll MQ. To enhance the performance, I increased the thread listening to the queue, from
from(""GShareMQ:queue:Q.US_OE.IN.TRANSACTIONSCHEDULE?acknowledgementModeName=CLIENT_ACKNOWLEDGE") // keep the JMS message for Client acknowledge on success .beanRef("jmsMessageUtil", "addMessageToHeader") .convertBodyTo(String.class) .log(LoggingLevel.INFO, "Processing SSB Execution:\n${in.body}") .beanRef("transactionScheduleParser", "parseTransactionScheduleMessage")
to
from(""GShareMQ:queue:Q.US_OE.IN.TRANSACTIONSCHEDULE?acknowledgementModeName=CLIENT_ACKNOWLEDGE&concurrentConsumers=10") // keep the JMS message for Client acknowledge on success .beanRef("jmsMessageUtil", "addMessageToHeader") .convertBodyTo(String.class) .log(LoggingLevel.INFO, "Processing SSB Execution:\n${in.body}") .beanRef("transactionScheduleParser", "parseTransactionScheduleMessage")
then on off, out of few thousand messages, 20 got some exception as
java.lang.NumberFormatException: multiple points at sun.misc.FloatingDecimal.readJavaFormatString(FloatingDecimal.java:1084) at java.lang.Double.parseDouble(Double.java:510) at java.text.DigitList.getDouble(DigitList.java:151) at java.text.DecimalFormat.parse(DecimalFormat.java:1303) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1936) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312) at java.text.DateFormat.parse(DateFormat.java:335) at com.bfm.cpm.parser.TransactionScheduleParser.parseTransactionScheduleMessage(TransactionScheduleParser.java:145)
or
java.lang.NumberFormatException: For input string: "" at java.lang.NumberFormatException.forInputString(NumberFormatException.java:48) at java.lang.Long.parseLong(Long.java:431) at java.lang.Long.parseLong(Long.java:468) at java.text.DigitList.getLong(DigitList.java:177) at java.text.DecimalFormat.parse(DecimalFormat.java:1298) at java.text.SimpleDateFormat.subParse(SimpleDateFormat.java:1591) at java.text.SimpleDateFormat.parse(SimpleDateFormat.java:1312) at java.text.DateFormat.parse(DateFormat.java:335) at com.bfm.cpm.parser.TransactionScheduleParser.parseTransactionScheduleMessage(TransactionScheduleParser.java:137)
line 137 and 145 are
Date prvDt = prvDateFmt.parse(prvDateStr); Date contrSettlementdate = prvDateFmt.parse(contrSettlementdateStr);
and prvDateFmt is instance variable, which is not thread safe.
http://stackoverflow.com/questions/6840803/simpledateformat-thread-safety
private SimpleDateFormat prvDateFmt = new SimpleDateFormat("yyyy-MM-dd");
When cpm parsing the message, it uses transactionScheduleParserBean as a singleton (Spring), which uses one same instance of SimpleDateFormat, and it is not threadSafe.
Date prvDt = prvDateFmt.parse(prvDateStr);
Date contrSettlementdate = prvDateFmt.parse(contrSettlementdateStr);
============================================updated on Aug 15, 2012
Solution:
I am to using the ThreadLocal class, which would provide one instance per thread.Think this should work.
here instead of using instance of SimpleDateFormat, I am to use instance of ThreadLocal as instance variable of the singleton spring bean, TransactionScheduleParser.
private ThreadLocal<SimpleDateFormat> postingDateFmt = new ThreadLocal<SimpleDateFormat>(){ @Override protected SimpleDateFormat initialValue() { // TODO Auto-generated method stub SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-ddHH:mm:ss"); df.setTimeZone(omsTz); return df; } }; private ThreadLocal<SimpleDateFormat> prvDateFmt = new ThreadLocal<SimpleDateFormat>(){ @Override protected SimpleDateFormat initialValue() { // TODO Auto-generated method stub SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd"); df.setTimeZone(omsTz); return df; } };
while within the method, which is per thread level, instead of using the instance variable of type SimpleDateFormat, I am to use instance variable of type ThreadLocal to get one SimpleDateFormat per thread.
Date contrSettlementdate = prvDateFmt.get().parse(contrSettlementdateStr); Date actualSettlementdate = prvDateFmt.get().parse(actualSettlementdateStr);
Addon: How ThreadLocal works:
Basically, each instance of type ThreadLocal would maintain one hashMap of Thread(threadId?) and the instance of ClassType. Invoking prvDateFmt.get() would return the instance of SimpleDateFormat corresponding to this thread.