Spring: define custom #Transactional behavior in Java config - java

I would like Spring to rollback a transaction on methods annotated with #Transactional in case the method throws a checked exception. An equivalent of this:
#Transactional(rollbackFor=MyCheckedException.class)
public void method() throws MyCheckedException {
}
But I need this behavior to be default for all #Transactional annotations without the need to write it everywhere. We are using Java to configure Spring (configuration classes).
I tried the configuration suggested by spring documentation, which is only available in XML. So I tried to create this XML file:
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd">
<tx:advice id="txAdvice" transaction-manager="txManager">
<tx:attributes>
<tx:method name="*" rollback-for="com.example.MyCheckedException" />
</tx:attributes>
</tx:advice>
</beans>
... and import it via #ImportResource. Spring did recognize and parse the file (I had some errors in it at first), but it doesn't work. The behavior of #Transactional has not changed.
I also tried defining my own transaction property source, as suggested in this answer. But it also used the XML configuration so I had to transform it into Java like this:
#Bean
public AnnotationTransactionAttributeSource getTransactionAttributeSource() {
return new RollbackForAllAnnotationTransactionAttributeSource();
}
#Bean
public TransactionInterceptor getTransactionInterceptor(TransactionAttributeSource transactionAttributeSource) {
TransactionInterceptor transactionInterceptor = new TransactionInterceptor();
transactionInterceptor.setTransactionAttributeSource(transactionAttributeSource);
return transactionInterceptor;
}
#Bean
public BeanFactoryTransactionAttributeSourceAdvisor getBeanFactoryTransactionAttributeSourceAdvisor(TransactionAttributeSource transactionAttributeSource) {
BeanFactoryTransactionAttributeSourceAdvisor advisor = new BeanFactoryTransactionAttributeSourceAdvisor();
advisor.setTransactionAttributeSource(transactionAttributeSource);
return advisor;
}
This also didn't work - Spring kept using its own transaction property source (different instance than the one which was created in the configuration).
What is the correct way to achieve this in Java?

You should rather implement own annotation - reference
#Target({ElementType.METHOD, ElementType.TYPE})
#Retention(RetentionPolicy.RUNTIME)
#Transactional(rollbackFor=MyCheckedException.class)
public #interface TransactionalWithRollback {
}

Related

Invoke annotated aspect on all methods with a specific annotation

I have an application based on MVC and I want to log inputs/outputs from all my controllers.
Also, each of my controller methods is annotated with some specific annotation (eg: #MyControllerMethod).
I wrote an Annotated Aspect which works fine when I use the annotation (eg: LogRequestReply) from the aspect on my controller methods.
#Around("#annotation(com.xyz.LogRequestReply)")
public Object logRequestResponse(ProceedingJoinPoint pjp) throws Throwable {
#MyControllerMethod
#LogRequestReply /* It invokes my logRequestResponse method */
public ResponseEntity controllerMethodA(...) {}
However, I have a lot of controller methods and each with annotation MyControllerMethod.
Is there a way I can make my aspect annotation make work on all controller methods by default, such that each of the methods automatically calls my aspect logger and I can log the inputs/outputs?
Note:
I also looked into writing an interceptor extending from HandlerInterceptorAdapter. But it has its own complexity of being able to log request/response where the stream can only be read once.
Please suggest.
One option is to let the #Around definition less restrictive, add a restriction by package or something like that.
Internally you can check if this is a Controller by this check bellow and then print the request/response:
#Pointcut("within(com.mypackage.controller..*)")
private void inControllerPackage() {
}
#Around("inControllerPackage()")
public Object logRequestResponse(ProceedingJoinPoint pjp) throws Throwable {
if (pjp.getSignature().getDeclaringType().isAnnotationPresent(RestController.class)) {
...
}
Perhaps you should include the AOP in your controller-config
<?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:p="http://www.springframework.org/schema/p"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xmlns:oxm="http://www.springframework.org/schema/oxm"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.1.xsd
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util-4.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-4.1.xsd
http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.1.xsd">
<mvc:default-servlet-handler />
<context:component-scan base-package="com.xyz.controller" />
<aop:aspectj-autoproxy proxy-target-class="true" />
<bean id="whatever" class="com.xyz.controller.MyRestController"/>
</bean>
and in your Aspect you can also include the within combined with annotation:
#Around(value = "#within(com.xyz.server.annotation.MyAnnotationForController) || #annotation(com.xyz.server.annotation.MyAnnotationForController)")
public Object ownersTimeZone(ProceedingJoinPoint joinPoint) throws Throwable {
//whatever you need before
Object obj = joinPoint.proceed();
//whatever you need after
return obj;
}

Spring AOP Advice is being executed twice

I am using Spring AOP to create an Aspect. The Aspect that I defined is being executed twice. I can't seem to figure out why. I'd appreciate any inputs anyone has on this issue.
Thanks!
// Spring Configuration
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
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/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"
default-autowire="no"
default-lazy-init="true">
<bean id="loggingAdvice" class="com.xyz.aop.LoggingAdvice" />
<aop:config>
<aop:aspect id="loggingAspect" ref="loggingAdvice">
<aop:pointcut id="loggingPointcut"
expression="execution(* com.xyz.FooServiceImpl.foo(..))"/>
<aop:around pointcut-ref="loggingPointcut" method="log" />
</aop:aspect>
</aop:config>
<context:component-scan base-package="com.xyz.controllers" />
</beans>
// Advice
package com.xyz.aop;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.aspectj.lang.ProceedingJoinPoint;
public class LoggingAdvice {
public Object log(final ProceedingJoinPoint pjp) throws Throwable {
log.info("Started!");
try {
return pjp.proceed();
} finally {
log.info("Ended!");
}
}
}
// Advised Method
package com.xyz;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
public class FooServiceImpl implements FooService {
private static final Log log = LogFactory.getLog(FooServiceImpl.class);
#Override
public void foo() {
log.info("Foo!");
}
}
}
// Output
Started!
Started!
Foo!
Ended!
Ended!
// EDIT 1: Added more spring configuration info. Not using any Spring AOP annotations at all. I attached a debugger and saw that the aspect/log statements were being executed twice as well. So I doubt that it has anything to do with log statement printing the string twice.
Well, it seems like this is actually an issue with the logger. Same problem I encountered long time back and found that everything is being logged twice. When I replaced logger calls with regular sysout calls, everything worked fine.
Because the <aop:around...> is telling this to do work both before and after the method. You could use <aop:before...> or <aop:after...> instead for running only once.

Unit testing ONE piece of JSR 303 validator class in Spring

I would like to unit test one validator class in Spring. There are 6 other existing validator classes but they don't have unit tests so I only want to test my validator class.
My problem is that when I run my unit test (which tests only my class) "somehow" an other validator class is being tested... (it doesn't have unit test).
Is it possible that this is the default behaviour of the LocalValidatorFactoryBean to scan all the validator classes and to run their isValid method?
Can I specify the validator classes somehow that have to be tested? Like defining a ConstraintValidatorFactory?
Java test class
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"/applicationContext-test-validator.xml"})
public class MyValidatorTest {
#Autowired
private Validator validator;
#Test
public void testIsValid_true() {
...
Set<ConstraintViolation<Record>> violations = validator.validate(data);
...
}
}
application context XML
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.2.xsd">
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
</beans>
Thanks,
V.

spring java xml configuration

im using spring for my java web app. the site has got bigger and i would like to set some configurations.
i have been researching and came across things like document builder factory, replacing spring xml with java config and others. i dunno where to start.
im thinking of implementing the configurations in xml (WEB/newConfig.xml) and have it read by the java beans. basically i wanna input my cofiguration values into xml and have it load by a java bean so that i can use it in controllers and jstl.
im just giving some examples here. for example xml configurations:
<property name="numberOfCars" value="3" />
<property name="webSiteName" value="New Spring Web App" />
....
and i read it in my java class:
class Config {
public getNumberOfCars() {
return numOfCars;
}
public getWebSiteName() {
return webSiteName;
}
}
where should i start and what online materials can i read?
==============================
update
here is what i have created.
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context"
xmlns:jee="http://www.springframework.org/schema/jee" xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 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/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">
<context:property-placeholder location="/WEB-INF/your_prop_file.properties" />
<bean id="ConfigMgr" class="org.domain.class.ConfigMgr">
<property name="username" value="${username}">
</bean>
</beans>
you_prop_file.properties
username=hello world name
ConfigMgr.java
public class ConfigMgr {
private String username;
...getter
...setter
}
in my controller, here is what i did:
ConfigMgr config = new ConfigMgr();
sysout.out.println(config.getUsername());
i am getting null and i am sure im missing something here. where should i set the username value to the ConfigMgr class?
Spring Java configuration is a newer feature that allows you to configure your Spring application using Java classes instead of XML files. Its just an alternative for XML configuration. XML way is equally feature rich.
From what I could figure out from your problem, you want to move the hardcoded values of params (numberOfCars,webSiteName.. ) outisde your configuration file.
If that is the case, you don't have to go that far.
Just use :-
<context:property-placeholder location="classpath:your_prop_file.properties" />
in your spring xml file and replace the param values like:-
<property name="webSiteName" value="${website.name}" />
You need to have a your_prop_file.properties file in your classpath with enteries like:-
website.name=New Spring Web App
You are not injecting the ConfigMgr bean that you created in XML file.
What you are doing is you are creating a new Object in controller which does not have a clue about properties file.
Now you can inject it using either #Autowired inside your controller or through xml configuration.
There are plenty of examples available in google on basic spring dependency injection.

logging with AOP in spring?

I am new to spring in my office . So there is no guidance for me.
I need to implement the logging with the AOP using the log4j.
I have implemented the logging without AOP in basic spring MVC example ?
Also did the small sample in AOP using the aspectJ without logging (just made the Sysout) ?
I don't know how to integrate it ?
Can any one please give me a start up idea?
Good answers are definitely appreciated ...
Spring makes it really easy for us to make use of AOP. Here's a simple logging example:
#Aspect
public class MyLogger {
private Logger log = Logger.getLogger(getClass());
#After("execution(* com.example.web.HomeController.*(..))")
public void log(JoinPoint point) {
log.info(point.getSignature().getName() + " called...");
}
}
Then simply configure your applicationContext.xml (or equivalent):
<aop:aspectj-autoproxy>
<aop:include name="myLogger"/>
</aop:aspectj-autoproxy>
<bean id="myLogger" class="com.example.aspect.MyLogger"/>
You'll notice in the MyLogger class that I specified #After right above the method. This is called the advice and it basically specifies that this 'log' method will be called after the method in question. Other options include #Before, #Around, #AfterThrowing.
The expression "execution(* com.example.web.HomeController.*(..))" is called a pointcut expression and specifies what we're targeting (in this case all methods of the HomeController class).
P.S. The aop namespace (xmlns:aop="http://www.springframework.org/schema/aop") and the schema location (version dependent) would need to be added to your applicationContext.xml right at the top. Here is my setup:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd
http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd">
You need to perform several steps to integrate Aspectj:
Install AspectJ
Add your aop.xml to META-INF\aop.xml in your project
Add aspectjrt-x.x.0.jar and aspectjweaver-x.x.0.jar in your project classpath
Add -javaagent:/path to aspectj installation/aspectjweaver-1.7.0.jar to your server's JVM.
Here is a sample aop.xml:
<aspectj>
<aspects>
<aspect name="test.MySimpleLoggerAspect" />
</aspects>
<weaver>
<include within="test.myproject.*" />
</weaver>
</aspectj>
If you are already using Spring then it is better to use Spring to simplify your setup.

Categories