miércoles, 31 de enero de 2007

Evaluating Acegi

I've been asked in the official Spring forums about one of my previous posts. In particular, a Senior Member there, wanted to know a little more about my impressions with Acegi and why did I assert that Acegi has a steep learning curve. I'm going to try to answer that question here and, by the way, provide some sample code too.

Goes without saying that Acegi features are impressive and difficult if not impossible to find in other software packages. And, of course, is Open Source to boot. It offers an endless list of capabilities all of them related to securing your applications. So then...what's not to love?

Let's talk about me a little. I currently work as a Software Architect at one of the biggest companies in Spain. I've worked before for companies like IBM or Accenture. I've worked with several products and technologies but mainly Java. Saying Java in this kind of companies is the same as saying J2EE (JEE if you like it).

I'm in a project now. A project whose security features could be considered pretty standard (let's say it's not a bank). We need to secure our applications (authentication) and (dis)allow access to features based in the authenticated user permissions (authorization). Nothing really amazing here but my guess is that the gross of the projects are like this. As we are using an Application Server (Weblogic 10 will be) we have all kind of services already provided by the container. As a matter of fact I only miss two things:

  • Be able to secure Spring beans (as they are not managed by the container, see it)

  • Hide/Show widgets in a JSP depending on the user roles

The rest is provided by the specification and by Weblogic. My work is now to decide how to accomplish these tasks. When talking about security and Spring you should check Acegi first. I have already casually read the Acegi documentation so more or less I know what I'm facing. I quickly find the topics I'm interested in.

Chapter 20 of the Reference Guide shows the following code

<authz:authorize ifAllGranted="ROLE_SUPERVISOR">
  <td>
    <A HREF="del.htm?id=<c:out value="${contact.id}"/>">Del</A>
  </td>
</authz:authorize>

And Chapter 21 has the following:

public interface BankManager {
  /**
  Secure Object Implementations
  Acegi Security System for Spring 77
  * Delete something
  */
  @Secured({"ROLE_SUPERVISOR","RUN_AS_SERVER" })
  public void deleteSomething(int id);
...
}

So far so good, isn't it?. In less than an hour I know what I should use! Nothing more nothing less..or not?

Well, may be, it won't be that simple. To start using this niceties you have to take some steps first. First there are the configuration issues, second the Authorities. A quick read tells me I will probably have to plug the authentication module aswell. And forget about roles, everything here is based on the concept of Authority.

I don't go on reading now. I don't want to have multiple concepts for the same purpose. It seems I would need to switch everything to Acegi and integrate it then with Weblogic (EJB3). That seems a lot of work for a simple tag.

I'm not saying Acegi is too complex (it is but just because it solves complex problems aswell). I'm not saying it's not worth the effort (it is if you need a catch all solution). But it was complex for my needs this time. You could argue that we are already using a pretty expensive AS but then, they always seem to be available, that is, 99% of my projects are somehow web related and executed in a container (even though I understand the importance Java outside a container).

Nonetheless, I learned a couple of important things while considering Acegi as a possible solution and I would like to share at least one of them. After taking a peek to the source code I decided to create a more simple authorize tag that suits my problem (it somehow mimics Acegi behavior with JEE roles). Here are the results:

public class Authorize extends TagSupport {
   private Log log = LogFactory.getLog(Authorize.class);

   private String ifAllGranted = null;
   private String ifAnyGranted = null;
   private String ifNotGranted = null;

   public void setIfAllGranted(String ifAllGranted) {
      this.ifAllGranted = ifAllGranted;
   }

   public void setIfAnyGranted(String ifAnyGranted) {
      this.ifAnyGranted = ifAnyGranted;
   }

   public void setIfNotGranted(String ifNotGranted) {
      this.ifNotGranted = ifNotGranted;
}

   private Set parse(String roles) {
      Set setOfRoles = new HashSet();
      if (roles != null) setOfRoles.addAll(Arrays.asList(roles.split(",")));
      return setOfRoles;
   }

   private boolean inAny(HttpServletRequest request, Set roles) {
      boolean complies = false;
      for (String role : roles) complies |= request.isUserInRole(role);
      return complies;
   }

   private boolean inAll(HttpServletRequest request, Set roles) {
      boolean complies = true;
      for (String role : roles) complies &= request.isUserInRole(role);
      return complies;
   }

   public int doStartTag() throws JspException {
      HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();
      Set notGranted = parse(
         ExpressionEvaluationUtils.evaluateString(
            "ifNotGranted", ifNotGranted, pageContext));
      if ((notGranted.size() > 0) && (inAny(request, notGranted)))
         return Tag.SKIP_BODY;
      Set anyGranted = parse(
         ExpressionEvaluationUtils.evaluateString(
            "ifAnyGranted", ifAnyGranted, pageContext));
      if ((anyGranted.size() > 0) && (inAny(request, anyGranted)))
         return Tag.EVAL_BODY_INCLUDE;
      Set allGranted = parse(
         ExpressionEvaluationUtils.evaluateString(
            "ifAllGranted", ifAllGranted, pageContext));
      if ((allGranted.size() > 0) && (!inAll(request, allGranted)))
         return Tag.SKIP_BODY;
      if (anyGranted.size() > 0) return Tag.SKIP_BODY;
      return Tag.EVAL_BODY_INCLUDE;
   }
}

Remember to create a taglib aswell

<?xml version="1.0" encoding="UTF-8"?>
<taglib version="2.0" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee web-jsptaglibrary_2_0.xsd">
   <tlib-version>1.0</tlib-version>
   <short-name>security</short-name>
   <uri>/WEB-INF/security</uri>
   <tag>
      <name>authorize</name>
      <tag-class>es.internna.spring.Authorize</tag-class>
      <bodycontent>tagdependent</bodycontent>
      <attribute>
         <name>ifNotGranted</name>
         <required>false</required>
      </attribute>
      <attribute>
         <name>ifAnyGranted</name>
         <required>false</required>
      </attribute>
      <attribute>
         <name>ifAllGranted</name>
         <required>false</required>
      </attribute>
   </tag>
</taglib>

And reference it in your pages

<%@ taglib uri="WEB-INF/lib/security.jar" prefix="security" %>

But...everything I've written before could be biased and false (ignorance is a bliss) so, if you know more and can point me in the right direction and prove me wrong, please do it!! Till next time

Handy annotations for Spring MVC

As I've told you before, there's a nice project at java.net called Spring Annotations for which I've contributed some code before. In that time it was all about security, today I would like to blog a little about the web framework (Spring MVC).

In Spring the MVC framework is considered a first class citizen, that is, it's considered an integral part of the framework. In practice this means that beans from it (usually known as controllers) aren't any different than any other bean in the context. This approach gives the framework a consistent look across the different projects that compose it but it also carries a little extra overload. In particular you have to define at least three beans to have everything working: a controller (who will handle the request), a view resolver (who will determine the view) and a mapper (in charge of retrieving the correct controller for a request).

Of the above three, the mapper is specially cumbersome to declare as it has to define both how to map URLs to controllers and handler adapters. Here is an example:

<bean id="simpleUrlMapping"
class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
   <property name="interceptors">
      <list>
         <ref bean="requestInterceptor"/>
      </list>
   </property>
   <property name="mappings">
      <props>
         <prop key="/index.htm">indexController</prop>
         <prop key="/logout.htm">logoutController</prop>
         <prop key="initparamsubmit.htm">initParamSubmitController</prop>
         <prop key="paramsubmit.htm">paramSubmitController</prop>
         <prop key="initadmin.htm">initAdminController</prop>
      </props>
   </property>
</bean>

As you can see that list could grow pretty fast if the number of mappings and interceptors increases. Spring offers a built-in solution to solve this (see here) based in Jakarta Commons Attributes. This solution has some important limitations and even the Spring reference documentation recommends using it only in relatively simple MVC scenarios.

Another approach was developed in the Spring Annotations project, allowing a developer to annotate a class with @UrlMapping and directly defining the mapping (the bean should be declared in an XML later). Quite useful if your goal is to simplify the mapper definition (and it is). But it doesn't provide a valid solution for declaring interceptors. I tried to tweak it a little so this could be managed aswell. No more simpleUrlMapping beans!

Here is the resulting code step by step.

First of all, we need to define the annotations. They are pretty simple, the first two will be used to handle mappings and the third to handle interceptors. In their current incarnation, all of them can annotate just classes

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UrlMapping {
   String value();
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface UrlMappings {
   UrlMapping[] value();
}

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface RequestInterceptor{}

Once you have them defined you can use them in your controllers and interceptors that is, classes that implement the Controller interface (ie extending MultiActionController) or the HandlerInterceptorAdapter interface. Let's see an example:

@UrlMapping("/security/*")
public class SecurityController extends MultiActionController
{
   public ModelAndView doY(HttpServletRequest httpServletRequest,
     HttpServletResponse httpServletResponse) throws Exception {
       return new ModelAndView("security");
   }
}

What we try to achieve here is mapping /**/security/* to this controller (/**/ has to be defined in the web.xml file of the application when declaring the dispatcherservlet, for example, <url-pattern>/mvc/*</url-pattern>), then the MultiActionController would automatically map the method doY() to /mvc/security/doY. The @RequestInterceptor annotation use is similar.

Let's take a look at the implementation of all this:

public class AnnotationUrlHandlerMapping extends AbstractUrlHandlerMapping
  implements BeanFactoryPostProcessor {
...
   public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
      configure(beanFactory, Controller.class);
      configure(beanFactory, HandlerInterceptor.class);
      startInterceptors();
   }
}

We are using here one of the extension points provided by Spring, in this case the post processors (see here). They define some callback methods that application developers may use to extend the core functionality. In our case we want to retrieve all beans that are instances of Controller and HandlerInterceptor and apply some configuration to them.

Here is the rest of the code:

private Class loadClass(String className)
{
   try {
      return Class.forName(className);
   } catch (ClassNotFoundException cle) {
      return null;
   }
}

private void processController(String beanName, BeanDefinition beanDefinition, Object bean)
{
   Class clazz;
   if (bean != null) clazz = bean.getClass();
   else clazz = this.loadClass(beanDefinition.getBeanClassName());
   UrlMapping mapping = clazz.getAnnotation(UrlMapping.class);
   if (mapping != null) registerHandler(mapping.value(), beanName);
   UrlMappings mappings = clazz.getAnnotation(UrlMappings.class);
   if (mappings != null)
      for (UrlMapping map : mappings.value())
         registerHandler(map.value(), beanName);
}

private void processInterceptor(Object interceptorBean) {
   if (interceptorBean != null) {
      if (interceptorBean.getClass().getAnnotation(RequestInterceptor.class) != null) {
         interceptors.add(interceptorBean);
      }
   }
   else {
      if (log.isErrorEnabled()) log.error("Cannot add a non-singleton interceptor");
   }
}

private void configure(ConfigurableListableBeanFactory beanFactory, Class clazz) {
   BeanDefinition beanDefinition;
   Object bean = null;
   for (String beanName : beanFactory.getBeanNamesForType(clazz)) {
      beanDefinition = beanFactory.getBeanDefinition(beanName);
      if (beanDefinition.isSingleton()) bean = beanFactory.getBean(beanName);
      if (HandlerInterceptor.class.equals(clazz)) processInterceptor(bean);
      else processController(beanName, beanDefinition, bean);
   }
}

private void startInterceptors() {
   if (!interceptors.isEmpty()) {
      this.setInterceptors(interceptors.toArray());
      this.initInterceptors();
   }
}

There are several interesting things in the code above
  • The code that uses the beanfactory to retrieve beans and bean definitions from the context
  • The code that handles the annotations
  • The code that registers mappings
  • The code that sets and inits interceptors
Take a close look if you are not used to it as it shows a little how the entrails of Spring work.

The final code was submitted to the Spring Annotations project and I hope it can make its way to a release. If it finally doesn't, you have a pretty good base to start here.

See you next time!

Important update: Although the code above works correctly it has a nasty side effect when the controllers are proxied. To handle this case it's just better to process everything after the initialization of the context(see):

public class AnnotationUrlHandlerMapping
  extends AbstractUrlHandlerMapping implements ApplicationListener {

   private Log log = LogFactory.getLog(AnnotationUrlHandlerMapping.class);

   private Class<?> getClass (Object bean) {
      Class clazz = bean.getClass();
      if (Advised.class.isInstance(bean)) {
         Advised advised = (Advised) bean;
            clazz = advised.getTargetSource().getTargetClass();
         }
      return clazz;
   }

   private void registerController(Object bean, String beanName) {
      Class<?> clazz = this.getClass(bean);
      UrlMapping mapping = clazz.getAnnotation(UrlMapping.class);
      if (mapping != null) registerHandler(mapping.value(), beanName);
      UrlMappings mappings = clazz.getAnnotation(UrlMappings.class);
      if (mappings != null)
         for (UrlMapping map : mappings.value())
            registerHandler(map.value(), beanName);
   }

   private void configureMappings(Map beans) {
      if (beans != null) {
         for (Object beanName : beans.keySet()) {
            registerController(beans.get(beanName), (String) beanName);
         }
      }
   }

   private void configureInterceptors(Map beans) {
      if (beans != null) {
         Set<Object> interceptors = new HashSet<Object>();
         for (Object beanName : beans.keySet())
            interceptors.add(beans.get(beanName));
         if (!interceptors.isEmpty()) {
            this.setInterceptors(interceptors.toArray());
            this.initInterceptors();
         }
      }
   }

   public void onApplicationEvent(ApplicationEvent applicationEvent) {
      if (applicationEvent instanceof ContextRefreshedEvent) {
         ContextRefreshedEvent event = (ContextRefreshedEvent) applicationEvent;
         ApplicationContext context = event.getApplicationContext();
         configureMappings(context.getBeansOfType(Controller.class));
         configureInterceptors(context.getBeansOfType(HandlerInterceptor.class));
      }
   }

}

This code has been already submitted to the Spring Annotations project.

lunes, 29 de enero de 2007

Using Java EE security annotations in Spring

Important update: The final revision of the code (posted at Internna - Google Code) includes some performance optimizations. As of it, you no longer need to declare the Aspect as pertarget and the targeted beans as request scoped. Please, download the sources and look through the example provided.

In the line of my last blog entry (see it here) I'm going to write again today about something relevant to every project, in this case Security. In particular, I would like to write about securing your Spring beans in a Java EE 5 environment.

When talking about Security in Java you can (mainly) take one of two approaches, working with the standard specification or working with Acegi. Both have advantages and disadvantages.

Acegi is the standar security module of the Spring Framework. It is designed to work without the need of a container (as the rest of Spring) and it's certainly capable. If you take a look at the list of features I'm sure you will be quite impressed. Unfortunately those all those features come with a price that is, basically, a steep learning curve.

On the other hand, you have Java EE and your Application Server. They probably come with less features but they're well integrated and useful nonetheless. And they are the standard specification so you should learn them anyway (and it won't be hard). One biggest limitations this approach imposes is not being able to work outside the classes managed by the container (servlets, filters, EJB and such), this is particulary painful when using Spring singletons.

I wanted a solution that could merge the best of both worlds, the ease of use of JEE (specially the annotations) and the power of Acegi (just authentication and authorization this time). Before doing anything I tried Google..and it won't let me down. I stumbled upon a nice project at java.net, Spring Annotations. The project goals (enable the use of JEE 5 annotations to configure spring beans) differed a little from what I had in mind, but not that much really. The best of all was they had a working draft of the subset of Java Security Annotations I was looking for since version 1.0.2! I got in touch with Rodrigo Urubatan (main project developer) and we pretty easy arranged something. I could work to improve a little the existing codebase mainly focusing in adding better JEE support. The code was submitted today and I hope it will make to the next release.

I would like now to explain, step by step, how everything works so anyone can use it in a project or, even better, improve it in the future!

The skeleton of the class is an aspect (see AspectJ):

@Aspect("pertarget(@annotation(javax.annotation.security.RolesAllowed) ||
  @annotation(javax.annotation.security.PermitAll) ||
  @annotation(javax.annotation.security.DenyAll))")
public class SecurityInterceptor implements RequestHolder {

As you can see it declares a pertarget type. This is only a requisite if you try to use the interceptor as a request scoped bean, for example in Spring MVC. Otherwise it would be configured as a singleton (it will be configured as a singleton if the annotated bean is a singleton itself). The Requestholder interface just defines a method setRequest needed later (you could pass without it but the code is cleaner this way). We have then to define the points where our interceptor should be fired. Two are needed. The first one deals with @DenyAll annotated methods:


@Before("@annotation(javax.annotation.security.DenyAll)")
public void deny(JoinPoint jp) throws Throwable {
   throw new SecurityException();
}

The implementing code is simple. Stop when you invoke the method and throw an exception. Nothing will be allowed if annotated with @DenyAll. This annotation is useful if you have previously annotated a type (class or interface) with other security rules. The other fire point would be declared as:


@Before("@annotation(javax.annotation.security.RolesAllowed) ||
  @annotation(javax.annotation.security.PermitAll)")
public void checkRoles(JoinPoint jp) throws SecurityException {
   int permitted = -1;
   try {
      MethodSignature met = (MethodSignature) jp.getSignature();
      permitted = checkPermission(
         jp.getSourceLocation().getWithinType().getMethod(met.getMethod().getName(),
         met.getMethod().getParameterTypes()));
      if(permitted == -1)
         permitted = checkPermission(jp.getSourceLocation().getWithinType());
   } catch (Exception ex) {
      if (log.isDebugEnabled()) log.debug(ex);
   } finally {
      if(permitted == 0) throw new SecurityException();
   }
}

That code is a little bit more complicated but not that much really. It will stop in any method annotated with @RolesAllowed or @PermitAll. It will check first the method and (if the method is undefined) then the class, to see if it should allow the access. The access validation code follows:


private int checkPermission(AnnotatedElement targetObject) {
   int perm = -1;
   if (targetObject != null) {
      RolesAllowed rolesAllowed =
         targetObject.getAnnotation(RolesAllowed.class);
      if (rolesAllowed != null) {
         for (String role : rolesAllowed.value()) {
            if (perm != 1) perm = checkRole(role) ? 1 : 0;
         }
      } else {
         if (targetObject.getAnnotation(PermitAll.class) != null) perm = 1;
      }
   }
   return perm;
}

The flow is simple, first check it the method was annotated with @RolesAllowed. If it was then get all the roles and check if the user is member of any. Else the method has to be annotated with @PermitAll and, of course, should be allowed.

The rest of the code is trivial so I won't post it here. The last important and somewhat difficult part is to inject the beans correctly. The trick is understanding the scope of the beans and configuring it correctly. In this example, using Spring MVC, the current request (HttpServletRequest) would be injected in the interceptor:


<bean id="securedController"
  class="es.internna.spring.mvc.DefaultController" scope="request">

<bean id="secInterceptor"
  class="org.springframework.aop.scope.ScopedProxyFactoryBean">
    <property name="proxyTargetClass" value="false" />
    <property name="targetBeanName" value="secInterceptorTarget" />
</bean>

<bean id="secInterceptorTarget"
  class="es.internna.security.SecurityInterceptor" scope="request" />

<bean id="requestInterceptor"
  class="es.internna.spring.mvc.RequestInterceptor">
    <property name="requestHolder" ref="secInterceptor" />
</bean>

Although there are missing parts in the declaration above it's easy to understand. The security interceptor needs to be request scoped because it needs the current request to check for the credentials. Notice aswell that the annotation pertarget was used so it can no longer be a singleton. The fact that it is created with a ScopedProxyFactoryBean instead of <aop:scoped-proxy/> just indicates the desire to create JDK proxies (otherwise CGLIB would be used). From 2.0.3 onwards Spring will accept <aop:scoped-proxy proxy-target-class="false"/> so the factory will be easily scooped. The secured bean (in this case a MVC Controller) has to be request scoped (aswell!) because it has to match the pertarget declaration. To inject the request a HandlerInterceptor was created (see HandlerMapping). I guess this can be done by several ways but that was the easiest one I could really think of.

And that was all for today! I've left some interesenting security tips yet so expect more with time :-)

viernes, 26 de enero de 2007

Annotated logging interceptor with Spring AOP

One of the questions that arise time and again during project development is logging. As its already known a good way to handle it, is using aspects and our favorite Java framework, Spring.

If you search a little you can find several tutorials about logging and AOP but, at least in my opinion, their authors were more interested in explaining AOP than resolving the logging concern. Meanwhile I was considering a topic for this, my first blog entry..so let this be it!

As I have mentioned there already are some tutorials that can help you creating a logging interceptor (see this for example). I wanted a little twist so I decided to go with the Java5 path. Our first task then is to create an annotation that will later be used in a method (can be modifed to be applied to classes if you wish) to declare our logging needs. Take in account that, as in a JEE container, Spring can only manage and proxy beans in its context, so only methods managed by Spring directly should be annotated. No more considerations, let's do it!

@Documented
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value={ElementType.METHOD})
public @interface LogDebug
{
   Class loggerClass();
}

The code above is very simple. It just defines an annotation which accepts a parameter of type java.lang.Class. This parameter will be of use as it will determine the class that should be written in the output.

The next step should be creating the aspect. We can start creating an skeleton of the class:

@Aspect
public class LogDebugInterceptor {

   @Before("@annotation(com.indra.rfef.annotations.LogDebug)")
   public void beforeLog(JoinPoint jp) {
      ...
   }

   @AfterReturning(
      pointcut="@annotation(LogDebug)",
      returning="retVal")
   public void afterLog(JoinPoint jp, Object retVal) {
      ...
   }
}

I'm going to try to explain the above code in detail. First of all, we have annotated our class as an Aspect, this means Spring will recognize it as such and create the needed proxies. We have then annotated two methods, one with @Before and the second as @AfterReturning. With this two annotations, called Advices, we are declaring when we expect the interceptor to start working. In our case just before entering the method and just after it has finished. Another useful times are covered by @AfterThrowing (for exception handling), @After (finally) or @Around (more complex advice that can work before and after with a powerful configuration). We have set the time but not the place where this interceptor is supposed to fire. This is done defining a PointCut. Pointcuts can be complex to be defined (AspectJ Pointcuts) but not in our case where we just need to stop when you have a method annotated with our previously defined LogDebug interface. Notice that the second method defines a value to store the result of the execution.

Finally we have to complete the logging code. First we need to obtained the method that was called in runtime and was annotated with @LogDebug. As you can see the code is a little bit more complicated than it should look. This is because methods can be declared somewhere (an interface for example) and be overriden/implemented later. We are interested in the actual (called) method.

protected Method getMethod(JoinPoint jp) {
   Method invoked = null;
   try {
      MethodSignature met = (MethodSignature) jp.getSignature();
      invoked = jp.getSourceLocation().getWithinType().getMethod(
                 met.getMethod().getName(),
                 met.getMethod().getParameterTypes());
   } finally {
      return invoked;
   }
}

Then, we just need to initialize the logger

protected Log getLog(JoinPoint jp) {
   Log log = null;
   try {
      LogDebug logdebug = this.getMethod(jp).getAnnotation(LogDebug.class);
      Class clazz = logdebug.loggerClass();
      if (clazz == null) clazz = LogDebugInterceptor.class;
      log = LogFactory.getLog(clazz);
   } finally {
      if (log == null) log = LogFactory.getLog(LogDebugInterceptor.class);
   }
   return log;
}

To complete the code, here are the final bits

public void beforeLog(JoinPoint jp) {
   Log log = this.getLog(jp);
   if (log.isDebugEnabled()) {
      log.debug(this.getMethod(jp));
      for (Object o : jp.getArgs()) {
         if (o != null) {
            log.debug("PARAMETER " + o.getClass().getSimpleName());
            log.debug(o.toString());
         }
      }
   }
}

public void afterLog(JoinPoint jp, Object retVal)
{
   Log log = this.getLog(jp);
   if (log.isDebugEnabled()) log.debug(retVal);
}

The work is done by now. You only need to wire it in Spring to see the magic. Remember to declare the <aop:aspectj-autoproxy/> tag!


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

   <aop:aspectj-autoproxy proxy-target-class="false" />

   <bean id="logInterceptor" class="es.internna.spring.LogDebugInterceptor" />

</beans>

I hope this was useful to you. Expect to see me writing some more articles about Spring, AOP or EJB3 in the future.