Design with Spring AOP

Exclusive offer: get 50% off this eBook here
Spring 2.5 Aspect Oriented Programming

Spring 2.5 Aspect Oriented Programming — Save 50%

Create dynamic, feature-rich, and robust enterprise applications using the Spring framework

$23.99    $12.00
by Massimiliano Dessì | February 2009 | Java Open Source Web Development

In this article by Massimiliano Dessi, we're going to examine some important design decisions to build better applications. In these design decisions, the AOP plays a significant role because it provides smart solutions to common crosscutting problems.

We will look at the following AOP design solutions:

  • Concurrency with AOP
  • Transparent caching with AOP
  • Security with AOP

Designing and implementing an enterprise Java application means not only dealing with the application core business and architecture, but also with some typical enterprise requirements.

We have to define how the application manages concurrency so that the application is robust and does not suffer too badly from an increase in the amount of requests. We have to define the caching strategies for the application because we don't want that CPU or data-intensive operations to be executed over and over.

We have to define roles and profiles, applying security policies and restricting access to application parts, because different kind of users will probably have different rights and permissions. All these issues require writing additional code that clutters our application business code and reduces its modularity and maintainability.

But we have a choice. We can design our enterprise Java application keeping AOP in mind. This will help us to concentrate on our actual business code, taking away all the infrastructure issues that can be otherwise expressed as crosscutting concerns.

This article will introduce such issues, and will show how to design and implement them with Spring 2.5 AOP support.

Concurrency with AOP

For many developers, concurrency remains a mystery.

Concurrency is the system's skill to act with several requests simultaneously, so that threads don't corrupt the state of the objects when they gain access at the same time.

A number of good books have been written on this subject, such as Concurrent Programming in Java and Java Concurrency in Practice. They deserve much attention, since concurrency is an aspect that's hard to understand, and not immediately visible to developers. Problems in the area of concurrency are hard to reproduce. However, it's important to keep concurrency in mind to assure that the application is robust regardless of the number of users it will serve.

If we don't take into account concurrency and document when and how the problems of concurrency are considered, we will build an application taking some risks by supposing that the CPU will never simultaneously schedule processes on parts of our application that are not thread-safe.

To ensure the building of robust and scalable systems, we use proper patterns: There are JDK packages just for concurrency. They are in the java.util.concurrent package, a result of JSR-166.

One of these patterns is the read-write lock pattern, of which there is the interface java.util.concurrent.locks.ReadWriteLock and implementations, one of which is ReentrantReadWriteLock.

The goal of ReadWriteLock is to allow the reading of an object from virtually endless number of threads, while only one thread at a time can modify it. In this way, the state of the object can never be corrupted because threads to reading the object's state will always read up-to-date data, and the thread modifying the state of the object in question will be able to act without the possibility of the object's state being corrupted. Another necessary skill is that the result of a thread's action can be visible to the other threads. The behavior is the same as we could have achieved using synchronized, but when using a read-write lock we are explicitly synchronizing the actions, whereas with synchronized synchronization is implicit.

Now let's see an example of ReadWriteLock on BankAccountThreadSafe object.

Before the read operation, that needs to be safe, we set the read lock. After the read operation, we release the read lock.

Before the write operation that needs to be safe, we set the write lock. After a state modification, we release the write lock.

package org.springaop.chapter.five.concurrent;

import java.util.Date;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

public final class BankAccountThreadSafe {

public BankAccountThreadSafe(Integer id) {
this.id = id;
balance = new Float(0);
startDate = new Date();
}

public BankAccountThreadSafe(Integer id, Float balance) {
this.id = id;
this.balance = balance;
startDate = new Date();
}

public BankAccountThreadSafe(Integer id, Float balance, Date start) {
this.id = id;
this.balance = balance;
this.startDate = start;
}

public boolean debitOperation(Float debit) {

wLock.lock();
try {

float balance = getBalance();

if (balance < debit) {

return false;

} else {

setBalance(balance - debit);

return true;
}
} finally {

wLock.unlock();
}
}

public void creditOperation(Float credit) {

wLock.lock();
try {
setBalance(getBalance() + credit);

} finally {

wLock.unlock();
}
}

private void setBalance(Float balance) {

wLock.lock();
try {
balance = balance;

} finally {

wLock.unlock();
}
}

public Float getBalance() {

rLock.lock();

try {
return balance;

} finally {

rLock.unlock();
}
}

public Integer getId() {
return id;
}

public Date getStartDate() {

return (Date) startDate.clone();
}

...
private Float balance;
private final Integer id;
private final Date startDate;
private final ReadWriteLock lock = new ReentrantReadWriteLock();
private final Lock rLock = lock.readLock();
private final Lock wLock = lock.writeLock();
}

BankAccountThreadSafeis a class that doesn't allow a bank account to be overdrawn (that is, have a negative balance), and it's an example of thread-safe class. The final fields are set in the constructors, hence implicitly thread-safe. The balance field, on the other hand, is managed in a thread-safe way by the setBalance,getBalance, creditOperation, and debitOperation methods.

In other words, this class is correctly programmed, concurrency-wise. The problem is that wherever we would like to have those characteristics, we have to write the same code (especially the finally block containing the lock's release).

We can solve that by writing an aspect that carries out that task for us.

  • A state modification is execution(void com.mycompany.BankAccount.set*(*))
  • A safe read is execution(* com.mycompany.BankAccount.getBalance())
  • package org.springaop.chapter.five.concurrent;

    import java.util.concurrent.locks.Lock;
    import java.util.concurrent.locks.ReadWriteLock;
    import java.util.concurrent.locks.ReentrantReadWriteLock;

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

    @Aspect
    public class BankAccountAspect {

    /*pointcuts*/


    @Pointcut("execution(* org.springaop.chapter.five.concurrent.BankAccount.getBalance())")
    public void safeRead(){}

    @Pointcut("execution(* org.springaop.chapter.five.concurrent.BankAccount.set*(*))")
    public void stateModification(){}

    @Pointcut( "execution(* org.springaop.chapter.five.concurrent.BankAccount.getId())")
    public void getId(){}

    @Pointcut("execution(* org.springaop.chapter.five.concurrent.BankAccount.getStartDate()))
    public void getStartDate(){}

    /*advices*/

    @Before("safeRead()")
    public void beforeSafeRead() {

    rLock.lock();
    }

    @After("safeRead()")
    public void afterSafeRead() {

    rLock.unlock();
    }

    @Before("stateModification()")
    public void beforeSafeWrite() {

    wLock.lock();
    }

    @After("stateModification()")
    public void afterSafeWrite() {

    wLock.unlock();
    }

    private final ReadWriteLock lock = new ReentrantReadWriteLock();
    private final Lock rLock = lock.readLock();
    private final Lock wLock = lock.writeLock();
    }

The BankAccountAspect class applies the crosscutting functionality. In this case, the functionality is calling the lock and unlock methods on the ReadLock and the WriteLock. The before methods apply the locks with the @Before annotation, while the after methods release the locks as if they were in the final block, with the @After annotation that is always executed (after-finally advice).

In this way the BankAccount class can become much easier, clearer, and briefer. It wouldn't have any perception that it can be executed in a thread-safe manner.

package org.springaop.chapter.five.concurrent; 

import java.util.Date;

public class BankAccount {

public BankAccount(Integer id) {
this.id = id;
this.balance = new Float(0);
this.startDate = new Date();
}

public BankAccount(Integer id, Float balance) {
this.id = id;
this.balance = balance;
this.startDate = new Date();
}

public BankAccount(Integer id, Float balance, Date start) {
this.id = id;
this.balance = balance;
this.startDate = start;
}

public boolean debitOperation(Float debit) {

float balance = getBalance();

if (balance < debit) {

return false;

} else {

setBalance(balance - debit);

return true;
}
}

public void creditOperation(Float credit) {

setBalance(getBalance() + credit);
}

private void setBalance(Float balance) {

this.balance = balance;
}

public Float getBalance() {

return balance;
}

public Integer getId() {

return id;
}

public Date getStartDate() {

return (Date) startDate.clone();
}

private Float balance;
private final Integer id;
private final Date startDate;
}

Another good design choice, together with the use of ReadWriteLock when necessary, is: using objects that once built are immutable, and therefore, not corruptible and can be easily shared between threads.

Transparent caching with AOP

Often, the objects that compose applications perform the same operations with the same arguments and obtain the same results. Sometimes, these operations are costly in terms of CPU usage, or may be there is a lot of I/O going on while executing those operations.

To get better results in terms of speed and resources used, it's suggested to use a cache. We can store in it the results corresponding to the methods' invocations as a key-value pair: method and arguments as key and return object as value.

Once you decide to use a cache you're just halfway. In fact, you must decide which part of the application is going to use the cache. Let's think about a web application backed by a database. Such web application usually involves Data Access Objects (DAOs), which access the relational database. Such objects are usually a bottleneck in the application as there is a lot of I/O going on. In other words, a cache can be used there.

The cache can also be used by the business layer that has already aggregated and elaborated data retrieved from repositories, or it can be used by the presentation layer putting formatted presentation templates in the cache, or even by the authentication system that keeps roles according to an authenticated username.

There are almost no limits as to how you can optimize an application and make it faster. The only price you pay is having RAM to dedicate the objects that are to be kept in memory, besides paying attention to the rules on how to manage life of the objects in cache.

After these preliminary remarks, using a cache could seem common and obvious. A cache essentially acts as a hash into which key-value pairs are put. The keys are useful to retrieve objects from the cache. Caching usually has configuration parameters that'll allow you to change its behavior.

Now let's have a look at an example with ehcache (http://ehcache.sourceforge.net). First of all let's configure it with the name methodCache so that we have at the most 1000 objects. The objects are inactive for a maximum of five minutes, with a maximum life of 10 minutes. If the objects count is over 1000, ehcache saves them on the filesystem, in java.io.tmpdir.

<ehcache>
...
<diskStore path="java.io.tmpdir"/>
...
<defaultCache

maxElementsInMemory="10000"
eternal="false"
timeToIdleSeconds="120"
timeToLiveSeconds="120"
overflowToDisk="true"
diskPersistent="false"
diskExpiryThreadIntervalSeconds="120"

/>
...
<cache name="methodCache"
maxElementsInMemory="1000"
eternal="false"
overflowToDisk="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"
/>
</ehcache>

Now let's create an CacheAspect . Let's define the cacheObject to which the ProceedingJoinPoint is passed. Let's recover an unambiguous key from the ProceedingJoinPoint with the method getCacheKey. We will use this key to put the objects in cache and to recover them.

Once we have obtained the key, we ask to cache the Element with the instruction cache.get(cacheKey). The Element has to be evaluated because it may be null if the cache didn't find an Element with the passed cacheKey.

If the Element is null, advice invokes the method proceed(), and puts in the cache the Element with the key corresponding to the invocation. Otherwise, if the Element recovered from the cache is not null, the method isn't invoked on the target class, and the value taken from the cache is given back to the caller.

package org.springaop.chapter.five.cache; 

import it.springaop.utils.Constants;
import net.sf.ehcache.Cache;
import net.sf.ehcache.Element;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;


public class CacheAspect {

public Object cacheObject(ProceedingJoinPoint pjp) throws Throwable {

Object result;
String cacheKey = getCacheKey(pjp);

Element element = (Element) cache.get(cacheKey);
logger.info(new StringBuilder("CacheAspect invoke:").append("n get:")
.append(cacheKey).append(" value:").append(element).toString());

if (element == null) {

result = pjp.proceed();

element = new Element(cacheKey, result);

cache.put(element);

logger.info(new StringBuilder("n put:").append(cacheKey).append(
" value:").append(result).toString());
}
return element.getValue();
}

public void flush() {
cache.flush();
}

private String getCacheKey(ProceedingJoinPoint pjp) {

String targetName = pjp.getTarget().getClass().getSimpleName();
String methodName = pjp.getSignature().getName();
Object[] arguments = pjp.getArgs();

StringBuilder sb = new StringBuilder();
sb.append(targetName).append(".").append(methodName);
if ((arguments != null) && (arguments.length != 0)) {
for (int i = 0; i < arguments.length; i++) {
sb.append(".").append(arguments[i]);
}
}
return sb.toString();
}

public void setCache(Cache cache) {
this.cache = cache;
}

private Cache cache;
private Logger logger = Logger.getLogger(Constants.LOG_NAME);
}

Here is applicationContext.xml

<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-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd"> ...

<bean id="rockerCacheAspect" class="org.springaop.chapter.five.cache.CacheAspect" >
<property name="cache">
<bean id="bandCache" parent="cache">
<property name="cacheName" value="methodCache" />
</bean>
</property>
</bean>

<!-- CACHE config -->

<bean id="cache" abstract="true"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager" />
</bean>

<bean id="cacheManager"
class="org.springframework.cache.ehcache.
EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:org/springaop/chapter/five/cache/ehcache.xml" />
</bean>

...
</beans>

The idea about the caching aspect is to avoid repetition in our code base and have a consistent strategy for identifying objects (for example using the hash code of an object) so as to avoid objects from ending up in the cache twice.

Employing an around advice, we can use the cache to make the method invocations give back the cached result of a previous invocation of the same method in a totally transparent way. In fact, to the methods of the classes defined in the interception rules in pointcuts will be given back the return values drawn from the cache or, if they are not present, they will be invoked and inserted in the cache. In this way, the classes and methods don't have any cognition of obtaining values retrieved from the cache.

Spring 2.5 Aspect Oriented Programming Create dynamic, feature-rich, and robust enterprise applications using the Spring framework
Published: February 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

Let's define the pointcut that intercepts the methods of the class DummyClass.

<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-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">
...

<aop:config>

<!-- Pointcuts -->
<aop:pointcut id="readOperation" expression=
"execution(* org.springaop.chapter.five.cache.DummyClass.get*(..))"
/>

<aop:pointcut id="exitOperation" expression=
"execution(void org.springaop.chapter.five.cache.DummyClass.exit())" />

<!-- Aspects -->
<aop:aspect id="dummyCacheAspect" ref="rockerCacheAspect">
<aop:around pointcut-ref="readOperation" method="cacheObject" />
<aop:after pointcut-ref="exitOperation" method="flush" />
</aop:aspect>

</aop:config>
...
</beans>


Class DummyClass used to check the cache's working:


package org.springaop.chapter.five.cache;

public class DummyClass {

public String getFooFighters(){
return "My hero";
}


public String getHives(String year){
if(year.equals("2004")){
return "Walk idiot walk !";}else{
return "Abra Cadaver";
}
}

public String getDandyWarhols(){
return "Ride";
}

public void exit(){
System.out.println("The end.");
}
}

Here is ApplicationContext.xml complete:

<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-2.5.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-2.5.xsd">

<bean id="dummy" class="org.springaop.chapter.five.cache.DummyClass"/>

<aop:config>
<!-- Pointcuts -->

<aop:pointcut id="readOperation"
expression="execution(* org.springaop.chapter.five.cache.DummyClass.get*(..))" />

<aop:pointcut id="exitOperation"
expression="execution(void org.springaop.chapter.five.cache.DummyClass.exit())" />

<!-- Aspects -->

<aop:aspect id="dummyCacheAspect" ref="rockerCacheAspect">
<aop:around pointcut-ref="readOperation" method="cacheObject" />
<aop:after pointcut-ref="exitOperation" method="flush" />
</aop:aspect>

</aop:config>

<bean id="rockerCacheAspect" class="org.springaop.chapter.five.cache.CacheAspect" >
<property name="cache">
<bean id="bandCache" parent="cache">
<property name="cacheName" value="methodCache" />
</bean>
</property>
</bean>

<!-- CACHE config -->

<bean id="cache" abstract="true"
class="org.springframework.cache.ehcache.EhCacheFactoryBean">
<property name="cacheManager" ref="cacheManager" />
</bean>

<bean id="cacheManager"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
<property name="configLocation" value="classpath:org/springaop/chapter/five/cache/ehcache.xml" />
</bean>

</beans>

ApplicationContext contains the following beans:

  1. The dummy bean, used to test the cache's working.
  2. The readOperation and exitOperation pointcuts.
  3. The dummyCacheAspect aspect, with around and after advices.
  4. The rockerCacheAspect, which is the implementation of the aspect class that contains the logic of recovery and insertion in the cache.
  5. The cache bean is an EhCacheFactoryBean.
  6. The cacheManager bean is an EhCacheManagerFactoryBean.

Here is the test class:

package org.springaop.chapter.five.cache;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class CacheTest {

public static void main(String[] args){

String[] paths = { "org/springaop/chapter/five/cache/applicationContext.xml" };


ApplicationContext ctx = new ClassPathXmlApplicationContext(paths);

DummyClass dummy = (DummyClass) ctx.getBean("dummy");
dummy.getFooFighters();
dummy.getHives("2004");
dummy.getDandyWarhols();


dummy.getFooFighters();
dummy.getHives("2004");
dummy.getDandyWarhols();


dummy.exit();
}
}

The result will be:

As we can see from the log, the first invocations of methods are always followed by put because the cache didn't contain the results of invocations.

On the second calls, the values are instead recovered by the cache with key className.method.arguments. The number of hit, the date of creation, and the last access are shown as well.

Security with AOP

Security is one of the most important elements of an application. The word "security" covers two concepts:

  • Authentication is the verification's process of a principal's identity; a principal is typically a user. A principal in order to be authenticated provides a credential that is the password.
  • Authorization, on the other hand, is the process of granting authorities, which are usually roles, to an authenticated user.

Once a user is authenticated and has roles, he or she can work on the application and perform the actions permitted by an access control list, which according to the user's roles allows certain operations.

Before Spring Security, the rules of who can do what were usually implemented using custom code and in-house framework, or using JAAS. Usually, the first type was a consequence of the second type's difficulty. Unfortunately, though custom-type security fits its purposes, it lacks in its main aim. This is because it's safer to employ a much used framework that is constantly updated and corrects security problems, rather than having an in-house framework that might be barely tested. Beside this considerations, that would be carefully take into account, that defining and applying security rules without AOP means causing code tangling and code scattering.

In fact, AOP applied to security solves most of the common practical problems concerning security. In order to solve them we use Spring Security 2.0.x  (formerly Acegi Security System for Spring), configuring it properly to carry out most of the work according to the application's needs. Now let's look just at some parts where AOP intervenes in its configuration.

For now, we will not deal with the authentications and roles attribution. Instead, we will start from the point at which the decision is taken to authorize a user and to provide him or her with roles to access a certain resource. Taking an actual decision whether or not to allow the user (based on its roles) gain access to the secure resource is the responsibility of the access decision manager.

An access decision manager implements the AccessDecisionManager interface, and in order to carry out his or her job, the manager needs a group of voters which implement the AccessDecisionVoter interface.

The AccessDecisionManagers provided by Spring are:

  • AffirmativeBased: At least one voter votes to grant access
  • ConsensusBased: A consensus of voters votes to grant access
  • UnanimousBased: All voters vote to abstain or grant access.

If none of them is specified, we employ AffirmativeBased with two voters, RoleVoter and AuthenticatedVoter. A voter can vote to grant, deny, or abstain.

  • RoleVoter: It bases its vote on role. If the user has the required role by the required resource, it votes ACCESS_GRANTED. But if the resource doesn't have a specified role, it votes ACCESS_ABSTAIN. If the resource has a role the user doesn't have, then it votes ACCESS_DENIED.
  • AuthenticatedVoter: This votes on the strength of the user's authentication. A user can be authenticated with:
    • IS_AUTHENTICATED_FULLY,
    • IS_AUTHENTICATED_REMEMBERED,
    • IS_AUTHENTICATED_ANONYMOUSLY
    • AuthenticatedVoter votes ACCESS_GRANTED if the authentication level is higher than the level requested by the resource. The highest one is IS_AUTHENTICATED_FULLY.

In the following XML, we see the declaration of the access decision manager:

<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd">
...
<bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.vote.RoleVoter" />
<bean class="org.springframework.security.vote.AuthenticatedVoter" />
</list>
</property>
</bean>

</beans>

Once we have defined the AccessDecisionManager, we can use AOP to decide the roles that are necessary to call the several beans' methods.

We can employ three strategies:

  1. Securing methods with security interceptor
  2. Securing methods with pointcuts
  3. Securing methods with annotations

In order to employ interceptors, the aspectjrt.jar and aspectjweaver.jar JARs must be in the classpath. For the annotations, there must be the JAR spring-security-core-tiger-2.0.x.jar.

Securing methods with security interceptor

With security interceptors we can define the roles necessary to execute methods on the bean.

Let’s have the interface:

public interface FooService {

public Integer getBalance(Integer idAccount);
public void setBalanceAccount(Integer id, Integer balance);
public boolean suspendAccount(Integer id);

}

FooService is implemented by FooServiceImpl, that is configured as follows :
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:security="http://www.springframework.org/schema/security"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/security
http://www.springframework.org/schema/security/spring-security-2.0.4.xsd">
...

<bean id="accessDecisionManager" class="org.springframework.security.vote.AffirmativeBased">
<property name="decisionVoters">
<list>
<bean class="org.springframework.security.vote.RoleVoter" />
<bean class="org.springframework.security.vote.AuthenticatedVoter" />
</list>
</property>
</bean>
...

<bean class="org.springaop.chapter.five.security.FooServiceImpl">
<security:intercept-methods
access-decision-manager-ref="accessDecisionManager">
<security:protect method="org.springaop.chapter.five.security.FooService.getBalance"
access="ROLE_USER" />
<security:protect
method="org.springaop.chapter.five.security.FooService.setBalanceAccount"
access="ROLE_ACCOUNTING,ROLE_ADMIN" />
<security:protect method="org.springaop.chapter.five.security.FooService.suspendAccount"
access="ROLE_ADMIN" />
</security:intercept-methods>
</bean>
...
</beans>

We have defined some roles (separated by comma) that can execute those method we want to be executed by a user that has a particular role. This choice permits to define roles on methods directly on beans, but makes the configuration files too long.

Securing methods with pointcuts

With this strategy, it's possible to define the roles required for the different pointcuts with AspectJ syntax that we define with the tag global-method-security.

We use the same rules on the same methods of the interface FooService.

<global-method-security 
access-decision-manager-ref="accessDecisionManager">
<protect-pointcut
expression="execution(* org.springaop.chapter.five.security.FooService.getBalance(..))"
access="ROLE_USER" />
<protect-pointcut
expression="execution(* org.springaop.chapter.five.security.FooService.set*(..))"
access="ROLE_ACCOUNTING,ROLE_ADMIN" />
<protect-pointcut
expression="execution(*org.springaop.chapter.five.security.FooService.suspendAccount(..))"
access="ROLE_ADMIN" />
</global-method-security>

When using pointcuts, we don't have to use interceptors if they can be in conflict with the executions of methods that we have defined in the configuration. This modality of configuration is consistent with AspectJ syntax for the definition of pointcuts, making the modality of configuration of aspects homogeneous.

Compared to interceptors, the configuration is less prolix and dispersive as it concentrates the methods that can be invoked with the different roles in one point.

Securing methods with annotations

We can use annotations to define which roles can be executed by the methods of our classes. We will use the same rules on the same methods of the interface FooService.

package org.springaop.chapter.five.security;

import org.springframework.security.annotation.Secured;

public class FooServiceImplWithAnnotations implements FooService{

@Secured("ROLE_USER")
public Integer getBalance(Integer idAccount) {
Integer result = 0;
// do something
return result;
}

@Secured( { "ROLE_ACCOUNTING", "ROLE_ADMIN" })
public void setBalanceAccount(Integer id, Integer balance) {
// do something
}

@Secured("ROLE_ADMIN")
public boolean suspendAccount(Integer id) {
boolean result = false;
// do something
return result;
}
}

With this strategy, we define the roles within the class, needed for the execution of methods. With this choice we don't have to configure any XML, but we lose the possibility of seeing the roles for the methods present defined in a single place.

Summary

In this article we've seen how to implement crosscutting functionalities, such as the concurrency control, the employment of a cache and the security management. Without AOP those functionalities would be scattered across the application, with the same code duplicated in different modules. With AOP we can have cleaner code, much more concise and easier to maintain and debug. We've seen how it is possible to implement these functionalities without having tangled or scattered code, implementing functionalities with aspects and advices.

Spring 2.5 Aspect Oriented Programming Create dynamic, feature-rich, and robust enterprise applications using the Spring framework
Published: February 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Massimiliano Dessì

Massimiliano Dessì is an experienced Java developer, who started developing JEE applications in 2000. In 2004 he discovered the Spring Framework 1.0, and since then he has been one of its most enthusiastic users.

He works as a Software Architect and Engineer for Sourcesense (http://www.sourcesense.com), one of the leading European Open-Source System Integrators. Before joining Sourcesense, he worked as software architect for CRS4 (http://www.crs4.it). He's also an active technical writer, author of various articles, publications, and reviews available on http://www.jugsardegna.org/vqwiki/jsp/Wiki?MassimilianoDessi and on http://www.slideshare.net/desmax74/slideshows.

Massimiliano is also a frequent speaker at Users Groups conferences, including, Java Users Groups, SpringFrameworkUser Group, Javaday, and Linux Users Groups. He is one of the founders of Java User Group Sardinia (https://jugsardegna.dev.java.net/ and http://www.jugsardegna.org), the founder of "Spring Framework Italian User Group", "Jetspeed Italian User Group" and "Groovy Italian User Group".

He maintains a personal weblog at: http://jroller.com/page/desmax and lives in Cagliari, Sardinia with his family.

Books From Packt

Spring Web Flow 2 Web Development
Spring Web Flow 2 Web Development

EJB 3 Developer Guide
EJB 3 Developer Guide

JBoss Tools 3 Developers Guide
JBoss Tools 3 Developers Guide

Tapestry 5: Building Web Applications
Tapestry 5: Building Web Applications

JBoss Portal Server Development
JBoss Portal Server Development

Grails 1.0 Web Application Development
Grails 1.0 Web Application Development

ZK Developer’s Guide
ZK Developer’s Guide

Apache OFBiz Development: The Beginner's Tutorial
Apache OFBiz Development: The Beginner's Tutorial

No votes yet

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
x
w
2
c
Z
b
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software