In each Issue

Feedback

Developing web apps: Spring is here

by Patrick Smith, Greg Lapouchnian

-->

Many Java Web applications use EJBs (Enterprise Java Beans) to encapsulate
business logic and manage persistence, and use Struts to facilitate
the creation of the frontend of the application. However,
recently a number of lightweight frameworks have been developed to
simplify the creation of enterprise
applications. The Spring Framework is the current front-runner.

This two-part article explores how you can greatly improve an application
and simplify its maintenance. Part one describes integrating Spring
gradually into an existing EJB/Struts web application with the goal
of eventually taking out EJBs completely; part two discusses removing
EJBs entirely in favor of using Hibernate
for persistence.

The Spring framework

The Spring framework was created to simplify the development of JEE
(formerly J2EE) web applications. Spring relies upon the ideas of
IoC (Inversion of Control)[1] and
AOP (Aspect Oriented Programming)[2]
to simplify the development process.
Spring also allows for integration with a variety of technologies—including
those that it is capable of replacing, such as Struts.

Spring relies on a flavor of inversion of control known as dependency
injection. Application objects configured as Spring beans do not
need to retrieve their dependencies; instead, Spring constructs these
dependencies and injects them at runtime. This removes the
dependence on container APIs and glue code
from the objects. (For instance, instead of looking up a datasource
using JNDI inside an application server, the datasource object is
injected into an object that depends on it using a JavaBean
setter method.)

Once dependencies within an application are externalized in a Spring
configuration file, it becomes easier to reconfigure parts of the
application by simply injecting a different Spring bean as a
dependency. An application configured using dependency injection
becomes simpler to test as mock objects can be injected as
dependencies during testing in place of real objects. The business
objects will not depend on container APIs that would not be available
during testing, and in most cases have no dependencies on Spring's
APIs.

Spring is modular and does not require the application developer to use it
for all parts of a program. It is easy to gradually introduce Spring
into an existing project, as we will show in the rest of this
article.

Spring also allows developers to choose from among a number of options for
various parts of their applications. When it comes to the web tier,
Spring can integrate with web
MVC[3] frameworks such as
Struts (as will be shown), WebWork or
Tapestry, or it can rely completely on its own MVC framework,
SpringMVC. The same principle applies to
persistence; developers can choose a framework that best suits their
needs. JDBC, Hibernate (used in part two of this article), and JDO
are all available as data-access technologies and are tightly
integrated into Spring.

Introduction to the examples

The sections that follow walk you through some of the steps we took to
convert an existing web application built upon EJBs and Struts to a
variety of different versions of the same application, all while
maintaining the same look and feel of the initial application. The
example used is called Olstore, which is an
online store demonstration application
used as an example program by the Red Hat Application Server. First
we integrate Spring into Olstore using a
minimalistic approach: Spring is simply a communication layer between
the EJBs and Struts. In the second example we remove Struts. In the
third example we remove EJBs to produce a "Spring and Hibernate"
application. In the end, we have four versions of the application.
While all appear almost identical to the user, their internals are
(in some cases) dramatically different.

This allows the Spring framework to load when the application is deployed
and ensures that all the Spring beans and their dependencies are
created.

Next, in order to bring Spring into our EJB and Struts application, we need
to use Spring's support for Struts. This leaves all of the existing
EJBs and most of the Struts code unchanged. In order to enable the
Struts actions in the current application to access Spring-managed
beans, the classes that extended org.apache.struts.action.Action
were modified to extend org.springframework.web.struts.ActionSupport.

ActionSupport provides a number of helpful methods, a key one being
getWebApplicationContext().
From the WebApplicationContext
object we can retrieve any bean configured in Spring using the
getBean() method.

Spring uses the applicationContext.xml
configuration file to configure the Spring beans; you place the file
inside the web application's WEB-INF
directory so that Spring can find it. We required two types of
configurations of Spring beans to ensure that the functionality of
the application remained the same. The first was used to connect to
stateless[4]
beans via a proxy, and the second was used to connect to a
stateful[5] bean.

The LocalStatelessSessionProxyFactoryBean
type used in the class field enables the configuration of a Spring
bean proxy for an existing stateless EJB in the application. All that
is required is to provide the business interface (a
POJO[6] interface class)
and the JNDI name to use when looking up the EJB.
This proxy enables the EJB to be treated as a POJO, thus freeing you
from dealing with EJB-specific details in your code. As the Spring
configuration file becomes more complex throughout the conversion
process the benefits of dependency injection will become more
apparent. A detailed example of one such situation will be presented
in part two of the article.

The JndiObjectFactoryBean type
follows the same basic principles, although a business interface is
not required because the JndiObjectFactoryBean
is not creating a proxy to an existing EJB, but rather is simplifying
how to access the EJB. JndiObjectFactoryBean
simplifies your code by doing the JNDI lookup and returning the home
interface for you.

The original Struts action required the following EJB specific code to
access the EJBs:

Here, EJBHomeFactory is a helper
class that uses a JNDI name to return an EJB LocalHome object, which
in turn is used to
create an instance of the EJB. This binds the non-EJB code tightly
to the EJBs themselves. In contrast, our new Spring-enabled actions
access the bean through a simple line of code:

The parameter passed to getBean()
is the ID of the bean configured above. Also note that the
return value is the POJO business interface, which enables our action
code to be EJB-independent. The Spring approach accesses the exact
same bean as the original method, yet requires less code and has a
more defined separation between the front end and back end of the application.

The Spring framework is divided up among a variety of JAR files (an
all-in-one JAR is also available), each handling a different aspect
of Spring's functionality. This enables you to package only what is
needed in your application. In the case of this first integration of
Spring, the required JARs are:

spring-beans.jar

spring-context.jar

spring-core.jar

spring-remoting.jar

spring-web.jar

Packaging more JARs with your application, or even the all-in-one spring.jar,
will not affect the performance of the application, merely the size.

Spring integration, step 2 - SpringMVC replaces Struts

Next, we move on to replacing the view layer of the web application.
SpringMVC provides support for many view technologies such as JSP,
FreeMarker, Velocity, Tiles, iText, and POI. The example used in
this article was originally written using JSPs, so we continued to
use JSPs throughout the integration process. This required
modifications to the JSPs to access the Spring content (more on this
later).

The controllers

Similar to the first integration of Spring, the EJBs are left in place, and
Struts is replaced with SpringMVC. The first steps in the migration
from Struts to SpringMVC are to remove the Struts Action
classes and to create new Spring Controller
classes. This is accomplished by extending the provided Spring
org.springframework.web.servlet.mvc.Controller
class (or any of the other provided controller classes) and
overriding the handleRequest()
method. The handleRequest()
method returns a ModelAndView
object, which helps illustrate a key advantage of SpringMVC over
Struts by accessing data from the Action/Controller in the view. The
ModelAndView
object that we returned takes two parameters, a String
that represents the name of the view (resolved to a file name by a
org.springframework.web.servlet.ViewResolver
bean specified in the olstore-servlet.xml
configuration file), and a java.util.Map
object that represents the model.

Example:

Note that in the example, the type object is a java.lang.String,
and the productList is a
java.util.List object. As the
model is a java.util.Map, you
can pass in any object you want and have that object (and fields)
available in the view, accessing them by using the key value given
when adding objects to the java.util.Map.
Conversely, in Struts a new Form class was needed for most Actions.
Accessing information while using Struts also required creating a
field for each object in the Form and adding set and get
methods. This makes the SpringMVC alternative a much more attractive
solution.

For example, if we wanted to print the value of the type String in our JSP, we would simply enter the following line:

<c:out value="${type}"/>

The c:out tag shown is available via the
JSTL[7]
library, which we used extensively for displaying content, looping
through lists, and handling conditionals. The original JSPs needed
to be converted to use the JSTL tags instead of the ones provided by
Struts, and information received in the views had to be changed. The overall result of the JSP changes made them easier to
read and completely decoupled from the EJBs.

The configuration

The configuration of the web-specific beans is placed in a separate file.
This file must be located in the WEB-INF directory of the application
and must be named servlet_name-servlet.xml, where servlet_name
is the name used in the web.xml
configuration of the DispatcherServlet
class. In this example the servlet_name
is Olstore:

The configuration of the bean above relies on the
BeanNameUrlHandlerMapping
class, one of the two HandlerMapping
implementations provided by Spring. The DispatcherServlet
looks for the HandlerMapping bean that is
defined in the servlet_name-servlet.xml configuration file. In
this case, BeanNameUrlHandlerMapping
bean is defined and therefore a request for /admin/createItem.do (after the servlet mapping part of the URL) will direct the request
to the bean with the name defined above. The class that will end up
processing the request is ItemController.

Interceptor

Another convenient feature used was Spring's ability to introduce an
interceptor into the request processing chain. An interceptor's
property can be added to the BeanNameUrlHandlerMapping
bean to specify which interceptors are to be invoked. Here is a
snippet of the configuration that accomplishes this:

Here, shoppingCartInterceptor is a Spring bean defined in the same
configuration file. The class that implements it,
ShoppingCartInterceptor,
extends HandlerInterceptorAdapter.
Three methods can be overridden to introduce additional logic,
preHandle(), postHandle()
and afterCompletion(). In this
case, the preHandle() method
is overridden in the ShoppingCartInterceptor
class to make sure that a ShoppingCart
object is always present in the HttpSession
before a request reaches a controller.

The reduction in configuration complexity is important both for ease of
development and for tracking down potential configuration bugs.

Stay tuned for next month's issue of Red Hat Magazine where we will detail
our experience replacing EJBs with Hibernate and how Spring's
built-in support for Hibernate simplifies this task. As more of the
application is converted to use Spring components the benefits of the
framework will become more apparent.

Footnotes

[1]Inversion of Control creates the objects for the developer, so they
do not have to be created manually. IoC allows you to define
properties of an object in an XML and inject properties into objects
either through a constructor argument or through setter methods. This
allows properties to be modified via an XML file rather then making
changes throughout various classes.

[2]Aspect Oriented Programming is the encapsulation of cross-cutting concerns
(those that end up scattered all over the program).
This keeps essential aspects of a program (such as logging or security)
separate from other parts of the program.

[3]Model-View-Controller (MVC) is an architecture for developing Web
Applications that separates an applications data (Model), user
interface (View), and controlling logic (Controller) into three
components.

[4]Stateless Beans are shared objects that do not have an associated state;
this allows concurrent access to the beans.

[5]Stateful Beans are shared object that have an associated state.
Access to such beans is limited to one client.

More information

About the authors

Greg Lapouchnian (glapouch@redhat.com)
and Patrick Smith (pasmith@redhat.com)
are both engineering interns in the Toronto office and students at
the University of Toronto.
They have spent the majority of their internship working on web applications.