spring JMS error handler is not Invoking - spring

I have spring JMS message listener container setup as follows.
<beans:bean id="messageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<beans:property name="connectionFactory" ref="amqConnectionFactory" />
<beans:property name="destination" ref="request.Queue" />
<beans:property name="messageListener" ref="messageListener" />
<beans:property name="sessionTransacted" value="true" />
<beans:property name="errorHandler" ref="inboundErrorHandler"/>
</beans:bean>
<beans:bean id="inboundErrorHandler" class="com.test.exception.MDPErrorHandler" />
Listener Code is as follows..
#Component
public class MyMessageListener implements MessageListener {
private static final Log LOG = LogFactory.getLogger(MyMessageListener.class);
#Autowired
private MessageHandler handler;
/**
* #see MessageListener#onMessage(Message)
*/
#Override
public void onMessage(Message inMessage) {
try
{
if (inMessage instanceof TextMessage) {
TextMessage msg = (TextMessage) inMessage;
handler.processRequest(msg.getText());
} else {
LOG.error("Message of wrong type: " + inMessage.getClass().getName());
}
} catch (Exception cause) {
LOG.error("Error while receiving message in MlaMessageListener.onMesage() ",cause);
}
}
}
my error handler is as follows..
public class MDPErrorHandler implements ErrorHandler {
/**
* #see org.springframework.util.ErrorHandler#handleError(java.lang.Throwable)
*/
#Override
public void handleError(Throwable t) {
LOG.warn("Inside MDPErrorHandler.handleError()*** ");
t.printStackTrace();
}
}
Why my error handler is not invoking automatically in case of any exception/error occurs in messageListener.onMessage()?
Do I need to invoke it manually?

You don't need to configure the container like that.
Simply add an error-channel to the inbound adapter; when an exception occurs on the main flow (incomingUpdateChannel->) an ErrorMessage will be sent to the error-channel. The payload of that message will be a MessagingException and you can extract the failed message to attempt to send to your failures queue. Just add a transformer (expresssion="payload.failedMessage") followed by a jms outbound adapter. If the send is successful the original transaction will commit. If the error-channel flow throws an exception, the original message will be rolled back.

Related

Spring: using of FlatFileItemReader

Could I use FlatFileItemReader not as reader of batch processing? I want to use it for parsing of File.
I have bean of reader:
<bean id="cvsFileItemReader" class="org.springframework.batch.item.file.FlatFileItemReader">
<property name="resource" value="file:${garmin.fs.in.received2}" />
<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="id,sales,qty,staffName,date" />
</bean>
</property>
<property name="fieldSetMapper">
<bean class="my.app.util.ReportFieldSetMapper" />
</property>
</bean>
</property>
</bean>
And I want to use it in my component:
#Component
public class Handler {
#Autowired
private FlatFileItemReader<Report> reader;
public File handleFile() {
try {
Report report = reader.read();
} catch (Exception e) {
e.printStackTrace();
}
return input;
}
}
But in line of code:
Report report = reader.read();
I got exception:
org.springframework.batch.item.ReaderNotOpenException: Reader must
be open before it can be read.
Is it possible to use spring batch reader in the following way?
As the exception implies, you have to call reader.open() before calling read() for the first time.
You should also call reader.close() when you are done.
As #GaryRussell wrote you can call open/close but - IMHO - use a SB component outside of SB domain is not a good approach because you couple your classes with SB classes too tight.
For me a better and more clear mode to operate is to use a CSV parsing library as BeanIO as main component and reuse it in your Handler as well as with BeanIOFlatFileItemReader as reader.
You have to implement ItemStream and override open, close and update method as below-
#Component
public class Handler implements ItemStream {
#Autowired
private FlatFileItemReader<Report> reader;
public File handleFile() {
try {
Report report = reader.read();
} catch (Exception e) {
e.printStackTrace();
}
return input;
}
#Override
public void open(ExecutionContext executionContext) {
reader.open(executionContext);
}
#Override
public void update(ExecutionContext executionContext) {
reader.update(executionContext);
}
#Override
public void close() {
reader.close();
}
}

Apache Camel: set an object dependency before operation with Spring

In this example, I am trying to set the object dependency before calling businessLogic. I am receiving a nullpointer because that 'consumer' object is not set.
Here is the basis of the example and mostly trying to use the Spring DSL.
http://camel.apache.org/polling-consumer
Section: Timer based polling consumer
Here is my camel/spring config:
<bean id="simpleOutboxMessageConsumer" class="org.berlin.camel.esb.logs.mq.SimplePrintMessageConsumer"/>
<!-- Continue with spring dsl for ESB -->
<camelContext id="myCamel" xmlns="http://camel.apache.org/schema/spring">
<!-- Define a MQ consumer template -->
<consumerTemplate id="consumer" />
....
</camelContext>
<route id="fromOutboxAndConsume">
<from uri="timer://foo?period=30000" />
<to uri="bean:simpleOutboxMessageConsumer?method=businessLogic" />
</route>
Java code
#Component
public class SimplePrintMessageConsumer {
private static final Logger logger = Logger.getLogger(SimplePrintMessageConsumer.class);
private int count;
#Autowired
private ConsumerTemplate consumer;
public void setConsumer(final ConsumerTemplate consumer) {
this.consumer = consumer;
}
public void businessLogic() {
logger.info("Launching business logic to consume outbox, blocking until we get a message >>>");
while (true) {
// Consume the message
final String msg = consumer.receiveBody("activemq:queue.outbox", 3000, String.class);
logger.info("Printing message found from queue: " + msg);
if (msg == null) {
// no more messages in queue
break;
}
}
}
}
There is a nullpointer at the usage of the consume object. I am thinking that spring is not just autowiring that bean properly. Even if I didn't use spring, how would I pass the consumer template object to this bean?
This should work
<bean id="simpleOutboxMessageConsumer" class="....SimplePrintMessageConsumer">
<property name="consumer" ref="consumer"/>
</bean>
Remove the #AutoWire , I am checking on why the #Autowire is not working by the way

How to set the Transaction isolationlevel using Spring with MyBatis

I like to set the Isolationlevel by my self, using the transactionmanager from the Spring Framework combined with myBatis. I was trying a lot of tutorial, but nothing worked.
My application is build as MVC Pattern, that means i have views, models, interfaces used for the dependency-injection from mybatis and a controller class.
I hope someone can give me advice i am new in mybatis and spring. The whole application is running very well but I like to take over controll over the isolationlevels.
This is the spring-configuration.xml file
<!--<mybatis-spring:scan base-package="de.hrw.model.**"/> -->
<mybatis-spring:scan base-package="de.hrw.*" />
<context:component-scan base-package="de.hrw.*" />
<bean id="dataSource" class="com.zaxxer.hikari.HikariDataSource">
<property name="jdbcUrl" value="jdbc:mysql://127.0.0.1:3306/carrental">
</property>
<property name="username" value="root"></property>
<property name="password" value="root"></property>
<property name="driverClassName" value="com.mysql.jdbc.Driver"></property>
<property name="autoCommit" value="false"></property>
<property name="registerMbeans" value="true"></property>
<property name="transactionIsolation"
value="TRANSACTION_SERIALIZABLE">
</property>
</bean>
<bean id="sqlSessionFactoryBean"
class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="dataSource"></property>
<property name="configLocation" value="classpath:mybatis-config.xml">
</property>
</bean>
<bean id="carController" class="de.hrw.controller.CarController">
<property name="transactionManager" ref="transactionManager" />
</bean>
<bean id="carSearchView" class="de.hrw.view.CarSearchView">
</bean>
<bean id="transactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource" />
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
I am using the dependecy-injection of mybatis to get data from and to the database
example of an iterface
package de.hrw.mgmtDAO;
import java.util.List;
import de.hrw.model.CarModel;
public interface ICarMgmt {
public CarModel selectCarById(final int carId);
public List<CarModel> selectAllCars();
}
this is the main-class where i include a view (frame)
public class Carrental_main {
public static void main(String[] args) {
// TODO Auto-generated method stub
ApplicationContext context =
new ClassPathXmlApplicationContext("spring-config.xml");
CarController carController = (CarController) context.getBean("carController");
carController.openSearchView();
carController.getCarSearchView().setVisible(true);
}
}
this is the controller. Here i try to set the isolation level to SERIALIZABLE but it is always set to default (-1)
#Transactional(propagation=Propagation.REQUIRES_NEW , isolation = Isolation.SERIALIZABLE)
public class CarController {
#Autowired
private ICarMgmt carMgmt;
private CarSearchView carSearchView;
private ApplicationContext applicationContext;
#Autowired
private PlatformTransactionManager transactionManager;
private TransactionStatus transactionStatus;
private TransactionDefinition defaultTransactionDefinition;
private DataSource dataSource;
public void openSearchView() {
this.setApplicationContext();
this.setDefaultTransactionDefinition();
this.setTransactionStatus();
this.carSearchView = (CarSearchView) applicationContext
.getBean("carSearchView");
try {
List<CarModel> carList = carMgmt.selectAllCars();
// this.carSearchView.setResultList(carList);
this.carSearchView.setLabelList(carList);
this.carSearchView.createTextFieldList();
this.carSearchView.createLabelFieldList();
transactionManager.commit(transactionStatus);
} catch (DataAccessException e) {
System.out.println("Error in creating record, rolling back");
transactionManager.rollback(transactionStatus);
throw e;
}
}
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
public void setDefaultTransactionDefinition() {
this.defaultTransactionDefinition = new DefaultTransactionDefinition();
}
public void setApplicationContext() {
applicationContext = new ClassPathXmlApplicationContext(
"spring-config.xml");
}
public void setTransactionManager(
PlatformTransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
public void setTransactionStatus() {
this.transactionStatus = transactionManager.getTransaction(defaultTransactionDefinition);
}
I've finally found a solution. I changed the TransactionDefinition object in the controller to DefaultTransactionDefinition object
private DefaultTransactionDefinition defaultTransactionDefinition;
former it was
private TransactionDefinition defaultTransactionDefinition;
but the TransactionDefinition doesn't provide any setting methods. I was wondering, because in the documentation I found such methods to set the isolationlevel, but this methods are just provided by the DefaultTransactionDefinition. After I've found this failure i added the the following to lines of codes and it finally works
defaultTransactionDefinition.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRED);
defaultTransactionDefinition.setIsolationLevel(DefaultTransactionDefinition.ISOLATION_REPEATABLE_READ);
Thx, for all your advises. If someone knows a really good tutorial for MyBatis + Spring and the transaction manager please post a link :D
You can apply transaction as shown below in mapper interface(though it is recommended to apply transaction annotation for class, however in mybatis, transaction defined in interface will be applied to proxy class)
import java.util.List;
import de.hrw.model.CarModel;
#Transactional
public interface ICarMgmt {
#Transactional(isolation = Isolation.SERIALIZABLE)
public CarModel selectCarById(final int carId);
public List<CarModel> selectAllCars();
}

Exception handling for Spring 3.2 "#Scheduled" annotation

How to customize the exception handling for #Scheduled annotation from spring ?
I have Cron jobs which will be triggered in the server (Tomcat 6) and when any exceptions occur I need to do some handling.
Spring version 3.2
Tomcat Server 6
If you want to use Java Config you will need to create configuration implementing SchedulingConfigurer
#EnableScheduling
#Configuration
class SchedulingConfiguration implements SchedulingConfigurer {
private final Logger logger = LoggerFactory.getLogger(getClass());
private final ThreadPoolTaskScheduler taskScheduler;
SchedulingConfiguration() {
taskScheduler = new ThreadPoolTaskScheduler();
taskScheduler.setErrorHandler(t -> logger.error("Exception in #Scheduled task. ", t));
taskScheduler.setThreadNamePrefix("#scheduled-");
taskScheduler.initialize();
}
#Override
public void configureTasks(ScheduledTaskRegistrar taskRegistrar) {
taskRegistrar.setScheduler(taskScheduler);
}
}
You can modify error handler for your needs. Here I only log a message.
Don't forget to call taskScheduler.initialize();. Without it you'll get:
java.lang.IllegalStateException: ThreadPoolTaskScheduler not initialized
You could implement and register an ErrorHandler for the ThreadPoolTaskScheduler that is used for your scheduling annotations.
<task:annotation-driven scheduler="yourThreadPoolTaskScheduler" />
<bean id="yourThreadPoolTaskScheduler" class="org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler">
<property name="poolSize" value="5" />
<property name="errorHandler" ref="yourScheduledTaskErrorHandler" />
</bean>
<bean id="yourScheduledTaskErrorHandler"
class="com.example.YourScheduledTaskErrorHandler"/>
Why not wrap your business logic and do a simple try catch in your #schedule method. Then you can log or take whatever action is necessary for failure cases.
#Scheduled(cron = "${schedulerRate}")
public void scheduledJob() {
try {
businessLogicService.doBusinessLogic();
} catch (Exception e) {
log.error(e);
}
}

JMS Unable to consume messages from Oracle queue using spring/jms

I have followed the spring documentation and setup a Spring JMS listener. Yet, even if I add a message to the queue, my code is not detecting this. My spring config is as follows:
<bean id="dataSourceListener" class="oracle.jdbc.pool.OracleDataSource">
<property name="URL" value="xxx"/>
<property name="user" value="xxx"/>
<property name="password" value="xxx"/>
</bean>
<bean id="jmsConnectionFactory" class="OracleAqFactoryBean">
<property name="dataSource" ref="dataSourceListener" />
</bean>
<jms:listener-container connection-factory="jmsConnectionFactory" acknowledge="transacted" concurrency="1-5">
<jms:listener destination="queuename" ref="myMessageListener"/>
</jms:listener-container>
<bean id="myMessageListener" class="Listener"/>
My Java is as follows:
My custom listener:
class Listener implements MessageListener {
#Override
void onMessage(Message message) {
// code to handle message is here
}
}
And my OracleAqFactoryBean:
public class OracleAqFactoryBean implements FactoryBean {
private DataSource dataSource;
public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}
#Override
public Object getObject() throws Exception {
return AQjmsFactory.getConnectionFactory( dataSource );
}
#Override
public Class<?> getObjectType() {
return ConnectionFactory.class;
}
#Override
public boolean isSingleton() {
return true;
}
}
[EDIT: THE ABOVE SETUP IS NOW WORKING SUCCESSFULLY]
I do not understand why you are wiring up a FactoryBean implementation to the Spring DMLC destination property. This is clearly not correct because the setDestinationmethod only accepts a javax.jms.Destination type. You've wired up the connectionFactory and the messageListener. That's all that's needed to begin consuming messages. If you remove the testmq ref that you have wired to the destination property, then messages should be successfully consumed.

Resources