Spring version 2.5
AspectJ version 1.7
Facing issue with Pointcut expression
#Pointcut("within(com.test.service..*)")
Following is my Aspect class:
package com.test.aspect.controller;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class ControllerAspects {
#Before("publicOperationInService()")
public void log(){
System.out.println("**********anyPublicOperationInService()**********");
}
#Before("anyPublicOperation()")
public void publicOp(){
System.out.println("**********anyPublicOperation()**********");
}
#Pointcut("execution(public * *(..))")
private void anyPublicOperation() {}
#Pointcut("within(com.test.service..*)")
private void inService() {}
#Pointcut("anyPublicOperation() && inService()")
public void publicOperationInService() {}
}
Configuration is correct as I am able to execute:
#Before("anyPublicOperation()")
public void publicOp(){
System.out.println("**********anyPublicOperation()**********");
}
Thanks.
Related
I'm developing an Advice and I want to make it advise only on field sets in specific methods.
I tried cflow(pointcutForSpecificMethod()) && set(* *) pointcut expression but it picks field sets in other methods under control flow of specific methods.
Any idea?
This is not possible directly with an exact pointcut expression, but you can use if() pointcuts to dynamically determine from the stack trace or - like in this case - from the enclosing join point static part exposed by AspectJ - what the executing method is. Here is a little example and an aspect in two variants: native syntax (my preference, more elegant and less boilerplate) and annotation-style syntax:
package de.scrum_master.app;
public class Application {
private int field = 0;
public static void main(String[] args) {
Application app = new Application();
app.foo(11);
app.bar(22);
}
public void foo(int i) {
field = i;
bar(2 * i);
}
void bar(int i) {
field = i;
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
public aspect MyAspectNative {
pointcut pointcutForSpecificMethod() : execution(* foo(*));
public static boolean executingMethodMatches(JoinPoint.StaticPart staticPart) {
return staticPart.getSignature().toLongString().contains("de.scrum_master.app.Application.foo(int)");
}
before() :
cflow(pointcutForSpecificMethod()) && set(* *) &&
if(executingMethodMatches(thisEnclosingJoinPointStaticPart))
{
System.out.println(thisEnclosingJoinPointStaticPart);
System.out.println(thisJoinPoint);
}
}
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
#Aspect
public class MyAspect {
#Pointcut("execution(* foo(*))")
private static void pointcutForSpecificMethod() {}
#Pointcut("if()")
public static boolean executingMethodMatches(JoinPoint.EnclosingStaticPart staticPart) {
return staticPart.getSignature().toLongString().contains("de.scrum_master.app.Application.foo(int)");
}
#Before(
"cflow(pointcutForSpecificMethod()) && set(* *) && " +
"executingMethodMatches(thisEnclosingJoinPointStaticPart)"
)
public void beforeAdvice(JoinPoint thisJoinPoint, JoinPoint.EnclosingStaticPart thisEnclosingJoinPointStaticPart)
{
System.out.println(thisEnclosingJoinPointStaticPart);
System.out.println(thisJoinPoint);
}
}
I tried to keep the two aspects as similar as possible structurally. No matter which aspect syntax variant you choose, the output will be:
execution(void de.scrum_master.app.Application.foo(int))
set(int de.scrum_master.app.Application.field)
I am new to AspectJ and reflections What I want to achieve is something like In the below example:
A test class :
public class Sample {
Home home = new Home();
Account account = new Account();
AccountAuthentication accountAuthentication = new AccountAuthentication();
#Test
public void loginInvalidCredentials(){
home.clickOnAccount();
account.login("Admin", "secret");
accountAuthentication.waitForPage();
Assert.assertTrue(true);
}
}
I want to log Output something like this :
packageName.Sample.loginInvalidCredentials
packageName.Home.clickOnAccount();
packageName.Account.login(userName = "Admin", Password ="secret");
packageName.AccountAuthentication.waitForPage();
packageName.Assert.assertTrue(value= true);
I have accessed the name of function packageName.Sample.loginInvalidCredentials with AspectJ
#Aspect
public class AspectClass {
#Around("execution(* *(..)) && #annotation(org.testng.annotations.Test)")
public void around(ProceedingJoinPoint point) throws Throwable {
Method method = MethodSignature.class.cast(point.getSignature()).getMethod();
String methodName = method.getName();
System.out.println("Aspect called for method "+ point.getSignature().getDeclaringType().name +"."+methodName);
try {
//TODO intercept each function call inside the method without any custom anotation and get the value of parameters as well
joinPoint.proceed();
}
}
}
Thanks in advance.
I am assuming that
you use full AspectJ and not something like Spring AOP (because then the answer would not apply),
you do not want to record method calls recursively but just the ones directly called from your #Test methods. For example, if Account.login(..) would internally call Account.checkPassword(..), it should not be recorded. That would also be possible, but then the solution would look differently.
all you want to do is log test execution and not do anything else, so an #Around advice is not necessary, #Before is enough.
Dummy application classes:
package de.scrum_master.app;
public class Account {
public void login(String user, String password) {
checkPassword(password);
}
public void checkPassword(String password) {}
}
package de.scrum_master.app;
public class AccountAuthentication {
public void waitForPage() {}
}
package de.scrum_master.app;
public class Home {
public void clickOnAccount() {
doSomethingElse();
}
public void doSomethingElse() {}
}
Marker annotation:
I created this one myself because I was too lazy to set up a project with TestNG which I normally do not use.
package org.testng.annotations;
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 Test {}
Sample application:
package de.scrum_master.app;
import org.testng.annotations.Test;
public class Sample {
Home home = new Home();
Account account = new Account();
AccountAuthentication accountAuthentication = new AccountAuthentication();
#Test
public void loginInvalidCredentials() {
home.clickOnAccount();
account.login("Admin", "secret");
accountAuthentication.waitForPage();
}
public static void main(String[] args) {
new Sample().loginInvalidCredentials();
}
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class TestExecutionLogger {
#Before("execution(* *(..)) && #annotation(org.testng.annotations.Test)")
public void logTest(JoinPoint joinPoint) {
System.out.println(joinPoint);
}
#Before("call(* *(..)) && withincode(#org.testng.annotations.Test * *(..))")
public void logTestActions(JoinPoint joinPoint) {
System.out.println(" " + joinPoint);
}
}
Console log:
execution(void de.scrum_master.app.Sample.loginInvalidCredentials())
call(void de.scrum_master.app.Home.clickOnAccount())
call(void de.scrum_master.app.Account.login(String, String))
call(void de.scrum_master.app.AccountAuthentication.waitForPage())
Of course you can refine your aspect if you really think you need to strip the plain method names from the informative AspectJ log output:
#Before("execution(* *(..)) && #annotation(org.testng.annotations.Test)")
public void logTest(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature());
}
#Before("call(* *(..)) && withincode(#org.testng.annotations.Test * *(..))")
public void logTestActions(JoinPoint joinPoint) {
System.out.println(" " + joinPoint.getSignature());
}
void de.scrum_master.app.Sample.loginInvalidCredentials()
void de.scrum_master.app.Home.clickOnAccount()
void de.scrum_master.app.Account.login(String, String)
void de.scrum_master.app.AccountAuthentication.waitForPage()
You can further refine it if you deliberately want to remove the method's return type or include the actual parameter values like in your example, which is easily possible via JoinPoint.getArgs(), but I leave that up to you. Your question was about pointcuts, not about how to extract which information from joinpoints.
I have a utility class to read the environment variables.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public final class PropertyUtil {
/** The system environment. */
private static Environment environment;
public static String getConfigProp(final String key) {
return environment.getProperty(key);
}
#Autowired
public void setEnvironment(Environment environment) {
PropertyUtil.environment = environment;
}
}
And I use it in one bean while initialization. The problem is that it runs file if I deploy the war file on tomcat but if I run the same application as a spring boot application from eclipse, it does not read the environment properties and the return values are therefore null.
Any idea what could be the cause of this problem?
package com.myspringboot.controller;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public class PropertyUtil implements EnvironmentAware {
/** The system environment. */
public Environment environment;
public String getConfigProp(String key) {
return environment.getProperty(key);
}
#Override
public void setEnvironment(Environment arg0) {
environment=arg0;
}
}
#Test
public void testProperty(){
/* String driver= System.getenv().get("spring.datasource.driverClassName");
System.out.println(driver);
System.out.println(PropertyUtil.environment);*/
String str= propertyUtil.getConfigProp("spring.datasource.driverClassName");
System.out.println(str);
}
package com.myspringboot.controller;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
#Component
public final class PropertyUtil implements EnvironmentAware {
/** The system environment. */
public static Environment environment;
public static String getConfigProp(String key) {
return environment.getProperty(key);
}
#Override
public void setEnvironment(Environment arg0) {
if(environment==null){
environment=arg0;
}
}
}
I have a bean annoteded with JSR 303 annotations. I also added Spring aspect (#Around) for handling MethodConstraintViolationException. My problem is: if I execute methods with correct parameters - my aspect works (is executed - breakpoints added), but when I run methods with incorrect parameters then MethodConstraintViolationException is thrown and my aspect is not executed.
package noname.exception;
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.hibernate.validator.method.MethodConstraintViolationException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.Ordered;
import noname.service.exceptions.ValidationException;
import noname.utils.ValidationExceptionProcessor;
#Aspect
public class ExceptionAspect implements Ordered {
#Autowired
private ValidationExceptionProcessor processor;
#Pointcut(value = "execution(* noname.conversionstrategy.api.IDocumentConverter.*(..))")
public void aopDocumentConverterPointcut() {
}
#Pointcut(value = "execution(* noname.service.api.IMailMerger.*(..))")
public void aopMailMargeServicePointcut() {
}
#SuppressWarnings("deprecation")
#Around("aopDocumentConverterPointcut() || aopMailMargeServicePointcut()")
public Object exceptionsAspect(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
try {
return proceedingJoinPoint.proceed();
} catch ( Throwable e ) {
if (e instanceof MethodConstraintViolationException) {
ValidationException exp = processor.process((MethodConstraintViolationException) e);
throw exp;
} else {
throw e;
}
}
}
#Override
public int getOrder() {
return Ordered.HIGHEST_PRECEDENCE;
}
}
Intefaces (IMailMerger and IDocumentConverter) are similar:
package noname.conversionstrategy.api;
import java.util.List;
import javax.validation.constraints.NotNull;
import org.springframework.validation.annotation.Validated;
import noname.service.domain.DocumentActionInput;
import noname.service.domain.DocumentActionResult;
import noname.validator.ValidActionInput;
#Validated
public interface IDocumentConverter {
DocumentActionResult convertDocument(#NotNull(message = "DocumentActionInput must be provided") #ValidActionInput DocumentActionInput document);
List<DocumentActionResult> convertDocuments(#NotNull(message = "DocumentActionInput must be provided") #ValidActionInput List<DocumentActionInput> documents);
}
I suppose spring execute first bean validation (it is probably executed with aspect too (?) ). If this validation throws MethodConstraintViolationException then my aspect is not executed, because spring aop doesn't support catching exceptions from anoother aspect (need confirmation).
I also created test with proxy (everything with my aspect looks fine):
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = {"classpath:applicationContextTest.xml"})
public class ExceptionAspectSpringTest {
#Autowired
private IDocumentConverter documentConverter;
#Autowired
private ExceptionAspect exceptionAspect;
private IDocumentConverter proxy;
#Before
public void setUp() {
AspectJProxyFactory aspectJProxyFactory = new AspectJProxyFactory(documentConverter);
aspectJProxyFactory.addInterface(IDocumentConverter.class);
aspectJProxyFactory.addAspect(exceptionAspect);
proxy = aspectJProxyFactory.getProxy();
}
#Test( expected = ValidationException.class )
public void shouldThownValidationException() {
DocumentActionInput document = new DocumentActionInput();
proxy.convertDocument(document);
}
}
Any help appreciated
I am new to spring AOP and struggling to achieve the following.
I want to define an annotation #RequirePermission which accepts a parameter String or enum and based on that string/enum I will do some calculations in advice. So, any method defined in Controller having #RequiredPermissions should be validated first. Here is my code so far.
Aspect:
package com.myapp.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.myapp.security.RequirePermissionType;
import com.myapp.security.RequirePermission;
#Aspect
public class RequirePermissionAspect {
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controllerBean() {}
#Pointcut("execution(#com.myapp.security.RequirePermission * com.myapp.controller.*.*(..))")
public void methodPointcut() {}
#Before("controllerBean() && methodPointcut() ")
public void afterMethodInControllerClass(com.myapp.security.RequirePermissionType name) {
System.out.println("before advice..");
System.out.println("before advice.."+name.name());
}
Annotation:
package com.myapp.security;
public enum RequirePermissionType {
VIEW, MANAGE, IMPORT, SUPER;
}
#Documented
#Target(ElementType.METHOD )
#Retention(RetentionPolicy.RUNTIME)
#Inherited
public #interface RequirePermission {
/**
*
* #return
*/
RequirePermissionType name() default RequirePermissionType.VIEW ;
}
Now if use the annotation in controller method as below the code works:
#RequestMapping(value={"create"})
#RequirePermission
public String createRegion(Model model){
}
But the following does not work:
#RequestMapping(value={"create"})
#RequirePermission(name=RequirePermissionType.VIEW)
public String createRegion(Model model){
}
Any one could guide me what I am missing and how do I achieve this.
#vamslip here is the updated aspect class. Rest all stays the same.
Updated Aspect:
package com.myapp.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;
import com.myapp.security.RequirePermissionType;
import com.myapp.security.RequirePermission;
#Aspect
public class RequirePermissionAspect {
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void controllerBean() {}
#Pointcut("execution(#com.myapp.security.RequirePermission * com.myapp.controller.*.*(..)) && #annotation(name)")
public void methodPointcut(com.myapp.security.RequirePermission name) {}
#Before("controllerBean() && methodPointcut(name) ")
public void afterMethodInControllerClass(com.myapp.security.RequirePermissionType name) {
System.out.println("before advice..");
System.out.println("before advice.."+name.name());
}
}