Thursday, March 19, 2015

pointcut and wild cards

Below is the code used to explain the logics
http://cgenit.blogspot.com/2015/03/create-simple-maven-app-to-test-and.html

We have made a simple applicatioon to test and run the AOP concept.
Here we have taken the logging aspect as a cross cutting concern.
Created LoginAspect.java and made it aspect using @Aspect and written advice using
@Before("execution(public String getName())")
The two object classes we have used is Circle and Triangle, where both having simple attribute name with getter and setter.
So we need to log it whenever the getter called.
We have created ShapeService class to get the object, for better standard way to present.
AppMain.java is used to test the application.

POM.XML  this include more dependencies , more than needed

----------------------------------------------------------------------------------------

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.test</groupId>
<artifactId>AOP</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>AOP</name>
<description>Aspect Oriented Programming</description>

<properties>
<jdk.version>1.6</jdk.version>
<spring.version>3.2.8.RELEASE</spring.version>
<spring.security.version>3.2.3.RELEASE</spring.security.version>
<jstl.version>1.2</jstl.version>
<javax.servlet.version>3.1.0</javax.servlet.version>
<mysql.connector.version>5.1.30</mysql.connector.version>
</properties>

<dependencies>
<!-- Spring 3 dependencies -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>

<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- spring jdbc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>

<!-- Spring Security -->
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-web</artifactId>
<version>${spring.security.version}</version>
</dependency>

<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-config</artifactId>
<version>${spring.security.version}</version>
</dependency>

<!-- AOP -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.7.3</version>
</dependency>

<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.11</version>
</dependency>

<!-- connect to mysql -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.connector.version}</version>
</dependency>


</dependencies>
</project>
----------------------------------------------------------------------------------------

spring.xml  - bean definition
----------------------------------------------------------------------------------------
<?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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd ">

<aop:aspectj-autoproxy />

<bean name="triangle" class="org.aop.model.Triangle">
<property name="name" value="Traingle name"></property>
</bean>

<bean name="circle" class="org.aop.model.Circle">
<property name="name" value="Circle name"></property>
</bean>

<bean name="shapeService" class="org.aop.service.ShapeService"
autowire="byName"></bean>

<bean name="loginAspect" class="org.aop.aspect.LoginAspect"></bean>

</beans>
----------------------------------------------------------------------------------------

Circle.java
----------------------------------------------------------------------------------------
package org.aop.model;

public class Circle {

private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

}

----------------------------------------------------------------------------------------
Triangle.java
----------------------------------------------------------------------------------------
package org.aop.model;

public class Triangle {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

----------------------------------------------------------------------------------------
ShapeService .java
----------------------------------------------------------------------------------------
package org.aop.service;

import org.aop.model.Circle;
import org.aop.model.Triangle;

public class ShapeService {

private Circle circle;
private Triangle triangle;

public Circle getCircle() {
return circle;
}

public void setCircle(Circle circle) {
this.circle = circle;
}

public Triangle getTriangle() {
return triangle;
}

public void setTriangle(Triangle triangle) {
this.triangle = triangle;
}

}

----------------------------------------------------------------------------------------
LoginAspect .java
----------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoginAspect {

@Before("execution(public String getName())")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

}

--------------------------------------------------------------------------------------------------------------------------
AppMain .java
--------------------------------------------------------------------------------------------------------------------------
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);

System.out.println(shapeService.getTriangle().getName());
}

}

------------------------------------------------------------------------------------------------------------

output Now
===============================================================
Login advice run::get method called
Traingle name


===============================================================


1. Need to limit only for specific class / object get method.
Above aspect's advice, the action taken is executed for each get method.
Limit only for circle getmethod();

we give the path to the method inside execution()

package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoginAspect {

@Before("execution(public String org.aop.model.Circle.getName())")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

}

Output for main method
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);

System.out.println(shapeService.getCircle().getName());
}

}


Output
===============================================================
Login advice run::get method called
Circle name
===============================================================

Let's execute this for Triangle as well to get confirm
===============================================================
Traingle name
===============================================================


2. give a wild card. Execute for any getMethod

package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoginAspect {

@Before("execution(public String *get*())")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

}

-----------------
Main class
-----------------
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);

System.out.println(shapeService.getTriangle().getName());
System.out.println(shapeService.getCircle().getName());
}

}



output
===============================================================
Login advice run::get method called
Traingle name
Login advice run::get method called
Circle name

===============================================================

4. Next try with any return type and any parameter 
to test we put a parameter for getter for circle


------------------------------
package org.aop.model;

public class Circle {
private String name;

public String getName(String s) {
return name;
}

public void setName(String name) {
this.name = name;
}

}
-------------------------------------

package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoginAspect {
@Before("execution(public * *get*(*))")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

}
-------------------------------
in execution
1. first we have public - access modifier
2. *  pink star for any return type
3. * black star for any package structure and class 
4. * Next star for any method start fro get prefix
5. * Next, red star for any parameter 
--------------------------------

So the output will be - Note: For triangle get method advice / action logging not executing. It does not have parameter
===============================================================
Traingle name
Login advice run::get method called
Circle name
===============================================================

5. If we change the circle get for two parameters,
there won't be any executing advice, logging action

package org.aop.model;

public class Circle {
private String name;

public String getName(String s,String ss) {
return name;
}

public void setName(String name) {
this.name = name;
}

}

----------------------------------------------------------------------------------------------------
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);

System.out.println(shapeService.getTriangle().getName());
System.out.println(shapeService.getCircle().getName("s","ss"));
}

}
-------------------------------------------------------------------------------------

output
===============================================================
Traingle name
Circle name

===============================================================


6. How to make it for any parameter. use two dots(..) elipse instead of star (*)
Here Circle getter has three parameters

-------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoginAspect {
@Before("execution(public * *get*(..))")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

}

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
package org.aop.model;

public class Circle {
private String name;

public String getName(String s,String ss,String sss) {
return name;
}

public void setName(String name) {
this.name = name;
}

}

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);

System.out.println(shapeService.getTriangle().getName());
System.out.println(shapeService.getCircle().getName("s","ss","SSS"));
}

}

-------------------------------------------------------------------------------------

output  Note: Advice run twice
===============================================================
Login advice run::get method called
Login advice run::get method called
Traingle name
Login advice run::get method called
Login advice run::get method called
Circle name

===============================================================

7. Let's run only for Circle getter with three parameters

-------------------------------------------------------------------------------------
package org.aop.model;

public class Circle {
private String name;

public String getName(String s,String ss,String sss) {
return name;
}

public void setName(String name) {
this.name = name;
}

}

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);

System.out.println(shapeService.getTriangle().getName());
System.out.println(shapeService.getCircle().getName("s","ss","SSS"));
}

}

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoginAspect {
@Before("execution(public * *get*(*,*,*))")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}

}

-------------------------------------------------------------------------------------
output  Note: Advice only run before circle getter
===============================================================
Traingle name
Login advice run::get method called
Circle name
===============================================================


8. Have another advice with same matching expression, secondAdvice
Both advice has matching @Before("execution(public * *get*())")

-------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class LoginAspect {
@Before("execution(public * *get*())")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}
@Before("execution(public * *get*())")
public void secondAdvice() {
System.out.println("Second advice executed.");
}


}

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);

System.out.println(shapeService.getTriangle().getName());
System.out.println(shapeService.getCircle().getName());
}

}

-------------------------------------------------------------------------------------

output  Two sets for Triangle and Circle
===============================================================
Login advice run::get method called
Second advice executed.
Login advice run::get method called
Second advice executed.
Traingle name
Login advice run::get method called
Second advice executed.
Login advice run::get method called
Second advice executed.
Circle name

===============================================================

9. Pointcut
As shown in above both advice has the same method, point where method/advice executes
So that is called pointcut
we can define Pointcut and share among two Advices. 
It is easy, and resusability.

Note: We have to use a dummy method. and we are giving the name of the dummy method(poincut) to match the advice / configure the advice.

I have used allGetters() dummy method to hold the PointCut
and changed @Before expression to "allGetters"


New Aspect
-------------------------------------------------------------------------------------
package org.aop.aspect;

import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LoginAspect {
@Before("allGetters()")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}
@Before("allGetters()")
public void secondAdvice() {
System.out.println("Second advice executed.");
}
@Pointcut("execution(public * *get*())")
public void allGetters(){}


}

-------------------------------------------------------------------------------------

-------------------------------------------------------------------------------------
package org.aop.main;

import org.aop.service.ShapeService;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class AppMain {
public static void main(String[] args) {
ApplicationContext ctx = new ClassPathXmlApplicationContext(
"spring.xml");

// you need to cast if not use second parameter ShapeService.class
ShapeService shapeService = ctx.getBean("shapeService",
ShapeService.class);

System.out.println(shapeService.getTriangle().getName());
System.out.println(shapeService.getCircle().getName());
}

}

-------------------------------------------------------------------------------------

output  Two sets for Triangle and Circle
===============================================================
Login advice run::get method called
Second advice executed.
Login advice run::get method called
Second advice executed.
Traingle name
Login advice run::get method called
Second advice executed.
Login advice run::get method called
Second advice executed.
Circle name

===============================================================


10. Not define Pointcut well.
You have to define the pointcut as a method to advice
That is, it needs to have ()  after method name  "allGetters()"
If fail to define ()  will have exception

Below allGetters  does not have ()

@Before("allGetters")
public void loginAdvice() {
System.out.println("Login advice run::get method called");
}



output  Two sets for Triangle and Circle
First to get bean triangle and execute getter
at this point Pointcut comes to play
fails as not well defined
===============================================================
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'triangle' defined in class path resource [spring.xml]: Initialization of bean failed; nested exception is java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting '(' at character position 0
allGetters
^

at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:529)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:458)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:296)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:223)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:293)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:628)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:932)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:479)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:139)
at org.springframework.context.support.ClassPathXmlApplicationContext.(ClassPathXmlApplicationContext.java:83)
at org.aop.main.AppMain.main(AppMain.java:9)
Caused by: java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting '(' at character position 0
allGetters
^

at org.aspectj.weaver.tools.PointcutParser.resolvePointcutExpression(PointcutParser.java:316)
at org.aspectj.weaver.tools.PointcutParser.parsePointcutExpression(PointcutParser.java:294)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:209)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.buildPointcutExpression(AspectJExpressionPointcut.java:196)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.checkReadyToMatch(AspectJExpressionPointcut.java:185)
at org.springframework.aop.aspectj.AspectJExpressionPointcut.getClassFilter(AspectJExpressionPointcut.java:166)
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:208)
at org.springframework.aop.support.AopUtils.canApply(AopUtils.java:262)
at org.springframework.aop.support.AopUtils.findAdvisorsThatCanApply(AopUtils.java:294)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findAdvisorsThatCanApply(AbstractAdvisorAutoProxyCreator.java:118)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.findEligibleAdvisors(AbstractAdvisorAutoProxyCreator.java:88)
at org.springframework.aop.framework.autoproxy.AbstractAdvisorAutoProxyCreator.getAdvicesAndAdvisorsForBean(AbstractAdvisorAutoProxyCreator.java:69)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.wrapIfNecessary(AbstractAutoProxyCreator.java:359)
at org.springframework.aop.framework.autoproxy.AbstractAutoProxyCreator.postProcessAfterInitialization(AbstractAutoProxyCreator.java:322)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:409)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1518)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:521)
... 11 more


===============================================================



No comments:

Post a Comment