Wiring up EJBs using Spring

In my recent work, I wanted to wire up my EJB 3 Service Facades using Spring 2.5. I looked for an existing solution to get this done, and it did not turn out to be successful. So I thought of writing my own solution to overcome this using Aspect Oriented Programming (AOP).

Looking into how Spring performs its magical bean wiring, I read through the source code of Spring, and found out that Spring uses the concept of BeanConfigurer and BeanWiringInfoResolver to resolve dependencies for a POJO. Since I was interested on annotations based configuration, I read through the code and found that AnnoationBeanWiringInfoResolver is capable of doing the wiring up for any POJO, by reading through present annotations for that type.

However, since I wanted to make sure that only my EJBs will be wired up using this approach, I decided to extend the AnnotationBeanWiringInfoResolver to provide my own implementation which only does this special treatment.

Following is the code for the custom implementation of BeanWiringInfoResolver. Note that ServiceFacade annotation is a custom marker annotation which I use to mark a particular EJB implementation to be wired by Spring.


package net.springejb.demo.service.platform.annotations;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;


/**
* This annotation defines that a class (EJB) is an service facade implementation.
* This in turn enables automatic dependency resolving for that particular
* class's instances through AOP.
*
* @author Yohan Liyanage
*
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
public @interface ServiceFacade {
// Marker Annotation
}


package net.springejb.demo.service.platform.spring;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.AnnotationBeanWiringInfoResolver;
import org.springframework.beans.factory.annotation.Autowire;
import org.springframework.beans.factory.wiring.BeanWiringInfo;
import org.springframework.beans.factory.wiring.BeanWiringInfoResolver;
import org.springframework.util.Assert;

import net.springejb.demo.service.platform.annotations.ServiceFacade;

/**
* A Spring {@link BeanWiringInfoResolver}, which scans for {@link ServiceFacade}
* annotation, and wires the bean accordingly.
* <p>
* Used by {@code ServiceFacadeDIAspect} to wire up Service Facade EJBs.
*
* @author Yohan Liyanage
*
*/
public class ServiceFacadeBeanWiringInfoResolver extends AnnotationBeanWiringInfoResolver {


private static final Log log = LogFactory.getLog(ServiceFacadeBeanWiringInfoResolver.class);

@Override
public BeanWiringInfo resolveWiringInfo(Object beanInstance) {
Assert.notNull(beanInstance, "Bean instance is null !");
ServiceFacade sfAnnotation = beanInstance.getClass().getAnnotation(ServiceFacade.class);
return (sfAnnotation !=null) ? buildWiringInfo(beanInstance, sfAnnotation) : null;
}

/**
* Default Autowiring Strategy is By Type. Also, a dependency check will be requested
* after validation.
*
* @param beanInstance
* @param sfAnnotation
* @return
*/
protected BeanWiringInfo buildWiringInfo(Object beanInstance, ServiceFacade sfAnnotation) {

if (log.isDebugEnabled()) {
log.debug("Wiring up EJB Service Facade : " + beanInstance.getClass().getName());
}

// By Type, Dependency Check
return new BeanWiringInfo(Autowire.BY_TYPE.value(), true);
}
}

In order to make sure that my EJB’s will be wired up automatically, I wrote an aspect using Aspect J to run once after the constructor for that particular session bean completes. This aspect performs the wiring of Spring beans on the EJB using the our custom ServiceFacadeBeanWiringInfoResolver (see above) and perform the dependency injection. Note that this aspect calls ApplicationConfiguration.getApplicationContext() method to obtain a reference to the Spring Application Context which contains bean configurations. It’s just a singleton class which holds the reference to the Spring application context.


package net.springejb.demo.service.platform.aspects;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.wiring.BeanConfigurerSupport;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import net.springejb.demo.core.platform.config.ApplicationConfiguration;
import net.springejb.demo.service.platform.spring.ServiceFacadeBeanWiringInfoResolver;

/**
* Spring-EJB Integration Aspect enables EJBs to access Spring Managed Beans just as if they were
* first class Spring Managed beans.
* <p>
* In addition, this also allows EJBs to be wired by Spring as if they were created by
* Spring Container, automatically wiring up dependencies (through {@code Autowired}).
* <p>
* <b>Note :</b> This requires the EJB beans to be annotated with {@link ServiceFacade}
* annotation.
*
* @author Yohan Liyanage
*
*/
public aspect ServiceFacadeDIAspect {

private static Log log = LogFactory.getLog(ServiceFacadeDIAspect.class);

private static BeanConfigurerSupport configurer = new BeanConfigurerSupport();

static {
initBeanConfigurer();
}

pointcut ejbServiceFacadeConstructor() :
execution(public (@net.springejb.demo.service.platform.annotations.ServiceFacade *).new());

before() : ejbServiceFacadeConstructor() {

// Obtain EJB Reference
Object ejb = thisJoinPoint.getTarget();

if (log.isDebugEnabled()) {
log.debug("Injecting Spring Beans to EJB " + ejb.getClass().getName());
}

// Configure EJB
configurer.configureBean(ejb);

}

/**
* Initializes Spring BeanConfigurerSupport with Application Context Information.
*/
private static void initBeanConfigurer() {
try {
configurer.setBeanWiringInfoResolver(new ServiceFacadeBeanWiringInfoResolver());

ApplicationContext apCtx = ApplicationConfiguration.getApplicationContext();

if (! (apCtx instanceof ClassPathXmlApplicationContext) ) {
log.error("ApplicationContext returned by ApplicationConfiguration.getApplicationContext() is not" +
" a ClassPathXmlApplicationContext. This is required to perform DI on EJBs.");
throw new IllegalArgumentException("ApplicationContext should be a ClassPathXmlApplicationContext !");
}

// Type Cast to ClassPathXmlApplicationContext so that we can obtain
// ConfigurableListableBeanFactory.
ClassPathXmlApplicationContext cpCtx = (ClassPathXmlApplicationContext) apCtx;

// This requires a ConfigurableListableBeanFactory
configurer.setBeanFactory(cpCtx.getBeanFactory());

configurer.afterPropertiesSet();
} catch (Exception e) {
log.fatal("Unable to start SpringEJbIntegrationAspect : Unable to obtain Application Context");
throw new IllegalStateException("Unable to obtain Application Context", e);
}
}
}

Once your code is instrumented using Aspect J, this aspect will ensure that once your EJBs are created, they will be auto-magically wired up through Spring. Following code is for a sample EJB that will be wired up using this approach.


package net.springejb.demo.service.security.api.impl;

import javax.ejb.Stateless;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;

import net.springejb.demo.core.domain.User;
import net.springejb.demo.core.exceptions.DemoServiceException;
import net.springejb.demo.persistence.platform.Page;
import net.springejb.demo.service.platform.annotations.ServiceFacade;
import net.springejb.demo.service.security.api.SecurityService;
import net.springejb.demo.service.security.internal.UserService;

/**
* Security Service EJB Facade.
* <p>
* This is a SLSB that exposes services of Security Module as a Facade.
*
* @author Yohan Liyanage
*
*/
@ServiceFacade
@Stateless
public class SecurityServiceBean implements SecurityService {

private static Log log = LogFactory.getLog(SecurityServiceBean.class);

// This will be injected by Spring Container.
@Autowired private UserService userService;

public SecurityServiceBean() {
super();
}

@Override
public User findUserById(String loginId) throws DemoServiceException {
return userService.findUserById(loginId);
}

@Override
public void removeUser(User user) throws DemoServiceException {
userService.removeUser(user);
}

@Override
public void saveOrUpdateUser(User user) throws DemoServiceException {
userService.saveOrUpdateUser(user);
}


}

2 comments

  1. Hi Michel,

    I was not aware about the 'SpringBeanAutowiringInterceptor'. Thanks for pointing it out :).

    Yes, you are correct that it would lead to a much simpler solution. Unfortunately, when I initially googled for a solution for this, I was not able to find about the interceptor. May be I have used wrong keywords.

    On a side note, SpringBeanAutowiringInterceptor seems to be relying on ContextSingletonBeanFactoryLocator which may not be useful in some cases (including the scenario for which I had to write this). But obviously, there are workarounds for that (which would still be simpler than using AOP). I will be trying that out, and may be write an article on that next :).

    Thanks a lot.

  2. I don't see much difference between the BeanFactoryLocator and this:

    ApplicationContext apCtx = ApplicationConfiguration.getApplicationContext();

    An easier solution would have been to extend the interceptor and override the way the BeanFactory is obtained.

    My 2 cents.
    Pablo.

Leave a Reply

Your email address will not be published. Required fields are marked *