As a consultant, you seldom get to voice out your opinions regarding the technologies used by your customers and it’s even more extraordinary when you’re heard. My current context belongs to the usual case: I’m stuck with Java 6 running JBoss 5.1 EAP with no chance of going forward in the near future (and I consider myself happy since a year and a half ago, that was Java 5 with JOnAS 4). Sometimes, others wonder if I’m working in a museum but I see myself more as an archaeologist than a curator.
Context
I recently got my hands on an application that had to be migrated from a proprietary framework, to more perennial technologies. The application consists of one web-front office and a Swing back-office. The key difficulty was to make the Swing part communicate with the server part, since both lives in two different network zones, separated by a firewall (with some open ports for RMI and HTTP). Moreover, our Security team enforces that such communications has to be secured.
The hard choice
The following factors played a part in my architecture choice:
- My skills, I’ve plenty more experience in Spring than in EJB3
- My team skills, more oriented toward Swing
- Reusing as much as possible the existing code or at least interfaces
- Existing requirement toward web-services:
- Web services security is implemented through the reverse proxy, and its reliability is not the best I’ve ever seen (to put it mildly)
- Web services applications have to be located on dedicated infrastructure
- Mature EJB culture
- Available JAAS LoginModule for secure EJB calls and web-services from Swing
Now, it basically boils down to exposing Spring web-services on HTTP or EJB3. In the first case, cons include no experience of Spring remoting, performance (or even reliability) issues, and deployment on different servers thus more complex packaging for the dev team. In the second case, they include a slightly higher complexity (yes, EJB3 are easier than with EJB 2.1, but still), a higher ramp-up time and me not being able to properly support my team when a difficulty is met.
In the end, I decided to use Spring services in fullest, but to put them behind a EJB3 façade. That may seem strange but I think that I get the best of both world: EJB3 skills are kept at a bare minimum (transactions will be managed by Spring), while the technology gets me directly through the reverse-proxy. I’m open for suggestions and arguments toward such and such solutions given the above factors, but the quicker the better :-)
Design
To ease the design to the maximum, each Spring service will have exactly one and only one EJB3 façade, which will delegate calls to the underlying service.
Most IDEs will be able to take care of the boilerplate delegating code (hell, you can even use Project Lombok with @Delegate
- I’m considering it).
On the class level, the following class design will be used:
This is only standard EJB design, with the added Spring implementation.
On the module level, this means we will need a somewhat convoluted packaging for the service layer:
- A Business Interfaces module
- A Spring Implementations module
- An EJB3 module, including remote interfaces and session beans (thanks to the Maven EJB plugin, it will produce two different artifacts)
How-to
Finally, developing the EJB3 façade and injecting it with Spring beans is ridiculously simple.
The magic lies in the JavaEE 5 Interceptors
annotation on top of the session bean class that references the SpringBeanAutowiringInterceptor
class, that will kick in Spring injection after instantiation (as well as activation) on every referenced dependency.
The only dependency in our case is the delegate Spring bean, which as to be annotated with legacy @Autowired
.
import javax.ejb.Stateless;
import javax.interceptor.Interceptors;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.ejb.interceptor.SpringBeanAutowiringInterceptor;
import ch.frankel.blog.ejb.spring.service.client.RandomGeneratorService;
@Stateless
@Interceptors(SpringBeanAutowiringInterceptor.class)
public class RandomGeneratorBean implements RandomGeneratorService {
@Autowired
private ch.frankel.blog.ejb.spring.service.RandomGeneratorService delegate;
@Override
public int generateNumber(int lowerLimit, int upperLimit) {
return delegate.generateNumber(lowerLimit, upperLimit);
}
}
In order to work, we have to use a specific Spring configuration file, which references the Spring application context defined in our services module as well as activate annotation configuration.
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd[
<context:annotation-config />
<bean>
<constructor-arg>
<list>
<value>classpath:ejb-service.xml</value>
</list>
</constructor-arg>
</bean>
</beans>
It’s mandatory to name this file beanRefContext.xml and to make it available at the root of the EJB JAR (and of course to set the Spring service module as a dependency).
|
Conclusion
Sometimes, you have to make some interesting architectural choices that are pertinent only in your context. In these cases, it’s good to know somebody paved the road for you: this is the thing with EJB3 façade over Spring services.