Custom Spring AOP Annotation Not Working for Default Method - java

I'm trying to add already working AOP annotation around a new method in the class where it is already placed. Its not working for the new method which I have defined as a default method to an interface (not working even when not overriden) and I'm unable to find the cause of the same. Code is
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface PaymentPlanLocking {}
#Aspect
#Component
public class PaymentPlanLockAspect
{
..
#Around("#annotation(PaymentPlanLocking)")
public Object paymentPlanLocking(ProceedingJoinPoint joinPoint) throws Throwable
{
..
public interface PaymentOrchestratorService<RQ, RS>
{
/**
* #param request to validate
*/
void validate(RQ request) throws PaymentServiceException;
/**
* #param request to execute
* #return response
*/
RS execute(RQ request) throws PaymentServiceException;
/**
* #param request to execute
* #return response
*/
default RS doExecute(RQ request) throws PaymentServiceException{
throw new RuntimeException("please override this method in subclass if using old model with execute-wrapped");
}
}
#Service("holdPaymentService")
public class HoldPaymentOrchestrationService extends AbstractService<HoldResponse, HoldRequest>
implements PaymentOrchestratorService<HoldRequest, HoldResponse>
{
...
#PaymentPlanLocking
#Override
public HoldResponse execute(HoldRequest holdRequest) throws PaymentServiceException
#PaymentPlanLocking
#Override
public HoldResponse doExecute(HoldRequest holdRequest) throws PaymentServiceException
Interception working for execute(HoldRequest holdRequest) but not for doExecute(HoldRequest holdRequest). Please help me with the fix for this.

This works flawlessly for me. The only explanation why doExecute(..) interception is not working for you is that you use self-invocation, e.g. like this:
#PaymentPlanLocking
#Override
public HoldResponse execute(HoldRequest holdRequest) throws PaymentServiceException {
return doExecute(holdRequest);
}
#PaymentPlanLocking
#Override
public HoldResponse doExecute(HoldRequest holdRequest) throws PaymentServiceException {
return new HoldResponse();
}
It is a classical Spring AOP beginner's mistake to assume that this is working, even though it is clearly documented otherwise (search for the term "self-invocation").
So the problem was not in the code you showed in your question, but it is in the code you chose to hide from us. Please be advised to learn why an MCVE in every question is so important and ask better next time. Thank you.

Related

Quarkus Annotation-Based Interceptor with Non-Optional Arguments

This seems to be a hot topic based on the amount of questions asked but I have not found the answer I am looking for just yet. I want to implement a simple authorization service in my Quarkus app, but I seem to be repeating code over and over again.
Basically, I take in the JWT from the Authorization Http header and check if the role supplied in it is sufficient to access my endpoint:
public void someApiCall(#Context HttpHeaders headers) {
authService.validate(ApiToken.SOME_API_CALL, headers); // Throws an exception when unauthorized
//…
}
Now, I think this looks really clunky and I do not like the additional parameter that I need for every single Http endpoint. I have done some research into AOP and know how to add an interceptor which could validate the Http headers through an annotation which would be applied to my method:
#Authorize
public void someApiCall(/*…*/) { /*…*/ }
The issue is, I do not know how to pass in arguments into this annotation to specify the required role. I want something like this:
#Authorize(UserRole.SYSADMIN)
This seems pretty simple but I cannot figure it out. Below you will find the interceptor and annotation classes (Missing the required role of course):
Authorize.java
#Retention(value=RUNTIME)
#Target(value=METHOD)
public #interface Authorize {}
AuthorizeInterceptor.java
#Interceptor
#Priority(3000)
#Authorize
public class AuthorizeInterceptor {
#Inject
AuthorizationService authService;
#AroundInvoke
public void validateRole(InvokationContext ctx) {
authService.validate(ApiToken.ALL, ((RestEndpoint)ctx.getTarget()).getHttpHeaders());
}
}
RestEndpoint.java
public class RestEndpoint {
#Context
HttpHeaders headers;
public HttpHeaders getHttpHeaders() { return headers; }
}
SomeResource.java
public class SomeResource extends RestEndpoint {
#GET
#Authorize
public Object someApiCall() {
/* do code directly */
}
}
So, in conclusion, where I write #Authorize, I want to have #Authorize(UserRole.SOME_ROLE).
Thanks in advance!
So, I managed to figure it out. It turns out that it isn't that hard, I just didn't know where to look.
Here are the modified classes:
Authorize.java
#InterceptorBinding
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Authorize {
// Nonbinding is very important. It makes the interceptor fire regardless of the value
#Nonbinding ApiToken value();
}
AuthorizeInterceptor.java
#Interceptor
#Priority(3000)
#Authorize(ApiToken.NULL)
public class AuthorizeInterceptor {
/* fields */
public Object validate(InvokationContext ctx) throws Exception {
authService.validate(/* stays the same */);
return ctx.proceed();
}
}
SomeResource.java
public class SomeResource {
#GET
#Authorize(ApiToken.SOME_API_CALL)
public Object someApiCall() { /* implementation */ }
}
As Turing85 pointed out, a similar API already exists in JavaEE which implements the authorization functionality in the same way.

Transactional rollback not working when called from Controller class but works when called from Test Class

First of all, I've been looking for an answer to this but I still can't find an answer even after 5 hours of of searching.
Summary: My Transactional class does not rollback when it is called from my Controller class but when it is called in my JUnit test class, it executes the rollback.
Controller
#RestController
public class NotifyServerController extends BaseController {
...
#RequestMapping(value = PathConstants.PATH_NOTIFY_SERVER, method = RequestMethod.POST)
public WiremoRoot notifyServer(// parameters //) throws SQLException {
... authentication and other if statements
// State monitoring request (control box information)
else if(content instanceof SpecificRequest){
response.getContents().setContent(serviceObj.processRequest(request));
} else
throw new InputErrorException();
return response;
}
}
ServiceObjectImplementation
#Service(// Qualifier //)
#Transactional
public class ServiceClassImplementation implements ServiceClass {
// -- Dao objects here
/**
* {#inheritDoc}
*/
public Response processRequest(// parameters //) throws SQLException {
...
methodA();
}
public void methodA(){
...
throw new RuntimeException();
}
...
}
Please note that it successfully rolls back when it is called in my Test class. It just doesn't work when called from RestController.
Apparently, my tx-annotation statement was inside my db-context.xml that's why it was not working in the actual build but was working in my test class because in my test class, I load specifically the my configurations. So I put my tx-annotation statement in my dispatcherServlet(servlet-context.xml) and it worked.

How and where validate ownership of resource in spring boot rest delete endpoint

I am writing simple spring boot rest application and i come up with little architecture problem while creating DELETE endpoint. I have try way to solve this problem and I need advice which one is better and why.
First of all, I have class annoted with #ControllerAdvice which contains exception handlers:
#ExceptionHandler(ResourceNotFoundException.class)
public ResponseEntity<String> handleResourceNotFoundException(ResourceNotFoundException ex) {
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(ex.getMessage());
}
#ExceptionHandler(ResourceNotAccessException.class)
public ResponseEntity<String> handleResourceNotAccessException(ResourceNotAccessException ex) {
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED).body(ex.getMessage());
}
So i want to create endpoint :
#DeleteMapping("/{offerId}")
public void deleteOffer(#PathVariable Long offerId, Authentication authentication) {
// here code to delete offer.
}
So the main assumption is that logged user can delete offer which id is equals to offeId and he is owner of that offer (ownership i can check using code : offer.getOwner().getUsername().equals(authentication.getName()); ) or has Role.ADMIN
I figure out three ways to do this:
First
All logic in controller and only simple method
#DeleteMapping("/{offerId}")
public void deleteOffer(#PathVariable Long offerId, Authentication authentication) {
AbstractOffer offer = offerService.findOfferById(offerId).orElseThrow(() -> new ResourceNotFoundException("Offer with given Id doesn't exists."));
if (!offerService.isOwner(offer,authentication)) throw new ResourceNotAccessException("Cannot delete offer if you aren't owner");
offerService.deleteOffer(offer);
}
Second
Logic to remove this resource put into either PermissionEvaluator or validate using SPeL and annotation #PreAuthorize(...) In this case i am not throwing any exception and there is not called ControllerAdvice
`
Third
Put all logic into service class and from controller only call this method.
#Service
public class OfferService {
.
.
.
public void deleteOfferIfOwner(Long offerId,Authentication authentication) {
AbstractOffer offer = findOfferById(offerId).orElseThrow(() -> new ResourceNotFoundException("Offer with given Id doesn't exists."));
if(!isOwner(offer,authentication)) throw new ResourceNotAccessException("Cannot delete offer if you aren't owner");
deleteOffer(offer);
}
.
.
}
Then:
#DeleteMapping("/{offerId}")
public void deleteOffer(#PathVariable Long offerId, Authentication authentication) {
offerService.deleteOfferIfOwner(offerId,authentication);
}
Summary
In my opinion first solution is good because exception are connected to controller and are translated to response but i am not sure how much logic should i put into controller and how in this scenario validate if Authentication is admin.
The second one - not sure what to say, for me it is too complicated and i had to inject service class to validation classes or annotation (not sure if it is good practice) but it should work.
Third one - I am putting all logic into service and throwing 2 exception which are RUNTIME EXCEPTION and are connected to controller and controllerAdvice because on those exception depends result of calling endpoint.
I would be glad for some tips how to proper solved this and any feedback about code and project architecture.
Most of the current code is available here:
Github
The way I am handling this in my apps resembles both your 1st and 3rd way but with a little twist.
More specifically I use a custom-made class ResourceAccessHelper and put inside all the code that validates user access rights to a resource or throws an exception if the user does not meet the criteria required.
/**
* Helper class for checking whether a user has certain access rights to a resource during an HTTP
* request.
*
* #author Thanos Psaridis
*/
#Component
public class ResourceAccessHelper {
private final RolesHelper rolesHelper;
/**
* Constructor accepting {#link RolesHelper}
*
* #param rolesHelper Helper class for checking user Roles
*/
#Autowired
public ResourceAccessHelper(RolesHelper rolesHelper) {
this.rolesHelper = rolesHelper;
}
/**
* Getter for {#link RolesHelper}
*
* #return {#link RolesHelper}
*/
public RolesHelper getRolesHelper() {
return rolesHelper;
}
/**
* Verifies that the given parameters are true by Checking user access by course id
*
* #param userEmail user email
* #param courseId the course id the user may have access to
* #throws AccessDeniedException in case user does not have access to this course id
*/
public final void checkAccess(#NotNull final String userEmail, long courseId)
throws AccessDeniedException {
checkCourseDaoAccess(userEmail, courseId);
}
/**
* Custom logic for checking whether a user can pass certain constraints given a courseId.
*
* #param userEmail user email
* #param courseId the course id the user may have access to
* #throws AccessDeniedException in case the access requirements are not met.
*/
private void checkCourseDaoAccess(#NotNull String userEmail, long courseId)
throws AccessDeniedException {
if (isSuperAdmin(userEmail)) return;
final AppUser appUser = rolesHelper.getUser(userEmail);
if (isAdmin(userEmail)) {
final List<Course> tenantCourses = appUser.getTenant().getCourses();
final Optional<Course> optional =
tenantCourses.stream().filter(course -> course.getId().equals(courseId)).findAny();
if (optional.isPresent()) return;
}
if (isStudent(userEmail)
&& appUser
.getEnrolments()
.stream()
.anyMatch(enrolment -> enrolment.getCourse().getId().equals(courseId))) return;
throw new AccessDeniedException("ACCESS DENIED");
}
}
Then I would call the above class followed by your service class that deletes the resource in the controller by wrapping those in a try-catch block like so.
try {
// check access beforehand
resourceAccessHelper.checkAccess(principalObject.getEmail(), givenCourseId);
//proceed with the deletion of course.
courseService.deleteCourse(givenCourseId)
} catch (AccessDeniedException ignored) {
//handle your exception here by sending a 404 response error or something similar
}

Spring-AOP pointcut not working?

Below is my code snippet:
ServiceImpl.java
#Service
public class ServiceImpl implements Service {
private Response worker(Audit send) throws ArgumentException {
System.out.println("STEP_1");
worker(send.getRequest(), send.getId());
}
private Response worker(Request request, String id) throws ArgumentException {
System.out.println("STEP_2");
try {
//throwing some exception
} catch (Exception e) {
System.out.println("STEP_3");
}
}
}
Now, what I want is whenever NullPointerException is being thrown from method worker(Request request, String id) as shown above I want to perform some specific task. For that I have written an Aspect class which is following:
MyAspect.java
#Aspect
#Component
public class MyAspect{
#Pointcut("com.xyz.myapp.ServiceImpl.worker() && args(request,..)")
private void someOperation(Request request) {}
#Before("someOperation(request)")
public void process(Request request) {
System.out.println("SUCCESS");
}
#AfterThrowing("com.xyz.myapp.ServiceImpl.worker() && args(request,..)")
public void doRecoveryActions() {
System.out.println("EXCEPTION_SUCCESS");
}
}
Current Output:
STEP_1
STEP_2
STEP_3
Desired Output:
STEP_1
STEP_2
STEP_3
SUCCESS
EXCEPTION_SUCCESS
As you can see MyAspect.java is not getting triggered hence NOT printing values.
What can be the reason for this?
Note:
I tried making worker as public classes too but it didn't work.
Also tried changing the name of the methods to eliminate any overloading issue that too didn't work.
Tried various other pointcut expressions all in vain as of now.
In my application there are other aspect classes working absolutely fine.
You made a typical Spring AOP beginner's mistake: You assume that it works for private methods, but as the documentation clearly says, it does not. Spring AOP is based on dynamic proxies, and those only work for public methods when implementing interfaces via JDK proxies and additionally for protected and package-scoped methods when using CGLIB proxies.
You should make the worker() method public if you want to intercept it from an aspect.
P.S.: Full-fledged AspectJ also works for private methods, but to switch to another AOP framework would be overkill here.
Update: You also have other problems in your code:
The first worker method, even if you make it public, does not return anything. The last statement should be return worker(send.getRequest(), send.getId());, not just worker(send.getRequest(), send.getId());.
Your pointcut com.xyz.myapp.ServiceImpl.worker() will never match because it has an empty argument list, but your method has arguments. The args() does not help you here.
The syntax of your pointcut is also wrong because it does not specify a return type for the method, not even *. Furthermore, the method name itself is not enough, it should be enclosed in an actual pointcut type such as execution(). I.e. you want to write something like:
#Pointcut("execution(* com.xyz.myapp.ServiceImpl.worker(..)) && args(request, ..)")
private void someOperation(Request request) {}
To intercept a method that throws an exception you can use this code (works only if methods are public):
#AfterThrowing(pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",throwing="ex")
public void doRecoveryActions(NullPointerException ex) {
// ...
}
Source: Spring AOP

Extending RequestMappingHandlerMapping to register alternate URL patterns

The web applications that we write require bilingual urls. We previously used "plain Spring/Spring MVC", but we've started transitioning to Spring boot.
In our older Spring 3.x/4.x applications, we solved this problem by unwinding most of the "autoconfig" in order to override the getMappingPathPatterns to include a translated version of the URL. For example:
#Override
protected Set<String> getMappingPathPatterns(RequestMappingInfo info) {
Set<String> unilingualPatterns = super.getMappingPathPatterns(info);
Set<String> bilingualPatterns = new HashSet<String>();
for (String pattern : unilingualPatterns) {
bilingualPatterns.add(pattern);
// Create the French and English URLs.
// "resolver" translates the english URL to French.
final String fraURL = resolver.convertUrl(pattern, Locale.CANADA_FRENCH);
if (!pattern.equals(fraURL)) {
bilingualPatterns.add(fraURL);
}
}
return bilingualPatterns;
}
This way, a controller that has a method like this:
#RequestMapping(value = "/home")
public String homeView() {
return "home";
}
Would automatically have a 2nd mapping of "/accueil". In essence it would be as if the method were actually annotated like this:
#RequestMapping(value = {"/home", "/accueil"})
public String homeView() {
return "home";
}
As I mentioned above, this required unwinding a lot of the "auto config", which made for a much more complicated setup.
We're starting to use Spring Boot for new projects, and one of our goals is to reduce the complexity of our configuration. I was hoping to find a cleaner way to include this functionality.
I've tried creating my own RequestMappingHandlerMapping bean in a Configuration class, but it doesn't get used. In fact, it seems Spring doesn't really want you to override this class anyhow.
After that, I tried getting a hold of the RequestMappingHandlerMapping that Spring Boot creates, iterating through the list of handlerMethods, and adding new ones for translated PatternsRequestCondition, but the map that I get back from RequestMappingHandlerMapping.getHandlerMethods() is unmodifiable, so that's a dead end as well.
I feel like I'm going down the wrong path anyhow. I've found that when using Spring, if my solution is getting complicated (like this one is), then I'm doing it wrong and I just need to find the "easy way".
If anybody has any idea of how I can manually add add new HandlerMethods (basically duplicating the existing ones, but with translated URL patterns), that would be fantastic.
Thanks in advance.
After trying a number of different solutions, the one that seemed the "most standard" was to use a single properties file for URLs. This is how I accomplished this with Spring Boot.
First, I created a new properties file called urls.properties under scr/main/resources that would contain the urls for my application, for example:
url.home=/home
url.start=/start
I added a #PropertySource annotation to my WebConfig configuration file:
/**
* Web configuration.
*
* Since 1.0.0
*/
#Configuration
#PropertySource("classpath:i18n/urls.properties")
public class WebConfig extends WebMvcConfigurerAdapter {
/**
* Locale resolver.
*
* #return <code>LocaleResolver</code>
*/
#Bean
public LocaleResolver localeResolver() {
SessionLocaleResolver localeResolver = new SessionLocaleResolver();
localeResolver.setDefaultLocale(Locale.CANADA);
return localeResolver;
}
/**
* Locale change interceptor.
*
* #return <code>LocaleChangeInterceptor</code>
*/
#Bean
public LocaleChangeInterceptor localeChangeInterceptor() {
LocaleChangeInterceptor localeChangeInterceptor = new LocaleChangeInterceptor();
localeChangeInterceptor.setParamName("lang");
return localeChangeInterceptor;
}
/** {#inheritDoc} */
#Override
public void addInterceptors(InterceptorRegistry registry) {
super.addInterceptors(registry);
registry.addInterceptor(localeChangeInterceptor());
}
}
This gives me access to these properties in my controllers.
/**
* Demo API controller.
*
* #since 1.0.0
*/
#Controller
#RequestMapping("${url.home}")
public class DemoController {
/**
* Demo GET method binding.
*
* #return <code>String</code>
*/
#RequestMapping(method = RequestMethod.GET)
public String getHome() {
return "home";
}
}
And made sure the urls.properties properties file was listed as a property source in my application.properties file.
spring.messages.basename=messages,urls
This gives me access to these urls in my Thymeleaf templates. For example:
<a th:href="#{#{url.home}}">Home</a>
Having the #PropertySource annotation also provides easy access to the urls in test cases, for example:
/**
* DemoController test class.
*
* #since 1.0.0
*/
public class DemoControllerTest extends AbstractMockMvcTest {
#Value("${url.home}")
private String urlHome;
/**
* Test get username.
*
* #throws Exception Exception
*/
#Test
public void getUsername() throws Exception {
// Request.
resultActions = mockMvc.perform(get(urlHome));
// Verify response.
resultActions.andExpect(status().isOk());
}
}
Now, when it's time to translate the urls, just the urls.properties file needs to be sent off for translation. It's important to note that this will still just be a single file (no _en and _fr variants), so the file will end up looking like:
url.home=/home-accueil
url.start=/start-debut
I hope this helps somebody else. It was a pain to figure out the best way to make this work.
If somebody else comes up with a better way to do this (that doesn't require hacking Spring too much), please let me know!
Thanks.

Categories

Resources