Transaction doesn't work in aspectj - aspectj

I have the aspect(see below) which should log actions(create, update, delete) in db. Depends on action logging happens in a preProcess or postProcess method. I shouldn't log anything if some fail happens through these actions. I.e. if create didn't happened, then there is no need to logging it.
I tried to tested it. I throw RunTimeException in the join point and expect that there is no new log in db. Unfortunately, new log is saved in spite of exception in the join point.
Aspect:
#Component
#Aspect
public class LoggingAspect {
#Autowired
private ApplicationContext appContext;
#Autowired
private LoggingService loggingService;
#Around("#annotation(Loggable)")
#Transactional
public void saveActionMessage(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature ms = (MethodSignature) joinPoint.getSignature();
Loggable m = ms.getMethod().getAnnotation(Loggable.class);
LoggingStrategy strategy = appContext.getBean(m.strategy());
Object argument = joinPoint.getArgs()[0];
strategy.preProcess(argument);
joinPoint.proceed();
strategy.postProcess(argument);
}
}
TestApplicationConfig:
<context:spring-configured/>
<import resource="applicationConfig-common.xml"/>
<import resource="applicationConfig-security.xml"/>
<aop:aspectj-autoproxy/>
<util:map id="testValues">
<entry key="com.exadel.mbox.test.testSvnFile" value="${svnFolder.configPath}${svnRoot.file[0].fileName}"/>
<entry key="com.exadel.mbox.test.testCommonRepositoryPath" value="${svnRoot.commonRepositoryPath}"/>
<entry key="com.exadel.mbox.test.testMailFile" value="${mailingList.configPath}"/>
</util:map>
<context:component-scan base-package="com.exadel.report.common" />
<!-- Jpa Repositories -->
<jpa:repositories base-package="com.exadel.report.common.dao" />
<tx:annotation-driven proxy-target-class="true"
transaction-manager="txManager" mode="aspectj"/>
<bean id="txManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- Data Source -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="org.hsqldb.jdbcDriver" />
<property name="url" value="jdbc:hsqldb:mem:testdb" />
<property name="username" value="sa" />
<property name="password" value="" />
</bean>
<!-- Entity Manager -->
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaVendorAdapter">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<property name="showSql" value="true"/>
<property name="generateDdl" value="true"/>
<property name="databasePlatform" value="org.hibernate.dialect.HSQLDialect"/>
</bean>
</property>
<property name="persistenceUnitName" value="exviewer-test"/>
</bean>
<!-- Transaction Manager -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>
[Update]
LoggingStrategy:
public interface LoggingStrategy {
public void preProcess(Object obj);
public void postProcess(Object obj);
}
BaseLoggingStrategy:
public class BaseLoggingStrategy implements LoggingStrategy {
#Override
public void preProcess(Object obj) {}
#Override
public void postProcess(Object obj) {}
}
UpdateProcessStrategy:
#Service
public class UpdateProcessStrategy extends BaseLoggingStrategy {
#Autowired
private LoggingService loggingService;
#Autowired
private UserService userService;
#Autowired
DeviceService deviceService;
private Device currentDevice;
#Override
#Transactional
public void preProcess(Object obj) {
currentDevice = (Device) obj;
Device previousDevice = deviceService.getById(currentDevice.getId());
String deviceDataBeforeUpdate = deviceService.getDeviceDetailsInJSON(previousDevice);
String deviceDataAfterUpdate = deviceService.getDeviceDetailsInJSON(currentDevice);
String login = userService.getCurrentUser().getLogin();
String actionMessage = LoggingMessages.DEVICE_UPDATE.name();
loggingService.save(
new Logging(
login,
actionMessage,
deviceDataBeforeUpdate,
deviceDataAfterUpdate,
new Date())
);
}
#Override
public void postProcess(Object obj) {}
}
Class intercepted by aspcet:
#Service
public class DeviceService {
#Loggable(value = LoggingMessages.DEVICE_CREATE, strategy = CreateProcessStrategy.class)
#Transactional
public void create(Device device) {
createOrUpdate(device);
}
#Loggable(value = LoggingMessages.DEVICE_UPDATE, strategy = UpdateProcessStrategy.class)
#Transactional
public void update(Device device) {
createOrUpdate(device);
}
private void createOrUpdate(Device device) {
deviceRepository.save(device);
}
#Loggable(value = LoggingMessages.DEVICE_REMOVE, strategy = RemoveProcessStrategy.class)
public void remove(Long deviceId) {
deviceRepository.delete(deviceId);
}
}
Loggable annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Loggable {
LoggingMessages value();
Class<? extends LoggingStrategy> strategy();
}
Log for update action contains:
id, created_dtm, action(DEVICE_UPDATE), device_data_before_action_on_the_device(in json format), device_data_after_action_on_the_device(in json format), created_by.

Disclaimer: Actually I am not a Spring expert, maybe someone else can help you out here. My field of expertise it AspectJ, which is how I found your question.
Anyway, you have two issues here:
#Transactional annotation on your aspect's advice LoggingAspect.saveActionMessage(..). Actually I have no idea if this works at all (I found no example using #Transactional on an aspect method/advice on the web, but maybe I searched in the wrong way) because declarative transaction handling in Spring is implemented via proxy-based technology, just like Spring AOP. Read the chapter 12 about transaction management in the Spring manual for further details, especially chapter 12.5.1. I am pretty sure you will find a way to do what you want there.
Nested transactions, because e.g. UpdateProcessStrategy.preProcess(..) is called by the very advice which is meant to be transactional, but is declared #Transactional too. So you have a transaction within a transaction. How Spring handles this, I have no idea, but maybe this tutorial about Spring transaction propagation contains enlightening details.
The Spring manual lists several means to implement transactional behaviour: programmatically, declaratively via annotations, XML-based <tx:advice> stuff and so forth. I don't know which way is the best for you, I merely wanted to provide some general hints.

Related

Spring MVC, Serving RESTFul request (json) and JSP (content negotiation)

I'm trying to create a Spring MVC 4 web project (Maven project) in order to handle REST requests.
The system should answer with a web page in case the HTTP accept is text/html
and in json in case it is application/json (and possibly other formats like XML in case it is application/xml).
I've set up a controller and the JSP (I've also used the #RestController). The problem is that I cannot make it working togheter. If the system is correctly answer with a JSP, then the json service is not working and vice versa.
Do I have to set an handler for each representation, and how?
Thank you in advance.
To determining what format the user has requested relies on a ContentNegotationStrategy,there are default implementations available out of the box,but you can also implement your own if you wish
To configure and use content negotiation with Spring using HTTP message converters :
You have to enable Content Negotiation in Spring MVC :
<bean id="cnManager"
class="org.springframework.web.accept.ContentNegotiationManagerFactoryBean">
<property name="favorPathExtension" value="true"/>
<property name="ignoreAcceptHeader" value="true" />
<property name="defaultContentType" value="application/json" />
<property name="useJaf" value="false"/>
<property name="mediaTypes">
<map>
<entry key="html" value="text/html" />
<entry key="json" value="application/json" />
<entry key="xml" value="application/xml" />
</map>
</property>
</bean>
Or using java configuration :
#Configuration
#EnableWebMvc
public class WebConfig extends WebMvcConfigurerAdapter {
#Override
public void configureContentNegotiation(
ContentNegotiationConfigurer configurer) {
configurer.favorPathExtension(true).
ignoreAcceptHeader(true).
useJaf(false).
defaultContentType(MediaType.TEXT_HTML).
mediaType("html", MediaType.TEXT_HTML).
mediaType("xml", MediaType.APPLICATION_XML).
mediaType("json", MediaType.APPLICATION_JSON);
}
}
This is an exemple :
the first method will return client.jsp page for each get for url end .../client and the second will return a client json object for each get end .../clients/id
#RequestMapping(value = "/client", method = RequestMethod.GET)
public ModelAndView getClientPage(final HttpServletRequest request,
final Model model,final HttpSession session) {
// traitement
return new ModelAndView("client");
}
#RequestMapping(value = "/clients/{id}", method = RequestMethod.GET)
public #ResponseBody Client getClient(#pathVariable("id") long id) {
Client client = YourService.getClient(id);
return client;
}
Contentnegotiatingviewresolver java config :
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = "com.yourbasepackage")
public class AppConfig extends WebMvcConfigurerAdapter {
#Bean
public ViewResolver jspViewResolver() {
InternalResourceViewResolver viewResolver = new InternalResourceViewResolver();
viewResolver.setViewClass(JstlView.class);
viewResolver.setPrefix("/WEB-INF/views/");
viewResolver.setSuffix(".jsp");
return viewResolver;
}
}

Spring #Transactional not starting with RoutingDataSources

I am using routing data sources, and my create operations are annotated with #Transactional annotations. But i noticed that transaction does not begin or commit. Following is my routing data source configuration.
<bean id="routingDataSource" class="com.test.dataaccess.base.dao.CustomerRoutingDataSource">
<property name="defaultTargetDataSource" ref="testDataSource" />
<property name="targetDataSources">
<map key-type="java.lang.String">
<entry key="0" value-ref="testDataSource" />
</map>
</property>
</bean>
<bean class="org.springframework.orm.jpa.JpaTransactionManager"
id="customerTransactionManager">
<property name="entityManagerFactory" ref="customerEntityManagerFactory" />
</bean>
Same data source i am using with my org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.
I am adding another datasources to routing data sources at deployment time as follows.
Spring transaction management does not works.
#Component
public class CustomerDataSourcePostProcessor implements ApplicationListener {
#Autowired
DatasourcesDAO datasourcesDAO;
#Autowired
#Qualifier("customerEntityManagerFactory")
private LocalContainerEntityManagerFactoryBean testContentEntityManagerFactory;
#Autowired
#Qualifier("routingDataSource")
private CustomerRoutingDataSource routingDataSource;
#Autowired
#Qualifier("customerTransactionManager")
private JpaTransactionManager customerTransactionManager;
private static final Logger LOGGER = LoggerFactory.getLogger(CustomerDataSourcePostProcessor.class);
public void onApplicationEvent(ApplicationEvent e) {
if (e instanceof ContextRefreshedEvent) {
loadCustomerDBConfigForServer();
}
}
private void loadCustomerDBConfigForServer() {
Map<Object, Object> databaseConfig = loadCustomerDatabaseConfig();
routingDataSource.setTargetDataSources(databaseConfig);
routingDataSource.afterPropertiesSet();
testContentEntityManagerFactory.setDataSource(routingDataSource);
testContentEntityManagerFactory.afterPropertiesSet();
EntityManagerFactory emf =testContentEntityManagerFactory.getObject(); // transaction not begin possible root cause one
customerTransactionManager.setEntityManagerFactory(emf);
customerTransactionManager.afterPropertiesSet();
}
}

Spring AOP #After annotation not running as expected

I am learning spring AOP, but I am having issues running the #After annotation.
For some reason, #After gets executed BEFORE the method call.
What am I doing wrong? Is it my Eclipse environment issue?
#Aspect
public class LoggingAspect {
#After("execution(* aop.*.* (..))")
public void AfterLoggingAdvice() {
System.out.println("AfterLoggingAdvice() is running");
}
}
This is my main class:
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(
"aop.xml");
ShapeService service = context.getBean("shapeService",
ShapeService.class);
System.out.println(service.getCircle().getName());
}
XML file:
<aop:aspectj-autoproxy/>
<bean id="circle" class="aop.Circle" >
<property name="name" value="Circle Name" />
<property name="id" value="Circle ID" />
</bean>
<bean id="shapeService" class="aop.ShapeService" autowire="byName"/>
<bean id="loggingAspect" class="aop.LoggingAspect"/>
</beans>
This is the output regardless of using #After or #Before:
AfterLoggingAdvice() is running
AfterLoggingAdvice() is running
Circle Name

How to instantiate and register a JobListener in the quartz.properties file?

I've read from many sources where people have said that it is possible and intended that a listener can be instantiated and registered to a scheduler all in the properties file
But I searched everywhere and can't find a single example of this.
Note: I am using this Quartz Initiazlizer Servlet to start my scheduler. Therefore I won't be able to use the conventional method of registering a JobListener to the scheduler. Other method of doing this is very accepted
Basically I want to do this:
JobListener jobListener = new SchedulerGlobalListener();
scheduler.getListenerManager().addJobListener(jobListener);
In this (under quartz.properties)
org.quartz.jobListener.NAME.class = com.foo.MyListenerClass
org.quartz.jobListener.NAME.propName = propValue
org.quartz.jobListener.NAME.prop2Name = prop2Value
*
*
Below is what I've tried and the results
#quartz.properties#
org.quartz.jobListener.SchedulerGlobalListener.class = com.scheduler.SchedulerGlobalListener
#listener class#
public class SchedulerGlobalListener implements JobListener {
private String name;
public SchedulerGlobalListener() {
}
public SchedulerGlobalListener(String name) {
if(name.isEmpty())
{
this.name = "SchedulerGlobalListener";
}
else
{
this.name = name;
}
}
#Override
public String getName() {
return name;
}
public String setName(String name) {
return name;
}
#Override
public void jobToBeExecuted(JobExecutionContext context) {
// do something with the event
}
#Override
public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) {
System.out.println("I just ran this job: " + context.getJobDetail().getJobClass().getName());
}
#Override
public void jobExecutionVetoed(JobExecutionContext context) {
// do something with the event
}
}
Result:
INFO: QuartzInitializer: Quartz Scheduler failed to initialize: java.lang.IllegalArgumentException: JobListener name cannot be empty.
Thanks for the help guys. I totally missed this (under Quartz config documentation):
For example if the properties file contains the property 'org.quartz.jobStore.myProp = 10' then after the JobStore class has been instantiated, the method 'setMyProp()' will be called on it.
quartz.properties
org.quartz.jobListener.SchedulerGlobalListener.class =
com.scheduler.SchedulerGlobalListener
org.quartz.jobListener.SchedulerGlobalListener.globalListenerName =
SchedulerGlobalListener
SchedulerGlobalListener.java
public void setGlobalListenerName(String name) {
this.name = name;
}
You have already done things almost.
Please refer
this link.
You just have to specify the name of the listener class in quartz.properties file
and make sure that the specified listener class is in your classpath.
Refer this
article for how to use joblistener except the part to register the
joblistener with scheduler. Instead, add the above mentioned properties to quartz properties file.
Here you find how hor to use Quartz and wrtie and trigger your jobs
http://www.mkyong.com/java/quartz-joblistener-example/
In Quartz there is a cron expression which describes the interval of quartz job to start again here
http://quartz-scheduler.org/documentation/quartz-1.x/tutorials/crontrigger
http://docs.oracle.com/cd/E12058_01/doc/doc.1014/e12030/cron_expressions.htm
you found how to write cron expressions.
Quartz simple trigger
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("TriggerName", "group1")
.withSchedule(
SimpleScheduleBuilder.simpleSchedule()
.withIntervalInSeconds(5).repeatForever())
.build();
Quartz Cron Trigger
Trigger trigger = TriggerBuilder
.newTrigger()
.withIdentity("TriggerName", "group1")
.withSchedule(
CronScheduleBuilder.cronSchedule("0/5 * * * * ?"))
.build();
If you are using spring then all quartz properties you can mention in your context file as
<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
<property name="triggers">
<list>
<ref bean="cronTrigger" />
<ref bean="simpleTrigger" />
</list>
</property>
<property name="quartzProperties">
<props>
<prop key="propertName">propertyValue</prop>
</props>
</property>
</bean>
and
<bean id="beanName" class="org.springframework.scheduling.quartz.SchedulerFactoryBean" destroy-method="destroy">
<property name="jobFactory">
<bean class="org.springframework.scheduling.quartz.SpringBeanJobFactory"/>
</property>
<property name="dataSource" ref="JNDIDataSource" />
<property name="transactionManager" ref="transactionManager" />
<property name="quartzProperties">
<util:properties location="classpath:/quartz.properties"/>
</property>
<property name="triggers">
<list>
<ref bean="triggerBean"/>
</list>
</property>
</bean>

JPA entityManager is null in Pointcut

I have defined a pointcut using the #Aspect annotation in my class.
I configure the pointcut using a custom annotation which I have defined in my context:
<aop:aspectj-autoproxy proxy-target-class="true"/>
<!-- Messaging pointcut -->
<bean id="messagePointcut" class="com.adobe.codex.aspects.MessagePointcut" >
<constructor-arg ref="msgPointcutEntityFactory"/>
<property name="buildDao" ref="buildDao"/>
</bean>
<!-- enable our own annotation -->
<aop:config proxy-target-class="true">
<aop:aspect ref="messagePointcut">
<aop:pointcut id="proxiedMethods" expression="#annotation(com..codex.aspects.annotation.MessageGateway)"/>
<aop:around pointcut-ref="proxiedMethods" method="interceptAnnotatedMethod"/>
</aop:aspect>
</aop:config>
Unfortunately the entityManager inside buildDao is always null if I have a reference to buildDao in my pointcut.
Not sure what the best way to fix this would be.
I'm assuming the problem is that the weaving used (load time) is does not know how to create an entityManager from the entityManagerFactory bean.
here is a snippet of my dao context.
<context:annotation-config />
<bean id="entityManagerFactory"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<property name="dataSource" ref="dataSource" />
<property name="jpaProperties">
<util:properties
location="classpath:com//codex/dao/jpa/hibernate.properties" />
</property>
</bean>
<bean id="buildDao" class="com..codex.dao.jpa.JpaBuildDao">
<description>
A DAO for Builds.
</description>
<property name="queryHelper" ref="queryHelper" />
<property name="partDao" ref="partDao" />
<property name="buildQueryFactory" ref="buildQueryFactory" />
</bean>
Here is my Pointcut:
#Aspect
#Transactional()
public class MessagePointcut implements Ordered, MsgObservable {
private MsgPointcutEntityFactory msgEntityFactory;
private BuildDao buildDao;
public void setBuildDao(BuildDao buildDao) {
this.buildDao = buildDao;
}
public MessagePointcut(MsgPointcutEntityFactory msgEntityFactory){
this.msgEntityFactory = msgEntityFactory;
}
#Transactional(readOnly = true)
public Object interceptAnnotatedMethod(ProceedingJoinPoint pjp) {
Object returnedEntity = null;
Object originalEntity = null;
try { //
// do stuff before executing the call
originalEntity = msgEntityFactory.fetch(id, Build.class);
//execute the call
returnedEntity = pjp.proceed();
// do stuff after executing the call
// ...
} catch (Throwable e) {
e.printStackTrace();
}
return returnedEntity;
}
#Override
public int getOrder() {
return 2;
}
}
And a snippet of my dao
#Repository
public class JpaBuildDao implements BuildDao {
private static final Log log = LogFactory.getLog(JpaBuildDao.class);
#PersistenceContext
private EntityManager entityManager;
private QueryHelper queryHelper;
private BuildQueryFactory standardQueryFactory;
private PartDao partDao;
public Build getFlatBuild(Integer id) {
Build returnBuild;
Query query = entityManager.createQuery(
"SELECT b FROM Build b " +
"WHERE " +
"b.id = :id");
query.setParameter("id", id);
returnBuild = (Build) query.getSingleResult();
return returnBuild;
}
Made some progress. The real issue is that buildDao is injected raw into the pointcut w/o the required Jpa proxy that instantiates the entityManager.
Turns out the issue only occurs when another config detail comes into the mix. I also have two MethodInvokingFactoryBean instances injecting beans into my pointcut:
<bean id="registerListenerJms"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="messagePointcut" />
</property>
<property name="targetMethod">
<value>registerObserver</value>
</property>
<property name="arguments">
<list>
<ref bean="jmsGateway" />
</list>
</property>
</bean>
<bean id="registerListenerAmf"
class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject">
<ref local="messagePointcut" />
</property>
<property name="targetMethod">
<value>registerObserver</value>
</property>
<property name="arguments">
<list>
<ref bean="amfGateway" />
</list>
</property>
</bean>
When I remove these two beans my pointcut doesn't get the raw proxy, but it gets a JdkDynamicAopProxy with a reference to the dao.
Have no clue why MethodInvokingFactoryBean messes up injecting the dao, but it does.
Bottom line is for the time being I'm removing the MethodInvokingFactoryBean that implement my observer pattern and live with a dependency of the pointcut on the beans that want to hook in.
Not a complete solution but an acceptable workaround.