Blog Zenika

#CodeTheWorld

Java

Using Spring to develop EJB 3.1

When developping EJB applications, you need to bootstrap an EJB container to run your EJBs. Before Java EE 6, EJB containers usually come bundled in full-blown JEE application servers, making the test and development of EJBs difficult and counter-productive. As of Java EE 6, the EJB specification comes with a new feature : an embedded API for EJB containers. This is a great step, but I found some limitations to this API (more on that later). That’s why I decided to code an stateless EJB (arguably the most commonly kind of EJB), test it with Spring and run it under Glasshfish.

Roadmap

The EJB will be at the heart of a very simple web application. It will allow to add and select contact entities. We’ll be using JPA and Derby for the persistence. We’ll also leverage the new web profile of Java EE, as the EJB will be distributed directly in the web application, along with the JPA persistence unit and a servlet (to display contacts).

The entity

Here is the code of the entity :

  1. package com.zenika.contact.domain;
  2. import javax.persistence.Entity;
  3. import javax.persistence.GeneratedValue;
  4. import javax.persistence.GenerationType;
  5. import javax.persistence.Id;
  6. @Entity
  7. public class Contact {
  8. @Id
  9. @GeneratedValue(strategy=GenerationType.IDENTITY)
  10. private Long id;
  11. private String firstName,lastName;
  12. public Contact() { }
  13. public Contact(String firstName, String lastName) {
  14. this();
  15. this.firstName = firstName;
  16. this.lastName = lastName;
  17. }
  18. // getters and setters…
  19. }

The EJB

Here is the interface of the EJB :

  1. package com.zenika.contact.service;
  2. import java.util.List;
  3. import com.zenika.contact.domain.Contact;
  4. public interface ContactService {
  5. /**
  6.   * Contact creation.
  7.   * @param contact
  8.   * @return
  9.   */
  10. Contact create(Contact contact);
  11. /**
  12.   * Contact selection.
  13.   * @return
  14.   */
  15. List<Contact> select();
  16. }

Note this is a plain interface (not remote nor locale), because I’m used to coding against interface, but we could also use the corresponding EJB annotations if we needed to.
And now, the code of the EJB itself :

  1. package com.zenika.contact.service.impl;
  2. import java.util.List;
  3. import javax.ejb.Stateless;
  4. import javax.persistence.EntityManager;
  5. import javax.persistence.PersistenceContext;
  6. import com.zenika.contact.domain.Contact;
  7. import com.zenika.contact.service.ContactService;
  8. @Stateless
  9. public class ContactServiceImpl implements ContactService {
  10. @PersistenceContext(name=« contact »)
  11. private EntityManager entityManager;
  12. @Override
  13. public Contact create(Contact contact) {
  14. entityManager.persist(contact);
  15. return contact;
  16. }
  17. @Override
  18. public List<Contact> select() {
  19. return entityManager.createQuery(« select c from Contact c »).getResultList();
  20. }
  21. }

The JPA configuration

The JPA configuration lies in the standard location, META-INF/persistence.xml:

  1. <?xml version=« 1.0 » encoding=« UTF-8 »?>
  2. <persistence xmlns=« http://java.sun.com/xml/ns/persistence » version=« 1.0 »>
  3. <persistence-unit name=« contact » transaction-type=« JTA »>
  4. <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
  5. <jta-data-source>jdbc/contactDS</jta-data-source>
  6. <class>com.zenika.contact.domain.Contact</class>
  7. <exclude-unlisted-classes />
  8. <properties>
  9. <property name=« eclipselink.ddl-generation » value=« drop-and-create-tables »/>
  10. <property name=« eclipselink.logging.level » value=« INFO »/>
  11. </properties>
  12. </persistence-unit>
  13. </persistence>

Note the persistence unit uses JTA transaction, EclipseLink (JPA 2.0 Reference Implementation, which ships with Glassfish 3.0) and a JNDI datasource.

The servlet

The servlet is only there to check that everything goes fine once the application has been deployed :

  1. package com.zenika.contact.web;
  2. import java.io.IOException;
  3. import java.util.List;
  4. import javax.ejb.EJB;
  5. import javax.servlet.ServletException;
  6. import javax.servlet.http.HttpServlet;
  7. import javax.servlet.http.HttpServletRequest;
  8. import javax.servlet.http.HttpServletResponse;
  9. import com.zenika.contact.domain.Contact;
  10. import com.zenika.contact.service.ContactService;
  11. public class ContactServlet extends HttpServlet {
  12. @EJB
  13. private ContactService contactService;
  14. @Override
  15. protected void doGet(HttpServletRequest req, HttpServletResponse resp)
  16. throws ServletException, IOException {
  17. List<Contact> contacts = contactService.select();
  18. if(contacts.isEmpty()) {
  19. resp.getWriter().append(« <p>No contact.</p> »);
  20.  25; else {
  21. for(Contact contact : contacts) {
  22. resp.getWriter().append(« <p> »)
  23. .append(contact.getFirstName())
  24. .append( » « )
  25. .append(contact.getLastName())
  26. .append(« </p> »);
  27. }
  28. }
  29. }
  30. }

There’s also a servlet listener, to populate the database :

  1. package com.zenika.contact.web;
  2. import javax.ejb.EJB;
  3. import javax.servlet.ServletContextEvent;
  4. import javax.servlet.ServletContextListener;
  5. import com.zenika.contact.domain.Contact;
  6. import com.zenika.contact.service.ContactService;
  7. public class PopulateDataBaseListener implements ServletContextListener {
  8. @EJB
  9. private ContactService contactService;
  10. @Override
  11. public void contextInitialized(ServletContextEvent sce) {
  12. if(contactService.select().isEmpty()) {
  13. contactService.create(< span style= »color: #7F0055; font-weight: bold; »>new Contact(« Arnaud »,« Cogoluegnes »));
  14. contactService.create(new Contact(« Carl »,« Azoury »));
  15. contactService.create(new Contact(« Olivier »,« Croisier »));
  16. }
  17. }
  18. @Override
  19. public void contextDestroyed(ServletContextEvent sce) { }
  20. }

The web.xml of the application :

  1. <web-app xmlns:xsi=« http://www.w3.org/2001/XMLSchema-instance »
  2. xmlns=« http://java.sun.com/xml/ns/javaee » xmlns:web=« http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd »
  3. xsi:schemaLocation=« http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd »
  4. id=« rest » version=« 2.5 »>
  5. <display-name>Zen Contact JEE</display-name>
  6. <listener>
  7. <description>Add some rows in the database if necessary.</description>
  8. <listener-class>com.zenika.contact.web.PopulateDataBaseListener</listener-class>
  9. </listener>
  10. <servlet>
  11. <servlet-name>contact</servlet-name>
  12. <servlet-class>com.zenika.contact.web.ContactServlet</servlet-class>
  13. </servlet>
  14. <servlet-mapping>
  15. <servlet-name>contact</servlet-name>
  16. <url-pattern>/contact.do</url-pattern>
  17. </servlet-mapping>
  18. </web-app>

By now, you should have something like the following : Overview of the project

Setting up the database

We’ll be using Derby, which ships with the JDK (as of version 1.6). Go to JAVA_HOME/db/bin and start the server with the startNetworkServer script.

Testing the EJB with Spring

Let’s say our EJB is much more complicated, with complex queries and business algorithms. Thus we need to test it against the database. The embedded EJB container API works but I found the one from Glassfish quite long to start and it remains difficult to switch from a test database to the server database, as the container picks up by default the META-INF/persistence.xml, which points to a JNDI datasource. Moreover, what we need in our test are transaction management and dependency injection (for the PersistenceContext). Spring offers a lightweight yet powerful solution to do that.

Configuring JPA with Spring

We can start by declaring the EJB in the Spring application context :

  1. <?xml version=« 1.0 » encoding=« UTF-8 »?>
  2. <beans xmlns=« http://www.springframework.org/schema/beans »
  3. xmlns:xsi=« http://www.w3.org/2001/XMLSchema-instance » xmlns:aop=« http://www.springframework.org/schema/aop »
  4. xmlns:context=« http://www.springframework.org/schema/context » xmlns:tx=« http://www.springframework.org/schema/tx »
  5. xsi:schemaLocation=« http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
  6. <bean class=« com.zenika.contact.service.impl.ContactServiceImpl » />
  7. <context:annotation-config />
  8. </beans>

Note the use of the context:annotation-config element, which tells Spring to automatically inject PersistenceContexts.
Thanks to Spring JPA support, we can use a specific persistence file, let’s call it persistence-test.xml :

  1. <?xml version=« 1.0 » encoding=« UTF-8 »?>
  2. <persistence xmlns=« http://java.sun.com/xml/ns/persistence »
  3. xmlns:xsi=« http://www.w3.org/2001/XMLSchema-instance »
  4. xsi:schemaLocation=« http://java.sun.com/xml/ns/persistence
  5. version=« 1.0 »>
  6. <persistence-unit name=« contact » transaction-type=« RESOURCE_LOCAL »>
  7. <provider>org.hibernate.ejb.HibernatePersistence</provider>
  8. <class>com.zenika.contact.domain.Contact</class>
  9. <exclude-unlisted-classes />
  10. </persistence-unit>
  11. </persistence>

Why do we use Hibernate for our test? Mainly because it enhances classes at runtime and does not need specific feature like load-time weaving through a JVM agent.
Let’s see now how to complement the Spring configuration :

  1. <bean ()>
  2. (…)
  3. <bean id=« dataSource »
  4. class=« org.springframework.jdbc.datasource.SingleConnectionDataSource »>
  5. <property name=« driverClassName » value=« org.apache.derby.jdbc.ClientDriver » />
  6. <property name=« url »
  7. value=« jdbc:derby://localhost:1527/contact_test;create=true » />
  8. <property name=« username » value=« APP » />
  9. <property name=« password » value=« APP » />
  10. <property name=« suppressClose » value=« true » />
  11. </bean>
  12. <bean id=« contactEntityManagerFactory »
  13. class=« org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean »>
  14. <property name=« persistenceXmlLocation » value=« /com/zenika/contact/service/impl/persistence-test.xml » />
  15. <property name=« dataSource » ref=« dataSource » />
  16. <property name=« jpaProperties »>
  17. <props>
  18. <prop key=« hibernate.dialect »>org.hibernate.dialect.DerbyDialect</prop>
  19. <prop key=« hibernate.hbm2ddl.auto »>create</prop>
  20. <prop key=« hibernate.show_sql »>false</prop>
  21. </props>
  22. </property>
  23. <property name=« jpaDialect »>
  24. <bean class=« org.springframework.orm.jpa.vendor.HibernateJpaDialect » />
  25. </property>
  26. </bean>
  27. </beans>

What about transaction management? We can add it declaratively, with the Spring JPA transaction manager and by decorating Spring beans that hold the @Stateless annotation :

  1. <bean ()>
  2. (…)
  3. <bean id=« transactionManager » class=« org.springframework.orm.jpa.JpaTransactionManager »>
  4. <property name=« entityManagerFactory » ref=« contactEntityManagerFactory » />
  5. </bean>
  6. <aop:config>
  7. <aop:pointcut expression=« within(@javax.ejb.Stateless *) » id=« ejbPointcut »/>
  8. <aop:advisor advice-ref=« transactionInterceptor » pointcut-ref=« ejbPointcut » />
  9. </aop:config>
  10. <tx:advice id=« transactionInterceptor »>
  11. <tx:attributes>
  12. <tx:method name=« * » />
  13. </tx:attributes>
  14. </tx:advice>
  15. </beans>

Writing the test

We’re ready now to test the EJB. The Spring TestContext Framework can handle the Spring application context lifecycle and would even cache it if other tests were using it, making the test suite faster. Here is the test :

  1. package com.zenika.contact.service.impl;
  2. import javax.sql.DataSource;
  3. import org.junit.Assert;
  4. import org.junit.Test;
  5. import org.junit.runner.RunWith;
  6. import org.springframework.beans.factory.annotation.Autowired;
  7. import org.springframework.jdbc.core.JdbcTemplate;
  8. import org.springframework.test.context.ContextConfiguration;
  9. import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
  10. import com.zenika.contact.domain.Contact;
  11. import com.zenika.contact.service.ContactService;
  12. @RunWith(SpringJUnit4ClassRunner.class)
  13. @ContextConfiguration
  14. public class ContactServiceSpringTest {
  15. @Autowired
  16. ContactService contactService;
  17. @Autowired
  18. DataSource dataSource;
  19. @Test public void create() {
  20. JdbcTemplate template = new JdbcTemplate(dataSource);
  21. int count = template.queryForInt(« select count(1) from contact »);
  22. Contact contact = new Contact();
  23. contact.setFirstName(« Arnaud »);
  24. contact.setLastName(« Cogoluegnes »);
  25. contactService.create(contact);
  26. Assert.assertEquals(count+1,template.queryForInt(« select count(1) from contact »));
  27. }
  28. @Test public void select() {
  29. JdbcTemplate template = new JdbcTemplate(dataSource);
  30. template.update(« delete from contact »);
  31. template.update(« insert into contact (id, firstName, lastName) values (default,?,?) », « Arnaud »,« Cogoluegnes »);
  32. Assert.assertEquals(1,contactService.select().size());
  33. }
  34. }

You can now run the test with Eclipse for instance. The test within Eclipse

Running the application on Glassfish

Before deploying the application, you need to install Glassfish and configure the JNDI datasource.

Setting up Glassfish

You can download Glassfish from its web page. We use here Glassfish 3.0. Once you have the archive, unzip it in a directory, go to the bin directory and start the server :

 ./asadmin start-domain

You can then access the admin console from the following URL: http://localhost:4848/

Setting up the datasource

The set up of the datasource is made of two steps: the pool and then the JDBC resource. Create first a pool for Derby, which uses the contact database (and creates it if necessary) : Pool configuration
And then, create the JDBC resources that uses this connection pool : JDBC resource

Deploying the application

Once packaged (see source code enclosed to this post), the application can be deployed on Glassfish. All the components we wrote have been detected : Deploying the application
You can check the application at http://localhost:8080/jeecontact-1.0.0/contact.do The application

Conclusion

Spring is not only able to power applications from the data access layer to the UI layer, it can also help you developping transactional components like EJBs. This is especially valuable when queries become complex and you want to clearly separate the test environment from the server environment, in a highly configurable and simple way.

Une réflexion sur “Using Spring to develop EJB 3.1

  • Hmmm. 80+ lines of XML ? 😉
    I’d suggest using @Startup or @PostConstruct to populate the database and removing web.xml altogether.
    Also, look into CDI’s @Alternative for switching between databases.

    Répondre

Répondre à Alexis MPAnnuler la réponse.

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.

En savoir plus sur Blog Zenika

Abonnez-vous pour poursuivre la lecture et avoir accès à l’ensemble des archives.

Continue reading