I have written one aspect around a service class. In the aspect, I am doing some operation in the before section, which I would like to be rolled back if some exception occurs in the enclosed service method.
The service class is as follows:
#Service
#Transactional
class ServiceA {
...
public void doSomething() {
...
}
...
}
The aspect is as follows:
#Aspect
#Order(2)
public class TcStateManagementAspect {
...
#Around(value = "applicationServicePointcut()", argNames = "joinPoint")
public Object process(ProceedingJoinPoint joinPoint)
throws Throwable {
...
*/Before section */
do some processing and persist in DB
...
Object object = joinPoint.proceed();
...
do some post-processing
}
}
I am seeing an exception in the service method is not rolling back the DB operation in the Begin Section. I tried putting #Transactional on #Around, but it did not help.
In this context, I have gone through the following posts:
Spring #Transactional in an Aspect (AOP)
Custom Spring AOP Around + #Transactional
But I am not able to get any concrete idea regarding how to achieve this. Could anyone please help here? Thanks.
Like I said in my comment, what your around advice does must be declared transactional too. You cannot do that directly, because #Transactional internally uses Spring AOP via dynamic proxies. However, Spring AOP aspects cannot be the target of other aspects. But you can simply create a new helper #Component which you delegate your advice's action to.
Let us assume that the goal is to log the arguments of the #Transactional method targeted by your aspect. Then simply do this:
package com.example.managingtransactions;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Aspect
#Component
public class TxLogAspect {
private final static Logger logger = LoggerFactory.getLogger(TxLogAspect.class);
#Autowired
TxLogService txLogService;
#Pointcut(
"#annotation(org.springframework.transaction.annotation.Transactional) && " +
"!within(com.example.managingtransactions.TxLogService)"
)
public void applicationServicePointcut() {}
#Around("applicationServicePointcut()")
public Object process(ProceedingJoinPoint joinPoint) throws Throwable {
logger.info(joinPoint.toString());
// Delegate to helper component in order to be able to use #Transactional
return txLogService.logToDB(joinPoint);
}
}
package com.example.managingtransactions;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;
import java.util.Arrays;
import java.util.List;
/**
* Helper component to delegate aspect advice execution to in order to make the
* advice transactional.
* <p>
* Aspect methods themselves cannot be #Transactional, because Spring AOP aspects
* cannot be targeted by other aspects. Delegation is a simple and elegant
* workaround.
*/
#Component
public class TxLogService {
#Autowired
private JdbcTemplate jdbcTemplate;
#Transactional
public Object logToDB(ProceedingJoinPoint joinPoint) throws Throwable {
jdbcTemplate.update(
"insert into TX_LOG(MESSAGE) values (?)",
Arrays.deepToString(joinPoint.getArgs())
);
return joinPoint.proceed();
}
public List<String> findAllTxLogs() {
return jdbcTemplate.query(
"select MESSAGE from TX_LOG",
(rs, rowNum) -> rs.getString("MESSAGE")
);
}
}
See? We are passing through the joinpoint instance to the helper component's own #Transactional method, which means that the transaction is started when entering that method and committed or rolled back depending on the result of joinPoint.proceed(). I.e. what the aspect helper writes to the DB itself will also be rolled back if something goes wrong in the aspect's target method.
BTW, because I never used Spring transactions before, I simply took the example from https://spring.io/guides/gs/managing-transactions/ and added the two classes above. Before, I also added this to schema.sql:
create table TX_LOG(ID serial, MESSAGE varchar(255) NOT NULL);
Next, I added made sure that TxLogService is injected into AppRunner:
private final BookingService bookingService;
private final TxLogService txLogService;
public AppRunner(BookingService bookingService, TxLogService txLogger) {
this.bookingService = bookingService;
this.txLogService = txLogger;
}
If then at the end of AppRunner.run(String...) you add these two statements
logger.info("BOOKINGS: " + bookingService.findAllBookings().toString());
logger.info("TX_LOGS: " + txLogService.findAllTxLogs().toString());
you should see something like this at the end of the console log:
c.e.managingtransactions.AppRunner : BOOKINGS: [Alice, Bob, Carol]
c.e.managingtransactions.AppRunner : TX_LOGS: [[[Alice, Bob, Carol]]]
I.e. you see that only for the successful booking transaction a log message something was written to the DB, not for the two failed ones.
Related
In a Java 11/Spring REST API project I have an interface with multiple implementations. I want to choose the implementation in the configuration (in this case, application.yml file):
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
#Retention(RUNTIME)
#Target(TYPE)
public #interface PickableImplementation {
// This id will match an entry in the config file
public String id() default "";
}
So I have the two possible "pickable" implementations:
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import com.mycompany.api.util.PickableImplementation;
#Component
#Service
#PickableImplementation(id = "s3")
public class BatchProcessServiceS3 implements BatchProcessService {
// Some implementation biz logic
}
// In another file, with the same imports:
#Component
#Service
#PickableImplementation(id = "azure")
public class BatchProcessServiceAzure implements BatchProcessService {
// Another implementation biz logic
}
In the consumer class which in this case is a Controller, I try to pick the desired implementation:
import com.mycompany.api.util.PickableImplementation;
import java.util.List;
#RestController
#RequestMapping("/batch")
public class BatchController {
#Autowired private Environment env;
private BatchProcessService batchProcessService;
private final List<BatchProcessService> batchProcessImplementations;
public BatchController(List<BatchProcessService> batchProcessImplementations,
Environment environment){
this.env = environment;
var activeSvcImplementationId = env.getRequiredProperty("buckets-config.active");
this.batchProcessImplementations = batchProcessImplementations;
for (var batchProcessService : this.batchProcessImplementations) {
if(batchProcessService.getClass().isAnnotationPresent(PickableImplementation.class)) {
// Verify the id, compare with the one from config file, etc.
}
}
}
}
Expected behavior: inside the loop, I expected to get the annotations of each implementation, traverse the list, verify if it matches with the one with the application.yml and if it does, pick it to populate the service layer (private BatchProcessService batchProcessService).
Actual behavior: not only the isAnnotationPresent() method returns false, but also if I try getAnnotations() I get an empty array, like there are no annotations in the class. And besides my custom one, there are at least two additional annotations (Component, Service and others related to logging and the like).
As another puzzling detail, if I run getAnnotations() on the qualified name of the class in the middle of a debugging session, the annotations are present. But in that very moment, running that method on the elements on the list return 0 annotations.
I've run out of ideas, has anyone tried this same combination of autowiring several implementations and at the same time, relying in custom annotations?
Additional references:
Custom Annotations in Java:
https://www.baeldung.com/java-custom-annotation
Autowired:
https://www.baeldung.com/spring-autowire
Useful answer on autowiring multiple implementations:
https://stackoverflow.com/a/51778396/6315428
I have really poor experience with ribbon/eureka so forgive me if this is a stupid question:
I have two different microservice both connected to a discovery server, the first one calls the second using a custom annotation that sends a request using rest template.
Custom annotation name is PreHasAuthority
Controller :
#PreHasAuthority(value="[0].getProject()+'.requirements.update'")
#PostMapping(CREATE_UPDATE_REQUIREMENT)
public ResponseEntity<?> createUpdateRequirement(#Valid #RequestBody RequirementDTO requirementDTO
, HttpServletRequest request, HttpServletResponse response) {
return requirementService.createUpdateRequirement(requirementDTO, request, response);
}
Annotation interface :
import java.lang.annotation.*;
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface PreHasAuthority {
String value();
}
Annotation implementation:
import java.lang.reflect.Method;
import javax.servlet.http.HttpServletRequest;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.stereotype.Component;
import netcomgroup.eu.service.AuthenticationService;
#Aspect
#Component
public class PreHasAuthorityServiceAspect {
#Autowired
private AuthenticationService authenticationService;
#Around(value = "#annotation(PreHasAuthority)")
public void around(ProceedingJoinPoint joinPoint) throws Throwable {
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
PreHasAuthority preHasAuthority = method.getAnnotation(PreHasAuthority.class);
Object[] args = joinPoint.getArgs();
String permission = preHasAuthority.value();
ExpressionParser elParser = new SpelExpressionParser();
Expression expression = elParser.parseExpression(permission);
String per = (String) expression.getValue(args);
String token =null;
for(Object o : args) {
if(o instanceof HttpServletRequest) {
HttpServletRequest request = (HttpServletRequest)o;
token=request.getHeader("X-Auth");
break;
}
}
if(token==null) {
throw new IllegalArgumentException("Token not found");
}
boolean hasPerm = authenticationService.checkPermission(per,token);
if(!hasPerm)
throw new Exception("Not Authorized");
}
}
My Ribbon configuration
import com.netflix.client.config.IClientConfig;
import com.netflix.loadbalancer.IRule;
import com.netflix.loadbalancer.RoundRobinRule;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
public class RibbonConfiguration {
#Autowired
IClientConfig config;
#Bean
public IRule ribbonRule(IClientConfig config) {
return new RoundRobinRule();
}
}
Eureka config in application properties
#Eureka config
eureka.client.serviceUrl.defaultZone= http://${registry.host:localhost}:${registry.port:8761}/eureka/
eureka.client.healthcheck.enabled= true
eureka.instance.leaseRenewalIntervalInSeconds= 10
eureka.instance.leaseExpirationDurationInSeconds= 10
by calling the api from postman request is sendend correctly to the second microservice and i'm certain the return is "true".
After that the request stops before entering the createUpdateRequirement method and returns '1' as postman body response. No error of sort is provided.
My guess is that the problem resides within the custom annotation, cause when i remove the annotation the api call works perfectly, but i cannot understand the problem as it seems all setted up correctly to me.
Your #Around advice never calls joinPoint.proceed(). Hence, the intercepted target method will never be executed.
The second problem is that your advice method returns void, i.e. it will never match any method returning another type such as the ResponseEntity<?> createUpdateRequirement(..) method.
Besides, around is a reserved keyword in native AspectJ syntax. Even though it might work in annotation-driven syntax, you ought to rename your advice method to something else like aroundAdvice or interceptPreHasAuthority - whatever.
Please do read an AspectJ or Spring AOP tutorial, especially the Spring manual's AOP chapter. 😉
I am playing with an idea to use a similar approach that #Configuration classes are able to do, that they can lazily create beans with calls to #Bean methods and return the existing objects if already called. This is done through some magic with CGLib proxies.
One particular interesting thing is that it works even when calling the method on itself:
#Configuration
class Config {
#Bean ClassA beanA() {
return new ClassA(beanB());
}
#Bean ClassB beanB() {
return new ClassB();
}
}
Now, in my use case, not concerning Spring configuration, I want to use this ability to lazily create arbitrary object graphs (which should not be Spring Beans) by calling a method of a Builder bean that would create the objects if not yet called, and returning existing objects if already called. And as well I want to leverage the ability to self-invoke methods on the same instance. So far, I wasn't able to do this.
How can I create and enhance Spring Beans (as CGLib proxies) so that they are able to self-invoke methods, similarly the #Configuration classes do, but with my own custom advice handling the laziness and caching?
EDIT : more detail
The result, in the end, should look similar to the configuration example above, but it would be a normal Spring singleton bean:
#Component
#MyBuilder // or some other custom annotation
class MyObjectGraphBuilder {
#Builder ClassA objectA() {
return new ClassA(objectB());
}
#Builder ClassB objectB() {
return new ClassB();
}
}
With the added capability to only call the original method once, and caching the result for any subsequent call (including especially the self-invocation). The above is just an example, there may be many such builder beans, and they can be complex with cross-dependencies between them.
The method call result caching is simple (could be done by AOP), but what I want is the self-invocation capability, which is normally not supported by Spring unless it's a #Configuration class.
I figured that Spring is doing this by enhancing the #Configuration bean classes with their own CGlib proxies. However, it involves a lot of copying and customizing (e.g. ConfigurationClassEnhancer, ConfigurationClassPostProcessor, etc), and so far I had no luck of actually making it work with my custom Post Processor and Enhancer (the code is too long, but it's basically a copy of the mentioned classes and writing my custom method interceptors). So I'm trying to find if there exists any other way.
The simple answer concerning AOP and self-invocation is: You cannot use Spring AOP, you have to use full AspectJ. The good news is that you don't require any proxies for that solution. The Spring manual describes how to use AspectJ from Spring via LTW (load-time weaving). Don't worry, if configured correctly you can use AspectJ alongside other aspects implemented via Spring AOP. Besides, if you don't like LTW, you can also use compile-time weaving via AspectJ Maven plugin.
Now here is a little caching example in pure Java + AspectJ (no Spring involved) for demonstration:
Builder annotation:
package de.scrum_master.app;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target(METHOD)
public #interface Builder {}
Sample classes:
package de.scrum_master.app;
public class ClassB {
#Override
public String toString() {
return "ClassB#" + hashCode();
}
}
package de.scrum_master.app;
public class ClassA {
private ClassB objectB;
public ClassA(ClassB objectB) {
this.objectB = objectB;
}
#Override
public String toString() {
return "ClassA#" +hashCode() + "(" + objectB + ")";
}
}
Driver application with annotated factory methods:
package de.scrum_master.app;
public class MyObjectGraphBuilder {
#Builder
ClassA objectA() {
return new ClassA(objectB());
}
#Builder
ClassB objectB() {
return new ClassB();
}
public static void main(String[] args) {
MyObjectGraphBuilder builder = new MyObjectGraphBuilder();
System.out.println(builder.objectB());
System.out.println(builder.objectA());
System.out.println(builder.objectB());
System.out.println(builder.objectA());
System.out.println(builder.objectB());
System.out.println(builder.objectA());
}
}
Console log without caching aspect:
ClassB#1829164700
ClassA#2018699554(ClassB#1311053135)
ClassB#118352462
ClassA#1550089733(ClassB#865113938)
ClassB#1442407170
ClassA#1028566121(ClassB#1118140819)
So far, so predictable. This is the normal behaviour, no caching at all.
Caching aspect:
Now this aspect is really simple. There is no thread-safety, no way to create multiple named beans of the same class or anything similar, but I guess you can take it from here, the principle stays the same.
package de.scrum_master.app;
import java.util.HashMap;
import java.util.Map;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
#Aspect
public class BuilderCacheAspect {
private Map<Class<?>, Object> cachedObjects = new HashMap<>();
#Around("#annotation(de.scrum_master.app.Builder) && execution(* *(..))")
public Object findOrCreateObject(ProceedingJoinPoint thisJoinPoint) throws Throwable {
//System.out.println(thisJoinPoint);
Class<?> returnType = ((MethodSignature) thisJoinPoint.getSignature()).getReturnType();
Object cachedObject = cachedObjects.get(returnType);
if (cachedObject == null) {
cachedObject = thisJoinPoint.proceed();
cachedObjects.put(returnType, cachedObject);
}
return cachedObject;
}
}
Console log with caching aspect:
ClassB#1392838282
ClassA#664740647(ClassB#1392838282)
ClassB#1392838282
ClassA#664740647(ClassB#1392838282)
ClassB#1392838282
ClassA#664740647(ClassB#1392838282)
Tadaa! There is our simple object cache. Enjoy.
I am trying to execute a set code in my advice but can't able to weave the code inside the function which has the #SecuredAPI annotation and calls the
setQuery() function.
Previously I tried the following pointcut and it worked very well
call(* org.elasticsearch.action.search.SearchRequestBuilder.setQuery(org.elasticsearch.index.query.QueryBuilder)) && args(queryBuilder)
But need to also include the annotated condition in this. Please help me with that.
My poincut and advice looks like this
#Around(value = "#annotation(SecuredAPI) && call(* org.elasticsearch.action.search.SearchRequestBuilder.setQuery(org.elasticsearch.index.query.QueryBuilder)) && args(queryBuilder)" )
public Object decorateQuery(ProceedingJoinPoint proceedingJoinPoint, QueryBuilder queryBuilder) throws Throwable {
// ...
}
And my function looks like this
#SecuredAPI
public List<Integer> getAllIds() {
// ...
SearchResponse response = conn
.getESClient().prepareSearch(ElasticSearchConstants.ATTRIBUTE_INDEX)
.setSearchType(SearchType.DEFAULT)
//.setQuery(QueryBuilders.queryStringQuery(searchQuery))
.setQuery(qb)
.setFrom(0).setSize(10000).setExplain(true).get();
}
Please help me to find a way to may it work for following condition
Okay, while editing your question (the code formatting was a bit chaotic) I read it again and you said that call() actually works for you. So you are not using Spring AOP because call() is not supported there. You must be using AspectJ, probably via LTW (load-time weaving) or maybe via CTW (compile-time weaving). It does not make a difference for the answer.
The problem is that #annotation(SecuredAPI) would actually work inside an execution() pointcut defined on your annotated method, but the method you call from there is not annotated, so the advice does not get triggered for call(). It only would if the target method setQuery(..) was annotated, but it is not. Consequently, #annotation() is not the right pointcut for your purpose.
What you want to express is: "a call to setQuery(..) from within code annotated by #SecuredAPI". This is done as follows (AspectJ example without Spring, please adjust class and package names to your needs):
package de.scrum_master.app;
import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.ElementType.TYPE;
import static java.lang.annotation.RetentionPolicy.RUNTIME;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;
#Retention(RUNTIME)
#Target({ TYPE, FIELD, METHOD })
public #interface SecuredAPI {}
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
Application application = new Application();
application.doSomething();
application.doSomethingElse();
}
#SecuredAPI
public void doSomething() {
System.out.println("Doing something before setting query");
setQuery("my first query");
System.out.println("Doing something after setting query");
}
public void doSomethingElse() {
System.out.println("Doing something else before setting query");
setQuery("my second query");
System.out.println("Doing something else after setting query");
}
public void setQuery(String query) {
System.out.println("Setting query to: " + query);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
#Aspect
public class SecuredAPIAspect {
#Around("#withincode(de.scrum_master.app.SecuredAPI) && call(* setQuery(..))")
public Object myAdvice(ProceedingJoinPoint thisJoinPoint) throws Throwable {
System.out.println(thisJoinPoint);
return thisJoinPoint.proceed();
}
}
See? #withincode() is your friend in this case. The console log looks as follows:
Doing something before setting query
call(void de.scrum_master.app.Application.setQuery(String))
Setting query to: my first query
Doing something after setting query
Doing something else before setting query
Setting query to: my second query
Doing something else after setting query
Besides, you also need to use a fully-qualified class name for the annotation such as de.scrum_master.app.SecuredAPI, not just SecuredAPI, unless the annotation happens to be in the same package as your aspect.
I am trying to implement Spring Security ACL in my application. I have many classes that I want to use an ACL on.
I read in the documentation that AOP have been used with success before. Does this mean that all the services should have a common interface for doing CRUD against the objects for maximum reuse of the advise?
Or is it normal to manually insert, delete, ... in the save, update, delete methods of the service?
I can't manage to find many examples of how people use the framework.
---- Listener for Entity removal (includes cascading deletes) -----
package com.acme.model.aspects;
import javax.annotation.PostConstruct;
import javax.persistence.PreRemove;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;
import org.springframework.data.domain.Persistable;
import com.acme.PermissionService;
#Component
public class ObjectIdentityListener {
private static final Logger LOG = LoggerFactory.getLogger(ObjectIdentityListener.class);
static private PermissionService permissionService;
#Autowired(required = true)
#Qualifier("permissionService")
public void setSearchService(PermissionService _permissionService)
{
permissionService = _permissionService;
}
#PreRemove
public void preRemove(Object object) {
if(object instanceof Persistable) {
LOG.info("Deleting object identity for class {} id {} ", persistable.getClass(), persistable.getId());
permissionService.deleteObjectIdentity((Persistable) object);
}
}
#PostConstruct
public void init() {
Assert.notNull(permissionService, "'permissionService' is required");
}
}
---- Delete method for permissionService ----
public void deleteObjectIdentity(Persistable persistable) {
try{
MutableAcl acl = (MutableAcl) mutableAclService.readAclById(identity(persistable));
mutableAclService.deleteAcl(acl.getObjectIdentity(), true);
} catch (NotFoundException e){
LOG.info("Could not find ACL for target {}", persistable);
}
}
It all depends on your app. Having a centralized hierarchy of services would certainly make it simpler to implement single security checks for create/retrieve/update/delete methods. But you have an existing app with different services that don't necessarily have a common parent implementation, then you'd have to add ALC security annotation on each service method.
Another option is to put ACL security on your DAO layer, it works fine, but for some reason just doesn't feel right. IMHO DAO's shouldn't deal with things like security. I've spent a LOT of time dealing with Spring Security ACL, got a pretty good handle on it by now, ping me if you need any concrete examples.