Idea

 

In this document I want to demonstrate a way to add customizations to Spring beans without overlaying/overriding the bean definition. This approach follows Kevin.Conaway's ideas of modifying or adding functionality in an unobtrusive and repeatable (i.e. "multiple plugins or extensions can apply the same pattern to the same page without overriding or canceling one another", Freemarker Mixins) manner. These ideas and applicable solutions are described in these documents:

  • Freemarker Mixins: This article is focused on applying an unobtrusive and repeatable customization style on Struts action mappings and views (i.e. freemarker templates).
  • How To: Add Struts Interceptors at Runtime: This document describes how to add a custom Struts interceptor at runtime without extending the respective Action class in Jive or modifying (i.e. overlaying or overriding) the Struts action mapping.
  • Modifying Prototype Spring Beans: While the first two documents are about customizing the Struts Actions and UI templates, this article describes how to modify Spring beans with prototype scope (that means no singletons) without overriding the bean definition in a spring-*.xml file in a plugin.

After reading these three articles I wanted to find a way to customize some core Jive functionality without extending the Jive core classes or overriding the bean definitions. My first thought was to apply such customizations in an aspect-oriented (AOP) style, as Spring offers a lot of possibilities here. I spent a few minutes on trying to apply some advices via AspectJ-style by annotations and namespace/schema configuration, but neither worked: when using annotation configuration my IDE (Eclipse with AJDT) showed up compilation errors - I guess because the compile-time-weaver probably has to weave classes outside of the project; I don't know why the schema-based configuration did not work (perhaps I will investigate on this some time), so I switched to the "auto-proxying" approach, which did finally work, but I had to work around a few issues that came up with Jive (using 5.0.3).

 

Basics on Aspect-oriented Programming and Proxies

 

Most experienced Java (and especially Spring) developers will probably know the basics about aspect-oriented programming and proxying Java objects, but here is a brief introduction:

By most definitions out there, aspect-oriented programming is a programming paradigm that offers a modular approach to implement cross-cutting-concerns in object-oriented applications. Usually, a cross-cutting-concern is some technical or infrastructural functionality, that has to be applied to a lot of components in the application, like logging or transaction management. In terms of aspect-oriented programming, the cross-cutting-concern is the Aspect. The aspect is implemented in an Advice, which is often an Interceptor. An advice is applied to a Join Point, which is the point in the application where the aspect should be executed. Most AOP frameworks offer the concept of Pointcuts, which are expressions to define join points. Although I do not really want to implement a concern that is cross-cutting (I want to apply additional logic to specific beans), the general requirements and conditions for implementing plugins in Jive make AOP a nice way to implement additional functionality in a modular, unobtrusive (depends on implementation) and repeatable way.

 

In Spring, aspect-oriented programming is implemented through Proxies. A Proxy object is an object that encapsulates or wraps the target object (i.e. the actual bean object) and offers the same programming interface as the target class. By applying a proxy to a target object and using the proxy object as bean instance, all method calls to the target object (the join point of the bean) can be intercepted, which means that you can apply code or logic (the aspect) before and after the actual method calls happens. This additional code is implemented in a MethodInterceptor (the advice). Spring offers two ways for creating proxy objects:

  • JDK Dynamic Proxy API: included in Java, enables you to create proxies with defined interfaces. As these interfaces have to be Java Interfaces, you can only use this mechanism if you develop consequently against interfaces.
  • CGLib: third-party-library (included in Jive), that enables you to create proxies for Java classes. So you can use this mechanism if you develop against classes instead of interfaces (of course you can still develop against interfaces, too).

 

Implementing the MethodInterceptor

 

At first you have to implement the aspect as advice, which is a MethodInterceptor in this case. The MethodInterceptor is applied to all method calls (i.e. all join points of the bean), so in most use cases you will probably have to perform additional checks (like method names/signatures of the MethodInvocation) for executing your aspect's code. In my example use case, I want to perform additional checks after a user is authenticated by the "daoAuthenticationProvider" bean.

 

public class AuthenticationProviderInterceptor implements MethodInterceptor {

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        final Object result = invocation.proceed();
        if ("authenticate".equals(invocation.getMethod().getName())) {
            // perform additional checks
        }
        return result;
    }
}



 

Bean definition:

<bean id="mfAuthenticationProviderInterceptor" class="de.metafinanz.jive.security.AuthenticationProviderInterceptor" />



 

Proxying with BeanPostProcessors

 

A common way in Spring to define proxy beans is to use a ProxyFactoryBean that enables you to create proxies for a target object with defined interfaces to expose and interceptors to apply. If you want to use a proxy instance of this FactoryBean, you would have to use the ProxyFactoryBean's name to refer to it. But as we cannot (or do not want to) change all bean definitions that use or reference the target bean, a different approach is required. Luckily, Spring offers the concept of BeanPostProcessors. A BeanPostProcessor is a Spring construct to hook into the creation process of bean instances that allows you to modify and even replace instances before they are injected into other beans. If a BeanPostProcessor is defined as Spring bean and the application uses an ApplicationContext, the processors are registered automatically. For applying proxies to beans, Spring even offers an implementation of a BeanPostProcessor: BeanNameAutoProxyCreator. Configured with bean names for target objects (property "beanNames") and interceptors (property "interceptorNames"), this class automatically creates proxies according to the supplied configuration. One important setting is the "proxyTargetClass" boolean-property (default: false). If it's set to true, Spring will use the CGLib mechanism to create proxies.

 

Unfortunately, I got some errors in Jive when I used the BeanNameAutoProxyCreator (caused by a call to beanFactory.getAliases(beanName)). I did not look deeper into this issue - instead I created a similar class that only checks for the bean name and skips the check for aliases:

 

public class BeanNameOnlyAutoProxyCreator extends AbstractAutoProxyCreator {

    private static final Logger LOG = Logger.getLogger(BeanNameOnlyAutoProxyCreator.class);

    private List<String> beanNames;

    public void setBeanNames(String[] beanNames) {
        this.beanNames = new ArrayList<String>(beanNames.length);
        for (String mappedName : beanNames) {
            this.beanNames.add(StringUtils.trimWhitespace(mappedName));
        }
    }

    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String beanName, TargetSource targetSource) {
        for (String matchedName : beanNames) {
            if (isMatch(beanName, matchedName)) {
                LOG.info("Proxying bean " + beanName);
                return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
            }
        }
        return DO_NOT_PROXY;
    }

    protected boolean isMatch(String beanName, String mappedName) {
        return PatternMatchUtils.simpleMatch(mappedName, beanName);
    }
}



 

Bean definition:

<bean id="mfAuthProviderProxyCreator" class="de.metafinanz.jive.security.BeanNameOnlyAutoProxyCreator">
    <property name="beanNames">
        <list>
            <value>daoAuthenticationProvider</value>
        </list>
    </property>
    <property name="interceptorNames">
        <list>
            <value>mfAuthenticationProviderInterceptor</value>
        </list>
    </property>
</bean>



 

Registering BeanPostProcessors in Jive

 

As I mentioned before, BeanPostProcessors are automatically registered if an ApplicationContext is used. As Jive does use an ApplicationContext, the proxying should already work after defining the AutoProxyCreator. Unfortunately, there is a class com.jivesoftware.base.event.v2.AutoEventListenerRegistrar, which is a BeanFactoryPostProcessor. BeanFactoryPostProcessors do - as the name indicates - post-process bean factories and are registered and executed before BeanPostProcessors (see org.springframework.context.support.AbstractApplicationContext.refresh()). The AutoEventListenerRegistrar performs a lookup for beans of type com.jivesoftware.base.event.v2.EventSource to inject the EventDispatcher. By doing this, the beans will be instantiated and initialized (I don't know if this applies to all beans or only the resolved references, but all *Manager classes implement EventSource and these classes have a lot of dependencies). As the BeanPostProcessor is registered afterwards and the target objects are already created, the post processing is not executed for these instances. To work around this issue, it is possible to register the BeanPostProcessor manually. I did this by defining a bean that implements the interface BeanDefinitionRegistryPostProcessor. BeanDefinitionRegistryPostProcessors are also registered and executed automatically, but before the BeanFactoryPostProcessors (see org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory)). In this bean, we can register the BeanPostProcessor.

 

public class BeanPostProcessorsSetup implements BeanDefinitionRegistryPostProcessor {

    private static final Logger LOG = Logger.getLogger(BeanPostProcessor.class);

    private List<BeanPostProcessor> beanPostProcessors;

    @Required
    public void setBeanPostProcessors(List<BeanPostProcessor> beanPostProcessors) {
        this.beanPostProcessors = beanPostProcessors;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory bf) throws BeansException {
        for (BeanPostProcessor bpp : this.beanPostProcessors) {
            LOG.info("Registering " + bpp.getClass().getName());
            bf.addBeanPostProcessor(bpp);
        }
    }

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // nothing to to
    }
}



 

Bean definition:

<bean id="mfBeanPostProcessorsSetup" class="de.metafinanz.jive.security.proxy.BeanPostProcessorsSetup">
    <property name="beanPostProcessors">
        <list>
            <ref bean="mfAuthProviderProxyCreator" />
        </list>
    </property>
</bean>



 

Summary (Pros & Cons)

 

The main reason for me to implement this approach was an additional plugin I had to develop: I needed to apply additional code after the daoAuthenticationProvider authenticates a user. I could have implemented this by extending the Jive class and overriding the bean definition, but I had already done this in another plugin (hence this was not repeatable). With this approach I am now able to add new behavior even to beans that are overridden in other plugins. I already have a use case, where a core bean is proxied by two separate plugins and it works perfectly.

 

But I think there are also two things you really have to consider before following this approach:

  • Implementation: you need to know the implementation of the target method and have answers to the following questions: What happens if I modify the method parameters? Which exceptions does the target method throw and how are they handled by callers? How is the result object used by callers and what happens if I modify the result object? If you use this approach, the aspect's code should be as unobtrusive as possible. If you modify method parameters and execution results, this is likely to affect the stability of the system and the repeatability of the approach. You also have to take other applied aspects into account, like transaction management for instance (transaction boundaries!).
  • Product upgrades and API conformity: the code of an implemented aspect/advice is not "type-safe". In the MethodInterceptor you probably evaluate the method name and signature and cast Objects to interfaces or classes. If the method name or signature changes with new versions, your advice might not work anymore or cause errors at runtime.

 

Comments and opinions on this approach are very welcome. Can you think of any drawbacks I might have missed? Has anybody done something similar yet and what are your experiences?