Friday, April 6, 2012

Spring beans injection into EJBs with Snowdrop

Snowdrop is a JBoss project that automatically creates Spring context when the server is being started, scans all deployments for spring beans, and instantiates them as singletons. The advantage of Snowdrop is that the developer can inject Spring Beans in EJBs without taking care of the bean lifecycle. The important thing to mention is that this is a specific JBoss AS oriented product (I mean, it is not like Hibernate that can work on other servers).

It is recommended that the beans that will be deployed in this way are Stateless. Snowdrop will only deploy those beans which are included in the *spring.xml name pattern.

This tutorial will first of all configure JBoss with Snowdrop, and then will show an example with an explanation.

Versions that will be used:
- JBoss 5.1.0.GA
- Snowdrop 2.0.2.Final
- Spring 3.1.1-RELEASE

Part 1 - Configuring JBoss with Snowdrop


1- Download Snowdrop from here
We will choose the Deployer JBoss AS 5 without Spring implementation cause want to use another version from the one it comes with (that version comes with 3.1.0-RELEASE).
2- Unzip into $JBOSS_HOME/server/$PROFILE/deployers
3- Now if you start your server some errors will appear, because there is no spring implementation yet added to the AS
4- The following jars are needed in your $JBOSS_HOME/server/$PROFILE/lib directory (we put them there because we want to make Spring be available to the whole server instance):

spring-context-3.1.1.RELEASE.jar
spring-core-3.1.1.RELEASE.jar
spring-beans-3.1.1.RELEASE.jar
spring-asm-3.1.1.RELEASE.jar
spring-expression-3.1.1.RELEASE.jar
spring-aop-3.1.1.RELEASE.jar
aopalliance-1.0.jar
commons-logging-1.1.1.jar

Read the pom.xml configuration section to see an easy way to get those jars. Now if you start your server no errors should appear which means Snowdrop and Spring are correctly set up in the server!

Part 2 - Developing an example

In this example, we will define a simple Spring bean and a test associated that creates a Spring context and get a bean just to make sure the bean is correctly defined. Then we will create an EJB and inject a Spring bean which was previously instantiated by Snowdrop. Both have the same result (to get a bean) but in different ways.

pom.xml
<repositories>
 <repository>
  <id>springsource-repo</id>
  <name>SpringSource Repository</name>
  <url>http://repo.springsource.org/release</url>
 </repository>
 
 <repository>
  <id>maven-nuxeo</id>
  <name>Maven Nuxeo Repository</name>
  <url>https://maven.nuxeo.org/nexus/content/groups/public/</url>
 </repository>
</repositories>

<dependencies>
 <!-- Since we will use the EJB when it is deployed in the JBoss AS we use this dependency -->
 <dependency>
  <groupId>org.jboss.jbossas</groupId>
  <artifactId>jboss-as-client</artifactId>
  <version>5.1.0.GA</version>
  <type>pom</type>
  <scope>provided</scope>
 </dependency>

 <!-- This allows us to use the Spring annotation for the EJB --> 
 <dependency>
  <groupId>org.jboss.snowdrop</groupId>
  <artifactId>snowdrop-deployers</artifactId>
  <version>2.0.2.Final</version>
  <scope>provided</scope>
 </dependency>

 <!-- This takes all the spring dependencies we put in the server lib folder -->
 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>3.1.1.RELEASE</version>
  <scope>provided</scope>
 </dependency>

 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.8.2</version>
  <scope>test</scope>
 </dependency>
</dependencies>

I had to add the Maven Nuxeo Repo cause there where some dependencies being retrieved from invalid places (http://repository.jboss.org/maven2/ which is unavailable)

The following xml has the definition of the bean, with the data it will have. This one has to be under the test/resources classpath folder.

test-beans.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

 <bean id="simplePerson" class="ar.com.pabloExample.model.Person">
  <property name="id" value="3" />
  <property name="name" value="Ruben" />
 </bean>
</beans>

The Person class is a POJO with id and name attributes with getters and setters, nothing special.

The next one is similar to the previous xml but it has the description tag which is  used in the injection. This one has to be under the java/resources classpath folder because this one will be deployed inside the jar.

beans-spring.xml
<beans xmlns="http://www.springframework.org/schema/beans"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd">

 <description>BeanFactory=(MyApp)</description>

 <bean id="simplePerson" class="ar.com.pabloExample.model.Person">
  <property name="id" value="3" />
  <property name="name" value="Ruben" />
 </bean>
</beans>


This test creates a Spring context with the test-beans.xml and then asks that context for the bean.

PersonBeanTest.java
public class PersonBeanTest {
 
 private ApplicationContext applicationContext;

 @Before
 public void init() {
  applicationContext = new ClassPathXmlApplicationContext("test-beans.xml");
 }

 @Test
 public void simplePersonInstantiatonTest() {
  Person simplePerson = (Person) applicationContext.getBean("simplePerson");
  
  System.out.println("SimplePerson name: " + simplePerson.getName());
  System.out.println("SimplePerson id: " + simplePerson.getId());
 }
}

Now, to show the real power of Snowdrop, here is the EJB:

PersonServiceBean.java
@Stateless
@Interceptors(SpringLifecycleInterceptor.class)
public class PersonServiceBean implements PersonService, PersonServiceRemote {
 
 @Spring(bean = "simplePerson", jndiName = "MyApp")
 private Person simplePerson;
 
 @Override
 public void writeSimplePersonData() {

  System.out.println("Will write the name of the person that has been injected with snowdrop:");
  System.out.println("Name: " + simplePerson.getName());
  System.out.println("ID: " + simplePerson.getId());
 }
}

It can be seen that with the @Spring annotation we get the simplePerson bean defined in the beans-spring.xml file.

PersonServiceTest.java
public class PersonServiceTest {
 
 private PersonService service;

 @Before
 public void init() throws NamingException {
  InitialContext context = new InitialContext();
  service = (PersonService) context.lookup("PersonServiceBean/remote");
 }
 
 @Test
 public void simplePersonWrite() {
  service.writeSimplePersonData();
 }
}


In order to run this test you have to deploy the jar file which can be generated from this project with:
mvn clean package -Dmaven.test.skip=true
take the generated jar and deploy it in $JBOSS_HOME/server/$PROFILE/deploy and run your server so that the lookup of the EJB is successful.

Additional documentation about Snowdrop can be found in their official site

No comments:

Post a Comment