Spring AOP - Multiple Interceptors not working together - java

I want to apply advice to a method helloWorld. I would like to throttle calls exceeding 'x' TPS and retry those calls.
So I have 2 interceptors -
Throttle Interceptor, which throttles call rate exceeds 'x'
Retry Interceptor, which retries if call is throttled.
The issue is that when I combine the 2 Interceptors the Throttling
interceptor does not seem work even if I provide the Rate as 0, that is the helloWorld methos gets executed.
If I only
use the Throttling Interceptor it works fine.
My Spring Configuration is like this.
<bean id="retryPolicy" class="xyz.RetryPolicyFactoryBean">
<property name="backoffCoefficient"><value>2</value></property>
<property name="multiplierMillis"><value>100</value></property>
<property name="maxDelayMillis"><value>2000</value></property>
<property name="maxAttempts"><value>2</value></property>
<property name="expirationDurationMillis"><value>60000</value></property>
<bean id="retryInterceptor" class="xyz.RetryInterceptor">
<constructor-arg index="0"><ref bean="retryPolicy"/></constructor-arg>
</bean>
<bean id="throttlingInterceptor" class="xyz.ThrottlingInterceptor">
<constructor-arg index="0"><value>key</value></constructor-arg>
</bean>
<bean id="helloWorld" class="xyz.helloWorld" />
<bean id="helloWorldProxy" class="org.springframework.aop.framework.ProxyFactoryBean">
<property name="target" ref="helloWorld" />
<property name="interceptorNames">
<list>
<value>retryInterceptor</value>
<value>throttlingInterceptor</value>
</list>
</property>
</bean>
Throttling Interceptor is like this:
#Override
public Object invoke(MethodInvocation methodInvocation) throws Throwable {
if (isDecorated(methodInvocation)) {
if (throttler.isThrottled(key)) {
throw new ThrottlingException("Call throttled.");
}
}
return methodInvocation.proceed();
}
My retry interceptor is like this:
public class RetryInterceptor extends RetryAdvice {
public RetryInterceptor(RetryPolicy retryPolicy) {
super(retryPolicy);
}
#Override
public Object invoke(final MethodInvocation methodInvocation) throws Throwable {
return super.invoke(methodInvocation);
}
What am I missing here?

I followed the newer approach using AspectJ Annotations & got it working (atleast now the interceptors are being called).
Annotate the Class which contains the advice with #Aspect
Here is my Throttle Advice:
#Before(pointcut="execution(* com.company.xyz.method())")
public void invoke() throws ThrottlingException {
if (throttler.isThrottled(throttleKey)) {
throw new ThrottlingException("Call Throttled");
}
}
My retry Interceptor:
#AfterThrowing(pointcut="execution(* com.company.xyz.method())", throwing="exception")
public Object invoke(JoinPoint jp, ThrottlingException exception) throws Throwable {
return RetryingCallable.newRetryingCallable(new Callable<Object>() {
#Override
public Object call() throws Exception {
MethodSignature signature = (MethodSignature) p.getSignature();
Method method = signature.getMethod();
return method.invoke(jp.getThis(), (Object[]) null);
}
}, retryPolicy).call();
}
Relevant Spring Config
<bean id="retryInterceptor" class="com.company.xyz.RetryInterceptor">
<constructor-arg index="0"><ref bean="retryPolicy"/></constructor-arg>
</bean>
<bean id="throttlingInterceptor" class="com.company.xyz.ThrottlingInterceptor">
<constructor-arg><value>throttleKey</value></constructor-arg>
</bean>
<aop:aspectj-autoproxy/>
Another point to take care of is the Order in which the Interceptors are called. Use #Order Annotation to ensure the order.

Related

Error while saving the Message object to a local db in the onMessage method of a class which implements SessionAwareMessageListener

What I want to achieve is that whenever a message arrives from the ActiveMQ queue to the onMessage method of the class which implements SessionAwareListener I want to save it to the database.
This is how my listener class looks like
#Service
public class NotificationEventListener implements SessionAwareMessageListener {
#Autowired
private NotificationGeneratorWritePlatformService notificationGeneratorWritePlatformService;
#Override
public void onMessage(Message message, Session session) throws JMSException {
if (message instanceof ObjectMessage) {
Notification notification = (Notification) ((ObjectMessage) message).getObject();
notificationGeneratorWritePlatformService.notify(
notification.getUserId(),
notification.getObjectType(),
notification.getObjectId(),
notification.getAction(),
notification.getActor(),
notification.getNotificationContent(),
notification.isSystemGenerated()
);
System.out.println("Received: " + notification.toString());
}
}
}
The sender broadcasts the object of type Notification
The notify method which is being called in the onMessage method is used to save the object of type Notification to the database.
Theoretically, it should work and save the object to the database but the error logs out that the table is not present in the database.
I have called the notify method(created by me to save the object to the database) on a random Rest request and it works perfectly there and saves the object to the database but when I use that notify method in the listener class it does not save the object and logs out that table is not present in the database.
This is my notificationContext.xml for setting up the sender and listener class.
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="amqConnectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory">
<constructor-arg index="0" value="tcp://localhost:61616" />
</bean>
<bean id="connectionFactory" class="org.springframework.jms.connection.CachingConnectionFactory">
<constructor-arg ref="amqConnectionFactory"/>
</bean>
<bean id="jmsTemplate" class="org.springframework.jms.core.JmsTemplate">
<property name="connectionFactory" ref="connectionFactory"/>
</bean>
<bean id="messageListenerContainer" class="org.springframework.jms.listener.DefaultMessageListenerContainer">
<property name="connectionFactory" ref="connectionFactory" />
<property name="destinationName" value="NotificationQueue"/>
<property name="messageListener" ref="notificationEventListener" />
</bean>
</beans>
Sender class
#Service
public class NotificationEvent {
#Autowired
private JmsTemplate jmsTemplate;
public void broadcastNotification(final Destination destination, final Notification notification) {
this.jmsTemplate.send(destination, new MessageCreator() {
#Override
public Message createMessage(Session session) throws JMSException {
return session.createObjectMessage(notification);
}
});
}
}
What could be the reason of table not being able to discovered in the listener class ?

Spring Transactional Rollback does not work

I have following Thread and transactional method, I have get thrown an exception to test rollback of insertion of DB but notthing is changed. What am I missing?
public class CleaningThread extends Thread {
public void run() {
try {
doJob();
} catch (Exception e) {
e.printStackTrace();
}
}
#Transactional(rollbackFor=Exception.class)
private void doJob() throws Exception {
//INSERT OPERATION
final BatchSqlUpdate bs = new BatchSqlUpdate
bs.flush()
throw new Exception("Custom exception")
//UPDATE
}
}
Application Context:
<tx:annotation-driven transaction-manager="txManager" proxy-target-class="true"/>
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>file:conf/offclear.properties</value>
</list>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
</bean>
<bean id="txManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<bean id="cleaningThread" class="CleaningThread" scope="prototype"/>
Using Spring 3.1
You're invoking method doJob() from method run() of the same class. That's why you're working with real method, not proxied one.
Actually, this question was covered in this topic: One Service method invoke inner multiple method for Spring transaction

MyIbatis-Spring Transaction closeSession

When I use MyIbatis with spring transaction, using the Annotation driven declarative method, do I have do bother manage the session.
Without the transaction I usually do like this:
public int insertPnt(Movimenti value) {
try (SqlSession session = sqlSessionFactory.openSession()) {
for (Movimenti value : values) {
session.insert(
"com.sirio.cisl.dal.MovimentiMapper.insertSelective",
value);
}
} catch (Exception e) {
log.error("Error inserting movimenti "+user+" anno "+anno+" "+ e.getMessage());
throw e;
}
}
but from MyIbatis-Spring documentation I read
MyBatis SqlSession provides you with specific methods to handle
transactions programmatically. ...........
. That means that Spring will always handle your
transactions.
You cannot call SqlSession.commit(), SqlSession.rollback() or
SqlSession.close() over a Spring managed SqlSession.
So I wandering if I'm doing correct when I just adding the #Transaction annotation (and the <tx:annotation-driven> config) to the method.
#Transactional(rollbackFor=Exception.class)
public int insertPnt(Movimenti value) {
try (SqlSession session = sqlSessionFactory.openSession()) {
..............
}
Does the transaction manager take care of the session resourse? Or I have to remove the try-catch clause.
Thk
I post the same question to the MyIbatis-user group see. The response was useful:
To use MyBatis you need either an SqlSesion or a Mapper interface.
With "classic" MyBatis you get the sessions out of an
SqlSessionFactory but when using Spring this changes.
So I follow the example of the documentation:
I inject the Service class with a sqlSessionTemplate that internally takes care of opening and close the session.
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean" >
<property name="dataSource" ref="dataSource" />
<property name="configLocation" value="classpath:myibatis.xml"/>
<property name="mapperLocations" value="classpath*:mappers/*.xml" />
</bean>
<bean id="asTableService" class="com.sirio.cisl.dal.AsTableService">
<property name="session" ref="sqlSession" />
</bean>
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate" destroy-method="clearCache">
<constructor-arg index="0" ref="sqlSessionFactory" />
<constructor-arg index="1" value="BATCH" />
</bean>
Then I can use the #Transactional annotation :
#Transactional(rollbackFor=Exception.class)
public int insertPnt(Movimenti value) {
session.create(......)
}
Davide

Spring 3's Quartz 2 implementation doesn't support requestsRecovery anymore

According to the Quartz 2's documentation
RequestsRecovery - if a job "requests recovery", and it is executing
during the time of a 'hard shutdown' of the scheduler (i.e. the
process it is running within crashes, or the machine is shut off),
then it is re-executed when the scheduler is started again. In this
case, the JobExecutionContext.isRecovering() method will return true.
Spring3 has support for both Quartz 1.x and Quartz 2.x. If we use Quartz 1.x then we need to use following configuration for creating JobDetail bean:
<bean id="ldapSynch" class="org.springframework.scheduling.quartz.JobDetailBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
Internally org.springframework.scheduling.quartz.JobDetailBean extends org.quartz.JobDetail and in Quartz 1.x org.quartz.JobDetail has a setter public void setRequestsRecovery(boolean shouldRecover).
But if we use Quartz 2.x implementation of Spring3 then the aforesaid configuration changes as:
<bean id="ldapSynch" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
And also in Quartz 2.x there is no such org.quartz.JobDetail class, instead it is an interface and eventually org.springframework.scheduling.quartz.JobDetailFactoryBean doesn't takes the parameter <property name="requestsRecovery" value="true" /> anymore.
How could we pass this important parameter to the Quartz's scheduler?
After looking into the source code of the class org.springframework.scheduling.quartz.JobDetailFactoryBean and source code of Quartz 2.0 I have found that there is a kind of init method in org.springframework.scheduling.quartz.JobDetailFactoryBean which is public void afterPropertiesSet(); within this method the instance of org.quartz.JobDetail is being created. Fortunately this instance of org.quartz.JobDetail can be accessible via a method public JobDetail getObject() of the class org.springframework.scheduling.quartz.JobDetailFactoryBean.
In Quartz 2.0 the class org.quartz.impl.JobDeialImpl implements the interface org.quartz.JobDetail; so the instance of org.quartz.JobDetail in org.springframework.scheduling.quartz.JobDetailFactoryBean is actually the instance of org.quartz.impl.JobDeialImpl.
So I created a class com.edfx.adb.scheduling.ADBJobDetailFactoryBean which extends org.springframework.scheduling.quartz.JobDetailFactoryBean and overrides the afterPropertiesSet() method as:
package com.edfx.adb.scheduling;
import org.quartz.impl.JobDetailImpl;
import org.springframework.scheduling.quartz.JobDetailFactoryBean;
public class ADBJobDetailFactoryBean extends JobDetailFactoryBean {
private boolean requestsRecovery;
public ADBJobDetailFactoryBean() {
super();
}
#Override
public void afterPropertiesSet() {
super.afterPropertiesSet();
JobDetailImpl jobDetail = (JobDetailImpl) getObject();
jobDetail.setRequestsRecovery(isRequestsRecovery());
}
public boolean isRequestsRecovery() {
return requestsRecovery;
}
public void setRequestsRecovery(boolean requestsRecovery) {
this.requestsRecovery = requestsRecovery;
}
}
And changed the spring bean configuration as:
<bean id="ldapSynch" class="com.edfx.adb.scheduling.ADBJobDetailFactoryBean">
<property name="jobClass" value="com.edfx.adb.scheduling.job.LDAPSynchronizer" />
<property name="requestsRecovery" value="true" />
</bean>
And voila. I have tested this by stopping running server while the scheduler was executing the task and when I restarted the server the scheduler was started executing the unfinished job.
Hope it will help someone.
#tapas-bose while searching for some quartz info I come across your post. It looks like Spring 3.2 support setting the property requestsRecovery. I don't know which version of Spring 3.? you have mentioned in your answer with which you faced the problem.
I set requestsRecovery property directly through setter injection and it worked for me.
<bean name="continousMonitoringProfileNotificationPushProcessing" class="org.springframework.scheduling.quartz.JobDetailFactoryBean">
<property name="jobClass" value="com.mycompany.projectApp.jobs.ContinousMonitoringNotificationPushJobBean" />
<property name="jobDataAsMap">
<map>
<entry key="executionSuccessful" value="true"/>
</map>
</property>
<property name="durability" value="true" />
<property name="requestsRecovery" value="true"/>
</bean>
When I decompiled code for JobDetailFactoryBean it is overriding afterPropertiesSet method which creates JObDetailImpl instance and set requestsRecovery, (piece of decompiled code as below)
public class JobDetailFactoryBean implements FactoryBean<JobDetail>, BeanNameAware, ApplicationContextAware, InitializingBean
{
private String name;
private String group;
private Class jobClass;
private JobDataMap jobDataMap = new JobDataMap();
private boolean durability = false;
private boolean requestsRecovery = false;
...
..
..
public void afterPropertiesSet()
{
if (this.name == null) {
this.name = this.beanName;
}
if (this.group == null) {
this.group = "DEFAULT";
}
if (this.applicationContextJobDataKey != null) {
if (this.applicationContext == null) {
throw new IllegalStateException("JobDetailBean needs to be set up in ApplicationContext to be able to handle an 'applicationContextJobDataKey'");
}
getJobDataMap().put(this.applicationContextJobDataKey, this.applicationContext);
}
Class<?> jobDetailClass;
try
{
jobDetailClass = getClass().getClassLoader().loadClass("org.quartz.impl.JobDetailImpl");
}
catch (ClassNotFoundException ex) {
jobDetailClass = JobDetail.class;
}
BeanWrapper bw = new BeanWrapperImpl(jobDetailClass);
MutablePropertyValues pvs = new MutablePropertyValues();
pvs.add("name", this.name);
pvs.add("group", this.group);
pvs.add("jobClass", this.jobClass);
pvs.add("jobDataMap", this.jobDataMap);
pvs.add("durability", Boolean.valueOf(this.durability));
pvs.add("requestsRecovery", Boolean.valueOf(this.requestsRecovery));
So we don't need to manually extend the JobFactoryBean class and set the requestsRecovery property.

Spring Transaction management

In java Spring, I am facing an issue regarding transaction rollback.
Example:
I have 3 DAOs in my code (A, B, C). All of them extend JDBCTemplate:
#Transaction(propagation=new_required)
public void serviceClassProc() throws Exception {
A.db1();
B.db2();
C.db3();
}
Now with the above code if I throw an exception in B.db2(), nothing gets rolled back.
Now if I modify B.db2 as following:
#Transaction(propagation=nested,rollbackon=Exception.class)
public void db2() throws Exception{
...
throw new Exception();
}
And then call serviceClassProc(), only the transaction in B.db2, gets rolled back.
I want an implementation where all transactions inside serviceClassProc() get rolled back.
Here are the 2 configurations I am using:
<bean id="bonddao" class="com.marki.bonds.staticdata.dao.MuniStaticDataDaoImpl"> <property name="dataSource" ref="c3p0DataSource" /> </bean> <bean id="dcldao" class="com.bonds.staticdata.dao.DclSettingsDaoImpl"> <constructor-arg ref="c3p0DataSource" /> </bean> <bean id="batchlogdao" class="com.bonds.staticdata.dao.MuniFeedHandlerBatchLogDaoImpl"> <constructor-arg ref="c3p0DataSource" /> </bean>
<bean id="bondsApplication" class="com.markit.bonds.staticdata.service.MuniRefDataSyncApp"> <property name="refdataService" ref="refDataSynchService" /> <property name="mailService" ref="mailSender"></property> <property name="batchLogger" ref="batchlogdao"></property> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="c3p0DataSource" /> </bean> <tx:annotation-driven transaction-manager="transactionManager" />
Where I am going wrong? Is it wrong to have 3 DAOs all extending JDBC template? Should all of them share same JDBCTemplate?
You should add rollbackon=Exception.class to the annotation of your service method and remove the transaction annotation entirely from the DAO methods. It is a bad idea to have transaction control at DAO level.
you can use also the : **org.springframework.transaction.interceptor.TransactionAspectSupport;**
here is an example that you can handle it:
#Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class, transactionManager = "transactionManager")
public void messageHandler() {
try {
//TODO your own code
} catch (Exception ex) {
TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
} finally {
}
}

Categories

Resources