Showing posts with label Spring. Show all posts
Showing posts with label Spring. Show all posts

Tuesday, May 28, 2013

Restful Generic DAO with Spring 3 + JPA + unit testing + integration testing

This time, we will create a generic DAO layer, a Service layer and a REST layer which exposes the Service layer to whoever wants to consume it. Unit tests and Integration tests will be created too. Everything is implemented with Spring 3.

Versions used:
  1. Spring 3.2.2.RELEASE
  2. JAXB
  3. Hibernate 4.2.1.Final
  4. Maven 3

pom.xml
<dependencies>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>3.2.2.RELEASE</version>
  </dependency>
  
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-orm</artifactId>
   <version>3.2.2.RELEASE</version>
  </dependency>

  <dependency>
   <groupId>org.hibernate</groupId>
   <artifactId>hibernate-entitymanager</artifactId>
   <version>4.2.1.Final</version>
  </dependency>

  <dependency>
   <groupId>hsqldb</groupId>
   <artifactId>hsqldb</artifactId>
   <version>1.8.0.10</version>
  </dependency>

  <dependency>
   <groupId>log4j</groupId>
   <artifactId>log4j</artifactId>
   <version>1.2.17</version>
  </dependency>

  <dependency>
   <groupId>junit</groupId>
   <artifactId>junit</artifactId>
   <version>4.11</version>
   <scope>test</scope>
  </dependency>
  
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>3.2.2.RELEASE</version>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
   <scope>test</scope>
  </dependency>

 </dependencies>

hsqldb will be our in-memory database. Let´s see the rest of the relevant pom info.

<!-- Jetty plugin -->
 <plugin>
  <groupId>org.mortbay.jetty</groupId>
  <artifactId>maven-jetty-plugin</artifactId>
  <version>6.1.10</version>
  <configuration>
   <scanIntervalSeconds>10</scanIntervalSeconds>
  </configuration>
 </plugin>
With the Maven Jetty Plugin you can manually run mvn jetty:run in your terminal and the server will be started. Now that the server is up, it lets you run the REST services tests (those tests are considered integration tests). Below you can find the automated way to treat integration tests just like normal unit tests are treated.

<!-- This one is to run integration tests -->
 <!-- By default runs all *IT.java tests -->
 <plugin>
  <groupId>org.apache.maven.plugins</groupId>
  <artifactId>maven-failsafe-plugin</artifactId>
  <version>2.12</version>
  <executions>
   <execution>
    <goals>
     <goal>integration-test</goal>
     <goal>verify</goal>
    </goals>
   </execution>
  </executions>
 </plugin>
Similar to running mvn test to run the unit tests, when you run mvn verify, integration tests are executed. But what if you want to run unit tests and integration tests when you compile your project? Should you run mvn test verify in that case? Well, you must tell maven to start the jetty server right after unit tests finish but before integration tests run (no, you cannot use mvn test jetty:run verify)... see below:

<!-- Responsible for starting jetty before integration tests -->
 <plugin>
  <groupId>org.codehaus.cargo</groupId>
  <artifactId>cargo-maven2-plugin</artifactId>
  <version>1.2.0</version>
  <configuration>
   <container>
    <containerId>jetty6x</containerId>
    <type>embedded</type>
   </container>
  </configuration>
   <executions>
    <execution>
     <id>start-jetty</id>
     <phase>pre-integration-test</phase>
     <goals>
     <goal>start</goal>
     </goals>
    </execution>
    <execution>
     <id>stop-jetty</id>
     <phase>post-integration-test</phase>
     <goals>
     <goal>stop</goal>
     </goals>
   </execution>
  </executions>
 </plugin>
Pretty straightforward, right? Thanks to the Cargo plugin, whenever integration tests are executed, in the pre-integration-test maven phase the jetty server will be automatically started.

web.xml
<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath*:spring-beans.xml</param-value>
 </context-param>

 <context-param>
  <param-name>log4jConfigLocation</param-name>
  <param-value>classpath*:log4j.properties</param-value>
 </context-param>
 <servlet>
  <servlet-name>applicationContext</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>
 <servlet-mapping>
  <servlet-name>applicationContext</servlet-name>
  <url-pattern>/client-crud/*</url-pattern>
 </servlet-mapping>
 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
That's a normal Spring setup.

applicationContext-servlet.xml
<!-- Enabling Spring beans auto-discovery -->
<context:component-scan base-package="ar.com.pabloExample" />
<!-- Enable the configuration of transactional behavior based on annotations -->
<tx:annotation-driven transaction-manager="txManager" />
<!-- Enabling Spring MVC configuration through annotations -->
<mvc:annotation-driven />
In that XML we configure our beans to be scanned because we will use annotations. The transaction manager is enabled to work with annotations too.

GenericDao
public interface GenericDao<T extends Serializable> {
 
 public long count();
 public T create(T t);
 public void delete(Object id);
 public T find(Object id);
 public List<T> getAll();
 public T update(T t); 
}
That's a generic DAO interface.

ClientDao
public interface ClientDao extends GenericDao<Client> {

}
For every entity we want to use the generic DAO, we must create the interface for that entity because the generic DAO is GenericDao<?> and we must tell it which is the entity it will accept, in this case GenericDao<Client>

GenericDaoImpl
public abstract class GenericDaoImpl<T extends Serializable> implements GenericDao<T> {
 
 private Class<T> type;
 
 @SuppressWarnings("unchecked")
 public GenericDaoImpl() {
  Type t = getClass().getGenericSuperclass();
  ParameterizedType pt = (ParameterizedType) t;
  type = (Class<T>) pt.getActualTypeArguments()[0];
 }
 
 @PersistenceContext
 protected EntityManager em;

 @Override
 public long count() {
  String entity = type.getSimpleName();
  final StringBuffer queryString = new StringBuffer("select count(ent) from " + entity + " ent");
  final Query query = this.em.createQuery(queryString.toString());
  return (Long) query.getSingleResult();
 }

 @Override
 public T create(final T t) {
  em.persist(t);
  return t;
 }

 @Override
 public void delete(final Object id) {
  em.remove(em.getReference(type, id));
 }

 @Override
 public T find(final Object id) {
  return em.find(type, id);
 }

 @Override
 public T update(final T t) {
  return em.merge(t);
 }

 @SuppressWarnings("unchecked")
 @Override
 public List<T> getAll() {
  Query query = em.createQuery("from " + type.getName());
  return query.getResultList();
 }
}
This is the implementation of the generic DAO with Hibernate.

ClientDaoImpl
@Repository
public class ClientDaoImpl extends GenericDaoImpl<Client> implements ClientDao {

}
Similar to what we did with the ClientDao interface, we have to do it with the implementation too. We must create the ClientDao implementation and make it extend the generic Dao implementation.
The @Repository annotation only tells Spring this is a bean the "annotations way" of type "DAO" (see the end of the post to understand more of it).

ClientService
public interface ClientService {
 
 public Client create(Client client);
 public void delete(Integer id);
 public Client update(Client client);
 public Client find(Integer id);
 public List<Client> getAll();
 public Long count();
}
This is the  Service layer. It can be generic too, altough in this example only the DAO layer is generic.

ClientServiceImpl
@Service
public class ClientServiceImpl implements ClientService {
 
 @Autowired
 private ClientDao clientDao;

 @Override
 @Transactional(readOnly = false)
 public Client create(Client client) {
  return clientDao.create(client);
 }

 @Override
 @Transactional(readOnly = true)
 public List<Client> getAll() {
  return clientDao.getAll();
 }

 @Override
 @Transactional(readOnly = false)
 public void delete(Integer id) {
  clientDao.delete(id);
 }

 @Override
 @Transactional(readOnly = false)
 public Client update(Client client) {
  return clientDao.update(client);
 }

 @Override
 @Transactional(readOnly = false)
 public Client find(Integer id) {
  return clientDao.find(id);
 }

 @Override
 @Transactional(readOnly = true)
 public Long count() {
  return clientDao.count();
 }

}
In this class, all of our methods are transactional, that's why we annotate them with @Transactional. With @Autorwired we inject the Client DAO implementation. The @Service does the same as @Repository; It tells Spring that this class is a bean too (check the end of the post for more information about it).

spring-beans.xml
<?xml version="1.0" encoding="UTF-8"?>
<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.xsd">

 <!-- Datasource configured in spring and not in persitence.xml -->
 <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
  <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
  <property name="url" value="jdbc:hsqldb:mem:testdb" />
  <property name="username" value="sa" />
  <property name="password" value="" />
 </bean>
 
 <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
  <property name="persistenceXmlLocation" value="classpath:persistence.xml"/>
  <property name="persistenceUnitName" value="spring-webapp-persistenceUnit" />
  <property name="dataSource" ref="dataSource"/>
 </bean>

 <bean id="txManager" class="org.springframework.orm.jpa.JpaTransactionManager">
  <property name="entityManagerFactory" ref="entityManagerFactory"/>
  <property name="dataSource" ref="dataSource" />
 </bean>

</beans>
This file is a normal one, we define the datasource, the hibernate entity manager and the transaction manager associated with both.

ClientRestService
@Controller
@RequestMapping("/rest-services/clients")
public class ClientRestService {
 
 private static final Logger LOGGER = Logger.getLogger(ClientService.class);
 
 @Autowired
 private ClientService clientService;

 @RequestMapping(method = RequestMethod.GET)
 @ResponseBody
 public Clients loadClients() {
  
  Clients clientsWrapper = new Clients();
  clientsWrapper.setClients(clientService.getAll());
  
  LOGGER.info("--- Client list retrieved ---");
  return clientsWrapper;
 }
 
 @RequestMapping(method = RequestMethod.POST)
 @ResponseBody
 public Client addClient(@RequestBody Client client) {
  
  Client newClient = clientService.create(client);
  
  LOGGER.info("--- New client saved ---");
  return newClient;
 }
 
 @RequestMapping(value = "/{clientId}", method = RequestMethod.PUT)
 @ResponseBody
 public Client updateClient(@PathVariable(value = "clientId") Integer clientId, @RequestBody Client client) {
  
  Client newClient = clientService.update(client);
  
  LOGGER.info("--- Client updated ---");
  return newClient;
 }
 
 @RequestMapping(value = "/{clientId}", method = RequestMethod.GET)
 @ResponseBody
 public Client findClient(@PathVariable(value = "clientId") Integer clientId) {
  
  Client foundClient = clientService.find(clientId);
  
  LOGGER.info("--- Client found ---");
  return foundClient;
 }
 
 @RequestMapping(value = "/{clientId}", method = RequestMethod.DELETE)
 @ResponseBody
 public void deleteClient(@PathVariable(value = "clientId") Integer clientId) {
  
  clientService.delete(clientId);
  LOGGER.info("--- Client deleted ---");
 }
 
}
Here we expose our service layer (in this case, our class annotated with @Service) via REST. To sum up the operations available, you can see this table:

URL Method type Operation
http://localhost:8080/spring-webapp-example/client-crud/rest-services/clients GET get all clients
http://localhost:8080/spring-webapp-example/client-crud/rest-services/clients POST create client
http://localhost:8080/spring-webapp-example/client-crud/rest-services/clients/1 PUT update client id = 1
http://localhost:8080/spring-webapp-example/client-crud/rest-services/clients/1 GET find client id = 1
http://localhost:8080/spring-webapp-example/client-crud/rest-services/clients/1 DELETE delete client id = 1

@Controller does the same job as the previous @Repository and @Service annotations; It tells Spring that this class is a Spring bean.

Clients
@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
public class Clients {
 
 private List<Client> clients;

 @XmlElement(name = "client")
 public List<Client> getClients() {
  return clients;
 }

 public void setClients(List<Client> clients) {
  this.clients = clients;
 }

}
This class acts as a wrapper for the list of client class. That wrapper eases the process of consuming a REST service that returns a list of objects. The wrapper class is annotated with JAXB annotations

Client
@XmlRootElement
@XmlAccessorType(XmlAccessType.PROPERTY)
@Entity
@Table(name = "T_CLIENT")
public class Client implements Serializable {

 private static final long serialVersionUID = -7682472386786656877L;
 
 @Id
 @GeneratedValue(strategy = GenerationType.SEQUENCE)
 private Integer id;
 private String name;

 public Integer getId() {
  return id;
 }

 public void setId(Integer id) {
  this.id = id;
 }

 public String getName() {
  return name;
 }

 public void setName(String name) {
  this.name = name;
 }

}
The Client JPA entity which is used by Hibernate, is annotated with JAXB elements too so that we can reuse the same entity when exposing it via REST. Of course you may create another Client class in order to expose only the information you want to expose.

Let's test this example!


ClientServiceTest
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/spring-beans-test.xml", "/applicationContext-servlet-test.xml" })
public class ClientServiceTest {
 
 @Autowired
 private ClientService clientService;
 
 private Client createNewClient() {
  Client client = new Client();
  client.setName("Rupert");
  
  return client;
 }
 
 @Test
 public void createTest() {
  
  Client client = createNewClient();
  
  Client clientSaved = clientService.create(client);
  
  Assert.assertNotNull(clientSaved);
  Assert.assertNotNull(clientSaved.getId());
 }
 
 @Test
 public void deleteTest() {
  Client client = createNewClient();
  
  Client clientSaved = clientService.create(client);
  clientService.delete(clientSaved.getId());
  Client clientDeleted = clientService.find(clientSaved.getId());

  Assert.assertNull(clientDeleted);
 }
 
 @Test
 public void findTest() {
  Client client = createNewClient();
  
  Client clientSaved = clientService.create(client);
  Client clientDeleted = clientService.find(clientSaved.getId());

  Assert.assertNotNull(clientDeleted);
 }
 
 @Test
 public void countTest() {
  Client client = createNewClient();
  
  long countBefore = clientService.count();
  clientService.create(client);
  long countAfter = clientService.count();
  
  Assert.assertEquals(countBefore, countAfter - 1);
 }
 
 @Test
 public void getAllTest() {
  this.createTest();
  List<Client> all = clientService.getAll();
  
  Assert.assertNotNull(all);
  Assert.assertTrue(all.size() > 0);
 }

}
This unit test is for the Client Service layer. You can run it with mvn test command.

ClientRestServiceIT
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/spring-beans-test.xml", "/applicationContext-servlet-test.xml" })
public class ClientRestServiceIT {
 
 private static final String REST_SERVICE_URL = "http://localhost:8080/spring-webapp-example/client-crud/rest-services/clients";
 private RestTemplate restTemplate = new RestTemplate();
 
 private Client createNewClient() {
  Client client = new Client();
  client.setName("Rupert");
  
  return client;
 }
 
 @Test
 public void createTest() {
  
  Client newClient = createNewClient();
  Client clientCreated = restTemplate.postForObject(REST_SERVICE_URL, newClient, Client.class);
  
  Assert.assertNotNull(clientCreated);
  Assert.assertNotNull(clientCreated.getId());
 }
 
 @Test
 public void updateTest() {
  
  Client newClient = createNewClient();
  Client clientCreated = restTemplate.postForObject(REST_SERVICE_URL, newClient, Client.class);
  
  clientCreated.setName("Stuart");
  
  String restServiceUrl = REST_SERVICE_URL + "/" + clientCreated.getId();
  restTemplate.put(restServiceUrl, clientCreated);
  Client clientModified = restTemplate.getForObject(restServiceUrl, Client.class);
  
  Assert.assertNotNull(clientModified);
  Assert.assertTrue(clientModified.getName().equals("Stuart"));
 }
 
 @Test
 public void findTest() {
  
  Client newClient = createNewClient();
  Client clientCreated = restTemplate.postForObject(REST_SERVICE_URL, newClient, Client.class);
  
  String restServiceUrlFind = REST_SERVICE_URL + "/" + clientCreated.getId();
  Client clientFound = restTemplate.getForObject(restServiceUrlFind, Client.class);
  
  Assert.assertNotNull(clientFound);
  Assert.assertEquals(clientFound.getId(), clientCreated.getId());
 }
 
 @Test
 public void getAllTest() {
  
  Client newClient = createNewClient();
  restTemplate.postForObject(REST_SERVICE_URL, newClient, Client.class);
  
  Clients clients = (Clients) restTemplate.getForObject(REST_SERVICE_URL, Clients.class);
  
  Assert.assertNotNull(clients);
  Assert.assertNotNull(clients.getClients());
  Assert.assertTrue(clients.getClients().size() > 0);
 }
 
 @Test
 public void deleteTest() {
  
  Client newClient = createNewClient();
  Client clientCreated = restTemplate.postForObject(REST_SERVICE_URL, newClient, Client.class);
  
  Assert.assertNotNull(clientCreated);
  Assert.assertNotNull(clientCreated.getId());
  
  //It should be found
  String restServiceUrlFind = REST_SERVICE_URL + "/" + clientCreated.getId();
  Client clientFound = restTemplate.getForObject(restServiceUrlFind, Client.class);
  Assert.assertNotNull(clientFound);
  Assert.assertNotNull(clientFound.getId());
  
  restTemplate.delete(restServiceUrlFind);
  
  //It should not be found
  Client clientDeleted = restTemplate.getForObject(restServiceUrlFind, Client.class);
  Assert.assertNull(clientDeleted);
 }
 
}
This test is the so called integration test we created. You can run it executing mvn verify.

About @Controller @Repository and @Service annotations

All of them do the same! they tell Spring that the class annotated is a Spring bean. So, why don't we use something like "@Bean" for every bean? Because the idea goes like this:
  • Every DAO should be @Repository
  • Every Controller/Action should be @Controller
  • Every Service should be @Service
Say, for instance, you have all of your beans annotated with @Controller (remember those 3 are the same) and you want to apply transactions via aspects with Spring to every Service class. Then, if you had all of your service beans annotated with @Service you could choose to apply transaction via aspects to every class annotated with @Service.

Download the complete example!

Checkout the project: https://subversion.assembla.com/svn/pablo-examples/spring-webapp-example

Run mvn test -> To run the unit tests only
Run mvn verify -> To run the unit tests + integration tests
Run mvn verify -Dmaven.test.skip=true -> To run integration tests only

Additionaly, you may run mvn eclipse:eclipse to convert it to Eclipse project

Friday, July 6, 2012

REST services with Spring 3 (XML, JSON)

As an alternative to webservices, we can use REST. Both REST and webservices have their differences. While in a previous post I wrote an example of webservices with Spring 3, I will post here one way of implementing REST using Spring 3. This example consists of a service that can return either XML or JSON depending on the URL invoked. You will be able to check the return values via a browser or a spring client test (which is provided in the example). The example also includes a web page built with extJS that fills a grid with data obtained via the service that return JSON data. Let's see it!


Versions used:
  • Spring 3.1.1-RELEASE
  • XStream 1.4.2 (XML parser)
  • Jackson 1.9.7 (JSON parser)
  • Maven 3


pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>ar.com.pabloExample</groupId>
 <artifactId>spring-rest-example</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>spring-rest-example Maven Webapp</name>
 <url>http://maven.apache.org</url>

 <dependencies>

  <!-- Spring dependencies -->

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>
  
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-webmvc</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-oxm</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <!-- JSON parser -->
  
  <dependency>
   <groupId>org.codehaus.jackson</groupId>
   <artifactId>jackson-mapper-asl</artifactId>
   <version>1.9.7</version>
   <scope>compile</scope>
  </dependency>

  <!-- XML parser -->
  
  <dependency>
   <groupId>com.thoughtworks.xstream</groupId>
   <artifactId>xstream</artifactId>
   <version>1.4.2</version>
   <scope>compile</scope>
  </dependency>

  <!-- For testing purposes -->

  <dependency>
   <groupId>javax.servlet</groupId>
   <artifactId>servlet-api</artifactId>
   <version>2.5</version>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>test</scope>
  </dependency>

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

 </dependencies>

 <build>
  <finalName>spring-rest-example</finalName>
  
  <plugins>
  
   <!-- With this you can start the server by doing mvn jetty:run -->
   <plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <version>6.1.26</version>
    <configuration>
     <scanIntervalSeconds>3</scanIntervalSeconds>
    </configuration>
   </plugin>
  
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
     <source>1.6</source>
     <target>1.6</target>
    </configuration>
   </plugin>
   
  </plugins>
 </build>
</project>

Book.java

@XStreamAlias("book")
public class Book {

 private Integer id;
 private String name;

 //Getters and setters ommited but must exist
}

We use the annotation @XStreamAlias("book") so that the XML generated output for the root book tag is <book>, else, it would be <ar.com.pabloExample.model.Book> because of the way XStream handles XML transformation.

<list>
 <book>
  <id>0</id>
  <name>book nro. 1</name>
 </book>
 <book>
  <id>1</id>
  <name>book nro. 2</name>
 </book>
 <book>
  <id>2</id>
  <name>book nro. 3</name>
 </book>
</list>

BookController.java

@Controller
@RequestMapping("/book")
public class BookController {

 @RequestMapping(value="/names", method=RequestMethod.GET)
 public List<Book> getNames() {
  
  return returnData();
 }
 
 private List<Book> returnData() {
  
  ArrayList<Book> names = new ArrayList<Book>();
  
  Book b1 = new Book();
  Book b2 = new Book();
  Book b3 = new Book();
  
  b1.setName("book nro. 1");
  b1.setId(0);
  b2.setName("book nro. 2");
  b2.setId(1);
  b3.setName("book nro. 3");
  b3.setId(2);
  
  names.add(b1);
  names.add(b2);
  names.add(b3);
  
  return names;
 }
}

With those @RequestMapping annotations we are able to create a REST service so that it returns the getNames() return value whenever this address is invoked:
http://localhost:8080/spring-rest-example/rest/book/names


Books.java

public class Books {
 
 @JsonProperty("bookList")
 private
 List<Book> books;

 public List<Book> getBooks() {
  return books;
 }

 public void setBooks(List<Book> books) {
  this.books = books;
 }
}

This entity is just a wrapper for our List<Book>, my JSON example wouldn't work managing the list directly.
Also, the @JsonProperty("bookList") annotation exists in order to map the return "bookList" to the "books" attribute.

{"bookList":[{"id":0,"name":"book nro. 1"},{"id":1,"name":"book nro. 2"},{"id":2,"name":"book nro. 3"}]}


web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>

 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath*:applicationContext.xml</param-value>
 </context-param>

 <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>

 <servlet>
  <servlet-name>spring</servlet-name>
  <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>spring</servlet-name>
  <url-pattern>/rest/*</url-pattern>
 </servlet-mapping>

</web-app>

This is an usual Spring web configuration context. Every http://localhost:8080/spring-rest-example/rest/* request will be attended by the spring-servlet.xml


spring-servlet.xml

<?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:mvc="http://www.springframework.org/schema/mvc"
 xmlns:context="http://www.springframework.org/schema/context"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
  http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd
  http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd
  http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.1.xsd">

 <mvc:annotation-driven/>

 <context:component-scan base-package="ar.com.pabloExample.controller" />

 <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
  <property name="order" value="1" />
  <property name="mediaTypes">
   <map>
    <entry key="json" value="application/json" />
    <entry key="xml" value="application/xml" />
   </map>
  </property>

  <property name="defaultViews">
   <list>
    <!-- JSON View -->
    <bean class="org.springframework.web.servlet.view.json.MappingJacksonJsonView" />

    <!-- XML View -->
    <bean class="org.springframework.web.servlet.view.xml.MarshallingView">
     <constructor-arg>
      <bean class="org.springframework.oxm.xstream.XStreamMarshaller">
       <property name="autodetectAnnotations" value="true"/>
      </bean>
     </constructor-arg>
    </bean>
   </list>
  </property>
 </bean>

</beans>

Here, we configure ContentNegotiatingViewResolver to either accept XML and JSON when answering a request.
http://localhost:8080/spring-rest-example/rest/book/names - returns XML (default when no extension)
http://localhost:8080/spring-rest-example/rest/book/names.xml - returns XML
http://localhost:8080/spring-rest-example/rest/book/names.json - returns JSON


applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<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">

</beans>

This XML file is mandatory in the Spring configuration. We put nothing here.


Let's test the services!


test-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="restTemplate" class="org.springframework.web.client.RestTemplate">  
      <property name="messageConverters">
       <list>
           <bean id="marshallingHttpMessageConverter" class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
            <property name="marshaller" ref="xstreamMarshaller"/>
            <property name="unmarshaller" ref="xstreamMarshaller"/>
           </bean>
           <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"/>
       </list>
      </property>
    </bean>
    
 <bean id="xstreamMarshaller" class="org.springframework.oxm.xstream.XStreamMarshaller">
  <property name="aliases">
         <props>
             <prop key="book">ar.com.pabloExample.model.Book</prop>
         </props>
     </property>
 </bean>

</beans>

The RestTemplate class has configuration for both XML and JSON message converters.
For XML marshalling we use XStream, and configure an alias so it is able to understand the <book> refers to <ar.com.pabloExample.model.Book>


RestClientTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-beans.xml" })
public class RestClientTest {

 @Autowired
 @Qualifier("restTemplate")
 private RestTemplate restTemplate;
 
 @Test
 public void restJsonClientTest() {
  
  Books booksAux = restTemplate.getForObject("http://localhost:8080/spring-rest-example/rest/book/names.json", Books.class);
  List<Book> books = booksAux.getBooks();
  
  Assert.assertNotNull(books);
  Assert.assertTrue(books.size() > 0);
 }
 
 @SuppressWarnings("unchecked")
 @Test
 public void restXmlClientTest() {
  
  List<Book> books = (ArrayList<Book>) restTemplate.getForObject("http://localhost:8080/spring-rest-example/rest/book/names.xml", List.class);
  
  Assert.assertNotNull(books);
  Assert.assertTrue(books.size() > 0);
 }
}

Run mvn jetty:run to start jetty web server so that the webservice is exposed, then you can run RestClientTest doing mvn test.

http://localhost:8080/spring-rest-example/rest/book/names - returns XML (default when no extension)

http://localhost:8080/spring-rest-example/rest/book/names.xml - returns XML
http://localhost:8080/spring-rest-example/rest/book/names.json - returns JSON


You can access http://localhost:8080/spring-rest-example/example.html too to see an extJS data grid example that is filled with the JSON service we've just deployed.


Get the complete code!


https://subversion.assembla.com/svn/pablo-examples/spring-rest-example/

  1. First, run mvn jetty:run to start jetty web server
  2. Then mvn test to run JUnit tests


Finally


I think there's too much boilerplate configuration to enable REST services with Spring, unless you're already using Spring to build your app. Maybe other products like RESTEasy are more suited if we don't want to use Spring for our webapp but still want to have services exposed via REST...

For more information about mvc:annotation-driven tag, @Controller annotation, @RequestMapping see:
http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html

Saturday, June 16, 2012

Native database support with MyBatis + Spring 3

Say, we may be building a new application while keeping the old database where a bunch of stored procedures are used (so, the database has most of the business logic too), or building a new whole application and we don't want to use an ORM like Hibernate cause we don't care about database coupling; we just want to have control of all of our queries... Well, in these cases we may want to take a look at MyBatis which really makes it easy to use native queries while still allowing us to take full control of queries being executed. We are not looking at MyBatis alone, we also look at it's integration with Spring which eases the whole configuration of this product.

So, we will see an example of a persistence layer being implemented with MyBatis + Spring with some explanation on the relevant sections. The full example can be downloaded too. So, let's see the example!

Versions used:
  • Spring 3.1.1-RELEASE
  • MyBatis-spring 1.1.0

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>ar.com.pabloExample</groupId>
 <artifactId>spring-mybatis-example</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>spring-mybatis-example</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <repositories>
  <repository>
   <id>springsource-repo</id>
   <name>SpringSource Repository</name>
   <url>http://repo.springsource.org/release</url>
  </repository>
 </repositories>

 <dependencies>

  <dependency>
   <groupId>commons-dbcp</groupId>
   <artifactId>commons-dbcp</artifactId>
   <version>1.4</version>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>org.mybatis</groupId>
   <artifactId>mybatis-spring</artifactId>
   <version>1.1.0</version>
   <scope>test</scope>
  </dependency>

  <!-- Here we use PostgreSQL, you may use another database but you must 
   check -->
  <!-- all your sql sentences cause you are attached to the database -->
  <dependency>
   <groupId>postgresql</groupId>
   <artifactId>postgresql</artifactId>
   <version>9.1-901.jdbc4</version>
   <scope>test</scope>
  </dependency>

  <!-- Spring dependencies -->

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>test</scope>
  </dependency>

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

 </dependencies>

 <build>
  <plugins>
   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
     <source>1.6</source>
     <target>1.6</target>
    </configuration>
   </plugin>
  </plugins>
 </build>

</project>

Book.java

public class Book {
 
 private Integer id;
 private String name;
 private List<Author> authors;

 //Getters and setters ommited but must exist
}

Author.java

public class Author {
 
 private Integer id;
 private String name;
 
 //Getters and setters ommited but must exist
}

BookDao.java

public interface BookDao {

 public Book getBook(Integer id);

 public List<book> getAllBooks();
 
 public int returnNumberOfBooks(Map<String, Object> map);
}

We will only create the DAO interfaces. No need to create any DAO implementation, it is done automatically by MyBatis!


BookDao.xml

<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">

<mapper namespace="ar.com.pabloExample.interfaces.BookDao">

 <!-- This example returns a book by id -->
 
 <select id="getBook" resultType="ar.com.pabloExample.model.Book" parameterType="int" >
  SELECT p_id as ID, a_name as NAME
  FROM "T_BOOK"
  WHERE p_id = #{id}
 </select>
 
 
 <!-- This example returns a list of books and shows usage of resultMap -->
 
 <select id="getAllBooks" resultMap="resultMap_getAllBooks" parameterType="int" >
  SELECT p_id, a_name
  FROM "T_BOOK"
 </select>
 
 <resultMap id="resultMap_getAllBooks" type="ar.com.pabloExample.model.Book">
  <id property="id" column="p_id"/>
  <result property="name" column="a_name"/>
  <collection property="authors" column="p_id" select="getAuthorsByBookId" />
 </resultMap>
 
 <select id="getAuthorsByBookId" parameterType="int" resultType="ar.com.pabloExample.model.Author" >
  SELECT p_id as ID, a_name as NAME
  FROM "T_AUTHOR_BOOK", "T_BOOK"
  WHERE p_id = #{id} and f_book_id = p_id
 </select>
 
 
 <!-- This example shows usage of input map -->
 
 <select id="returnNumberOfBooks" parameterType="map" resultType="int">
  SELECT COUNT(*) FROM "T_BOOK" WHERE 1 = #{param1} AND 2 = #{param2}
 </select>
 
</mapper>

In order MyBatis to automatically create DAO implementations behind the scene, each <select ...> tag has to match a DAO interface method.


applicationContext.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="datasource" class="org.apache.commons.dbcp.BasicDataSource">
  <property name="driverClassName" value="org.postgresql.Driver" />
  <property name="url" value="jdbc:postgresql://localhost:5432/mybatistest" />
  <property name="username" value="postgres" />
  <property name="password" value="123456" />
 </bean>

 <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="configLocation" value="mybatis-config.xml"/>
  <property name="dataSource" ref="datasource" />
 </bean>

 <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="ar.com.pabloExample.interfaces" />
 </bean>

 <!-- This is a more manual way to create each DAO impl beans with  MapperFactoryBean  -->
 <!-- instead of using MapperScannerConfigurer -->

 <!--  <bean id="bookDao2" class="org.mybatis.spring.mapper.MapperFactoryBean"> -->
 <!--   <property name="mapperInterface" value="ar.com.pabloExample.interfaces.BookDao" /> -->
 <!--   <property name="sqlSessionFactory" ref="sqlSessionFactory" /> -->
 <!--  </bean> -->

 </beans>

The first bean is the datasource, the others are for mybatis. The SqlSessionFactoryBean contains a reference to the datasource and also a file where we can set parameters for mybatis. The MapperScannerConfigurer will automatically scan the whole classpath for xmls.


mybatis-config.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
 <settings>
  <setting name="cacheEnabled" value="false" />
  <setting name="lazyLoadingEnabled" value="false" />
 </settings>
</configuration>

These are just some settings for the configuration file. Remember this file is not a must.


Let's test all the DAO methods!


BookDaoTest.java

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/applicationContext.xml" })
public class BookDaoTest {
 
 @Autowired
// @Qualifier("bookDao2") add this only if using MapperFactoryBean instead of MapperScannerConfigurer
 private BookDao bookDao;
 
 @Autowired
 @Qualifier("datasource")
 private BasicDataSource datasource;

 
 @Before
 public void connectionTest() throws SQLException {
  
  datasource.getConnection();
 }
 
 @Test
 public void getBookByIdTest() {
  
  Assert.assertNotNull(bookDao);
  Book book1 = bookDao.getBook(0);
  
  Assert.assertNotNull("Book returned is null", book1);
  Assert.assertTrue("Returned another book", book1.getName().equals("EJB3 in Action"));
 }
 
 @Test
 public void getAllBooksTest() {
  
  Assert.assertNotNull(bookDao);
  List<Book> list = bookDao.getAllBooks();
  
  Assert.assertNotNull("List of books returned null", list);
  Assert.assertTrue("List of books is empty", !list.isEmpty());
  Assert.assertNotNull("Valid book", list.get(0));
  Assert.assertNotNull("ID not loaded", list.get(0).getId());
  Assert.assertNotNull("NAME not loaded", list.get(0).getName());
  Assert.assertNotNull("List of authors null", list.get(0).getAuthors());
  Assert.assertTrue("List of authors is empty", !list.get(0).getAuthors().isEmpty());
  Assert.assertNotNull("ID of author is null", list.get(0).getAuthors().get(0).getId());
  Assert.assertNotNull("NAME of author is null", list.get(0).getAuthors().get(0).getName());
 }
 
 @Test
 public void returnNumberOfBooksTest() {
  
  Assert.assertNotNull(bookDao);
  
  Map<String, Object> map = new HashMap<String, Object>();
  map.put("param1", 1);
  map.put("param2", 2);
  int result = bookDao.returnNumberOfBooks(map);
  
  Assert.assertTrue(result > 0);
 }
 
}

You may have noticed we inject "datasource" which can be found as a bean in applicationContext.xml but we inject "bookDao" (No @Qualifier will take "bookDao" implicitly) too but cannot see any bean of BookDao type in any XML file. Why? Well, remember this is because "bookDao" implementation bean is automatically created by MyBatis at runtime (we didn't create any DAO implementation!). How it is achieved? When the MapperScannerConfigurer scans for the DAOs.

Before running the example run the database script generation for Postgresql and add some data to the tables!
Then run it through your IDE or run from a console: mvn test

Get the complete code (including the database script generator) at:


https://subversion.assembla.com/svn/pablo-examples/spring-mybatis-example

You can also check the official example in mybatis site here

Wednesday, May 16, 2012

Webservices with Spring 3 + Apache CXF

This example will consist of a web app that publishes a simple web service using Apache CXF + Spring. It will include a web service client test too, and a lightweight server (Jetty) to quick test with. Before running the JUnit test through Eclipse IDE or Maven, you have to start your server manually. This configuration is going to be explained.

Versions used:
- Maven 3
- Apache CXF 2.6
- Spring 3.1.1-RELEASE


This is the structure of our archetyped webapp:




pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 <groupId>ar.com.pabloExample</groupId>
 <artifactId>spring-cxf-example</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 <name>spring-cxf-example Maven Webapp</name>
 <url>http://maven.apache.org</url>

 <repositories>
  <repository>
   <id>springsource-repo</id>
   <name>SpringSource Repository</name>
   <url>http://repo.springsource.org/release</url>
  </repository>
 </repositories>

 <dependencies>

  <!-- Spring dependencies -->

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <!-- Apache CXF for webservices -->

  <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-api</artifactId>
   <version>2.6.0</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-frontend-jaxws</artifactId>
   <version>2.6.0</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.apache.cxf</groupId>
   <artifactId>cxf-rt-transports-http</artifactId>
   <version>2.6.0</version>
   <scope>compile</scope>
  </dependency>

  <!-- For testing purposes -->

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>test</scope>
  </dependency>

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

 </dependencies>

 <build>
  <finalName>spring-cxf-example</finalName>

  <plugins>

   <!-- With this you can start the server by doing mvn jetty:run -->
   <plugin>
    <groupId>org.mortbay.jetty</groupId>
    <artifactId>maven-jetty-plugin</artifactId>
    <version>6.1.26</version>
    <configuration>
     <scanIntervalSeconds>3</scanIntervalSeconds>
    </configuration>
   </plugin>

   <plugin>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>2.3.2</version>
    <configuration>
     <source>1.6</source>
     <target>1.6</target>
    </configuration>
   </plugin>

  </plugins>

 </build>

</project>


HelloWorldService

@WebService
public interface HelloWorldService {
 
 public void sayHello();
}

In order to work with webservices, we must use interfaces for our services...


HelloWorldServiceBean

@WebService(endpointInterface = "ar.com.pabloExample.services.HelloWorldService")
public class HelloWorldServiceBean implements HelloWorldService {

 @Override
 public void sayHello() {
  System.out.println("Hello World!!!");
 }

}

The endpointInterface must point to the interface we are implementing (that's why interfaces are a must).


service-definition-beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<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="helloWorldService" class="ar.com.pabloExample.services.HelloWorldServiceBean" />

</beans>

Here we define a normal service bean.


webservice-definition-beans.xml

<?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:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

 <import resource="classpath:service-definition-beans.xml"/>

 <jaxws:endpoint id="helloWorld" implementor="#helloWorldService" address="/HelloWorld" />

</beans>

The bean defined in service-definition-beans.xml can be moved here instead of importing that resource. You may want to have separate bean xml files, it is important to separate by functionality (e.g. DAO beans, webservice beans, test beans...). By doing implementor="#helloWorldService" we are making reference to a Spring bean with id="helloWorldService"


service-definition-beans-test.xml

<?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:jaxws="http://cxf.apache.org/jaxws"
 xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
  http://cxf.apache.org/jaxws http://cxf.apache.org/schemas/jaxws.xsd">

 <jaxws:client id="helloWorldClient" serviceClass="ar.com.pabloExample.services.HelloWorldService"
  address="http://localhost:8080/spring-cxf-example/HelloWorld" />

</beans>

Here we create the CXF webservice client which we will invoke during our test by injecting a Spring bean. For this client to be successful, the server has to be up and running with the application.


web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>

 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>WEB-INF/webservice-definition-beans.xml</param-value>
 </context-param>

 <servlet>
  <servlet-name>CXFServlet</servlet-name>
  <display-name>CXF Servlet</display-name>
  <servlet-class>org.apache.cxf.transport.servlet.CXFServlet</servlet-class>
  <load-on-startup>1</load-on-startup>
 </servlet>

 <servlet-mapping>
  <servlet-name>CXFServlet</servlet-name>
  <url-pattern>/*</url-pattern>
 </servlet-mapping>

 <listener>
  <listener-class>
   org.springframework.web.context.ContextLoaderListener
  </listener-class>
 </listener>

</web-app>

This is were we configure CXFServlet to scan all our request and Spring ContextLoaderListener to scan all of our bean configuration files deployed.


Let's test the webservice!


HelloWorldServiceTest

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/service-definition-beans-test.xml" })
public class HelloWorldServiceTest {

 @Autowired
 @Qualifier("helloWorldClient")
 private HelloWorldService helloWorldClient;

 @Test
 public void helloWorldClientTest() {
  
  helloWorldClient.sayHello();
 }

}

Now, here we invoke the previous mentioned webservice client. Just do mvn jetty:run from a console to start the server and run the test via Eclipse IDE or Maven!!


Get the complete code!


You can checkout the code from here:

https://subversion.assembla.com/svn/pablo-examples/spring-cxf-example/

To run the tests successfully just do mvn clean package -Dmaven.test.skip=truemvn jetty:run and then mvn test.


Finally...


The next step is to run this test as an integration test because this test needs preconditions, like the server up, so it cannot be part of normal tests. I will explain the implementation differences between a test and integration-test with an example in another post.

Here is another useful related post from the official Apache CXF home.

Saturday, May 12, 2012

Testing with JUnit + Spring

In this post I want to share the most convenient way to test Spring beans using JUnit. The idea is to test using dependency injection techniques instead of performing a manual injection.

The versions I used to create this project are:

- Spring 3.1.1-RELEASE
- JUnit 4
- Maven 3


Configuration files:


pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>ar.com.pabloExample</groupId>
 <artifactId>spring-junit-example</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>spring-junit-example</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <dependencies>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>test</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-test</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>test</scope>
  </dependency>

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

 </dependencies>
</project>


test-beans.xml

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

 <bean class="ar.com.pabloExample.services.MessageService" id="messageService"/>
 
</beans>

We just define a normal Spring bean in the test classpath.


Creating the test


BeanInjectionNewWayTest

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(locations = { "/test-beans.xml" })
public class BeanInjectionNewWayTest {
 
 @Autowired
 @Qualifier("messageService")
 private MessageService messageService;

 
 @Test
 public void test() {
  messageService.printMessage();
 }

}

The @RunWith(SpringJUnit4ClassRunner.class) tells JUnit to run JUnit using the SpringJUnit4ClassRunner class as the internal core. This allows the rest of the annotations (except for @Test) to work as expected. @ContextConfiguration brings up all the XML bean files in a context and we can dispone of those beans anytime in any test.

@Autowired injects a bean, if no @Qualifier specified, it is done BY TYPE; if @Qualifier is specified, it is injected BY BEAN ID. Let's see this in more detail:

In this example, we just have one bean of ar.com.pabloExample.services.MessageService type. So we can just put the @Autowired (no @Qualifier) and everything works as expected because it matches BY TYPE.

If we had the following beans file instead:

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

 <bean class="ar.com.pabloExample.services.MessageService" id="messageService"/>
 <bean class="ar.com.pabloExample.services.MessageService" id="messageService2"/>
 
</beans>

Then we cannot match only BY TYPE because we have now two beans of the same type. So, appart from @Autowired we must use @Qualifier with the bean id of the explicit bean to inject.


Testing JUnit + Spring the old way!


Just to keep in mind, here are two other ways of to use Spring beans while testing with old versions of JUnit and Spring. These ways are still being used in many enterprise applications:


BeanInjectionOldWayTest

public class BeanInjectionOldWayTest extends AbstractSingleSpringContextTests {
 
 private MessageService messageService;

 @Override
 protected String[] getConfigLocations() {
  String[] locations = { "/test-beans.xml" };
  return locations;
 }

 @Override
 public void onSetUp() {
  messageService = (MessageService) this.applicationContext.getBean("messageService");
 }

 public void testInject() throws Exception {
  
  messageService.printMessage();
 }

}


BeanInjectionOldWay2Test

public class BeanInjectionOldWay2Test {
 
 private ApplicationContext applicationContext;
 private MessageService messageService;

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

}

Monday, April 30, 2012

Scheduling jobs with Quartz + Spring 3

You are asked to schedule a job to run under a certain frequency. How would you solve this requirement?

You can easily add Quartz to your project to meet this requirement. But using Quartz + Spring configuration in your project is a cleaner solution and more powerful.

In this example we will create a simple service that says "Hellooo World!!!!" every one minute using a cron expression. Then we will see how to start the schedule when you deploy your application in your server.

On the first place, let's see the pom file for the scheduler project:

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
 <modelVersion>4.0.0</modelVersion>

 <groupId>ar.com.pabloExample</groupId>
 <artifactId>spring-quartz-example</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>

 <name>spring-quartz-example</name>
 <url>http://maven.apache.org</url>

 <properties>
  <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
 </properties>

 <repositories>
  <repository>
   <id>springsource-repo</id>
   <name>SpringSource Repository</name>
   <url>http://repo.springsource.org/release</url>
  </repository>
 </repositories>

 <dependencies>
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>provided</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework.batch</groupId>
   <artifactId>spring-batch-core</artifactId>
   <version>2.1.8.RELEASE</version>
   <scope>provided</scope>
  </dependency>

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

As this scheduler service project will be packaged as a JAR file, every dependency is in provided or test scope. This is because we will package it inside a WAR file that will have the same dependencies but in compile scope.

Now, this is the service to be scheduled:

HelloWorldService

package ar.com.pabloExample.service;

public class HelloWorldService {
 
 public void sayHelloWorld() {
  System.out.println("Hellooo World!!!!");
 }
}

Now, let's create a XML file where we will define the HelloWorldService as a Spring bean and the rest of scheduling configuration beans.

beans-spring.xml

<bean id="helloWorldService" class="ar.com.pabloExample.service.HelloWorldService" />

<bean id="myJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
 <property name="targetObject" ref="helloWorldService" />
 <property name="targetMethod" value="sayHelloWorld" />
</bean>

<bean id="everyMinuteTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
 <property name="jobDetail" ref="myJobDetail" />
 <property name="cronExpression" value="0 0/1 * 1/1 * ? *" />
</bean>

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
 <property name="jobDetails">
  <list>
   <ref bean="myJobDetail" />
  </list>
 </property>
</bean>

Let's see this XML in more detail:

<bean id="myJobDetail" class="org.springframework.scheduling.quartz.MethodInvokingJobDetailFactoryBean">
 <property name="targetObject" ref="helloWorldService" />
 <property name="targetMethod" value="sayHelloWorld" />
</bean>
This bean defines the class and method of that class that is going to be scheduled.


<bean id="everyMinuteTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
 <property name="jobDetail" ref="myJobDetail" />
 <property name="cronExpression" value="0 0/1 * 1/1 * ? *" />
</bean>
This bean defines a trigger associated to the previous bean. Here a cron expression is used, this one in particular defines a frequency of one minute. You can easily create cron expressions using online tools like this one.

<bean class="org.springframework.scheduling.quartz.SchedulerFactoryBean">
 <property name="triggers">
  <list>
   <ref bean="everyMinuteTrigger" />
  </list>
 </property>
</bean>
This is the bean that can holds the list of jobs chosen to run. When this bean is instantiated, the scheduler starts. An explanation on how to start the scheduler is the next step to be explained.

The result up to now should be a jar file with the class to be scheduled and the bean configuration file.


I already have the schedule configuration, how should I start the schedule?


Ways to initiate the scheduling:
1- Only if using JBoss AS, you can use Snowdrop; it will automatically scan deployed beans and instantiate every bean under *spring.xml name pattern. This is pure server configuration, it is useful for deploying either a WAR or a simple JAR file. Take a look at this post to see the configuration needed, except for the EJB bean injection; you don't need that part.
2- Add a Spring listener in your web.xml that automatically deploys chosen XML files (you define the listener and the chosen bean files in the same web.xml file). Basically this is the same as the above way but within the context of a webapp.


Creating a webapp to deploy the scheduling service


For the most usual case, this is the configuration you should follow to schedule within the context of a webapp (a WAR file):

pom.xml

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
 <modelVersion>4.0.0</modelVersion>
 
 <groupId>ar.com.pabloExample</groupId>
 <artifactId>spring-quartz-web-example</artifactId>
 <packaging>war</packaging>
 <version>1.0-SNAPSHOT</version>
 
 <name>spring-quartz-web-example Maven Webapp</name>
 <url>http://maven.apache.org</url>

 <dependencies>

  <!-- This dependency has the ContextLoaderListener defined in web.xml -->
  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-web</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <!-- This dependency has the beans-spring.xml with all the quartz config included -->

  <dependency>
   <groupId>ar.com.pabloExample</groupId>
   <artifactId>spring-quartz-example</artifactId>
   <version>1.0-SNAPSHOT</version>
   <scope>compile</scope>
  </dependency>

  <!-- In the war all the jars needed by spring-quartz-example to work the are included -->

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-context-support</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework</groupId>
   <artifactId>spring-tx</artifactId>
   <version>3.1.1.RELEASE</version>
   <scope>compile</scope>
  </dependency>

  <dependency>
   <groupId>org.springframework.batch</groupId>
   <artifactId>spring-batch-core</artifactId>
   <version>2.1.8.RELEASE</version>
   <scope>compile</scope>
  </dependency>
 </dependencies>

 <build>
  <finalName>spring-quartz-web-example</finalName>
 </build>

</project>

For the WAR file, the pom has the dependency for the artifact spring-quartz-example which is the jar file that includes the HelloWorldService class and the beans configuration XML that was created in the previous step.

web.xml

<!DOCTYPE web-app PUBLIC
 "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
 "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
 <display-name>Archetype Created Web Application</display-name>
 <context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>classpath:beans-spring.xml</param-value>
 </context-param>

 <listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
 </listener>
</web-app>

This way, when you deploy your WAR file in the server, the Spring Context listener will scan your beans-spring.xml file and deploy the beans. Nothing else is needed at this point; the schedule has already been started.

Tuesday, April 17, 2012

Aspect Oriented Programming (AOP) with Spring 3

What does it mean?

AOP is a programming paradigm as well as Object Oriented Programming (OOP). In OOP we work with "objects" in a procedural world, meanwhile in AOP we work with "aspects" in a declarative world. You can use AOP concepts while working mainly with OOP, like the example presented below.


What's the basic concept behind?

The basic concept is to declare aspects. An aspect is a method of a class that will run automatically whenever certain conditions occur. Really simple but powerful.

It is similar to database triggers, you need to do something if some event occurs. The simplest example can be when you want to automatically log something before any method of your service layer runs.

Enough, let's see an example!


Hello World Spring 3 AOP example:

First of all we will create a service with a method that just does nothing (DummyService). Then, we will create another service with a method that writes "hello" in the console (SayHelloWorldService). Then we will create a test that instantiates the DummyService bean and runs the dummy method that does nothing, the result will be say hello written on console instead of nothing. This will be achieved by some AOP configuration that will always execute the say hello method before the dummy method is executed.

Versions we will use:
Spring 3.1.1-RELEASE

pom.xml
<repositories>
 <repository>
  <id>springsource-repo</id>
  <name>SpringSource Repository</name>
  <url>http://repo.springsource.org/release</url>
 </repository>
</repositories>

<dependencies>

 <dependency>
  <groupId>junit</groupId>
  <artifactId>junit</artifactId>
  <version>4.8.2</version>
  <scope>test</scope>
 </dependency>
 
 <!-- These are for Spring beans and to enable the AOP features  -->

 <dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>3.1.1.RELEASE</version>
  <scope>test</scope>
 </dependency>

 <dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjrt</artifactId>
  <version>1.6.12</version>
  <scope>test</scope>
 </dependency>
  
 <dependency>
  <groupId>org.aspectj</groupId>
  <artifactId>aspectjweaver</artifactId>
  <version>1.6.12</version>
  <scope>test</scope>
 </dependency>

 <dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>2.2.2</version>
  <scope>test</scope>
 </dependency>

</dependencies>

DummyService
package ar.com.pabloExample.services;

public class DummyService {

 public void dummyMethod() {
  
 }
}
This service has a method that does nothing. The final result is that we will always see a Hello World message in the console whenever we run this method. You may be asking how that's possible... well, thanks to AOP whenever we run dummyMethod(), the sayHelloWorld() method will run before...

SayHelloWorldService
package ar.com.pabloExample.services;

public class SayHelloWorldService {
 
 public void sayHelloWorld() {
  System.out.println("--- Hello World!!! ---");
 }
}
This services just prints the Hello World message on the console.

DummyServiceTest
public class DummyServiceTest {
 
 private ApplicationContext applicationContext;
 private DummyService dummyService;

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

 @Test
 public void dummyMethodTest() {
  
  dummyService.dummyMethod();
 }
}
This is the test that will simply instantiate a DummyService bean and run the dummyMethod() to see the result.


Creating the test-beans.xml with AOP configuration:

For this test to run we need to define these beans:
<bean class="ar.com.pabloExample.services.SayHelloWorldService" id="sayHelloWorldService">
 
<bean class="ar.com.pabloExample.services.DummyService" id="dummyService">

The AOP configuration is:

<aop:config>
 <aop:aspect id="myAspect" ref="sayHelloWorldService">
  <aop:pointcut expression="execution(public void ar.com.pabloExample.services.*.*(..))" id="pointcut1" />
  <aop:before method="sayHelloWorld" pointcut-ref="pointcut1" />
 </aop:aspect>
</aop:config>

<aop:config>
</aop:config>
This tag defines an AOP configuration section in the xml.

<aop:config>
 <aop:aspect id="myAspect" ref="sayHelloWorldService" />
</aop:config>
We declare an aspect referencing the bean with the service we want to execute.

<aop:pointcut expression="execution(public void ar.com.pabloExample.services.*.*(..))" id="pointcut1" />
The pointcut is the condition that must occur so that this aspect runs. In this case the condition is "This aspect will run for any method of any class with any parameter that is under the package ar.com.pabloExample.services and returns a void and is public"

<aop:before method="sayHelloWorld" pointcut-ref="pointcut1" />
This AOP will run the sayHelloWorld() before the dummyMethod() in this case.

So, the xml with the beans ends like this:

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

 <aop:config>
  <aop:aspect id="myAspect" ref="sayHelloWorldService">
   <aop:pointcut expression="execution(public void ar.com.pabloExample.services.*.*(..))" id="pointcut1" />
   <aop:before method="sayHelloWorld" pointcut-ref="pointcut1" />
  </aop:aspect>
 </aop:config>

 <bean class="ar.com.pabloExample.services.SayHelloWorldService" id="sayHelloWorldService" />
 
 <bean class="ar.com.pabloExample.services.DummyService" id="dummyService" /> 
</beans>

Now you can run the test and see the results.

The same AOP configuration can be done with annotations instead of xml configuration, but I prefer the xml way because AOP configuration is decoupled from the java classes, and a change of AOP configuration doesn't require a new compilation. A really nice example on how to do profiling method execution using annotations can be found here.

The main disadvantage of AOP is that the flow of the program structure may change completely (it depends on how much you depend on aspects...) and it becomes more complex because it is more dificult now to track issues looking at the code.

More information about Spring 3 AOP can be found here

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