Migrating from the Spring Framework to OSGi using the WebSphere Application Server V7 Feature Pack for OSGi Applications

Run Spring applications in an OSGi container then migrate to a standards-based dependency injection container

The Spring and OSGi frameworks are powerful tools for application development and offer significant value to application developers. With the release of the IBM® WebSphere® Application Server V7 Feature Pack for OSGi Applications, WebSphere Application Server now offers a scalable standards-based runtime for OSGi applications and dependency injection. This article describes how to get the basic modularity benefits of OSGi by deploying a Spring-based Java™ EE application to the OSGi container, and then how to migrate the Spring XML bean definition to the standards-based OSGi Blueprint component model to obtain the full benefit of declarative OSGi services, and to remove the burden of the dependency injection container from the application. This content is part of the IBM WebSphere Developer Technical Journal.

Valentin Mahrwald (mahrwald@uk.ibm.com), OSGi Applications Development Engineer, IBM

Valentin Mahrwald is a software engineer at the IBM Hursley lab. He is actively involved in the OSGi Applications feature pack for WebSphere Application Server as well as the Apache Aries project. In his spare time he pursues his interest in languages, programming or otherwise. Valentin received an MMath for Mathematics and Computer Science from the University of York.



Tim Ward, OSGi Applications JPA lead, IBM

Tim Ward is a design and development lead for the OSGi Applications feature of WebSphere Application Server. He is an active participant in the OSGi Enterprise Expert Group (EEG) where he co-authored the OSGi JPA Service specification and is currently leading several RFCs. Tim is also a committer in the Apache Aries project, a JPA advocate, and one of IBM's key Spring Framework experts.



Zoe Slattery, OSGi Technology Evangelist, IBM

Zoe Slattery is a Technology Evangelist working for IBM Hursley, United Kingdom. Her current interests are in in Enterprise OSGi, in particular the development of open source OSGi components in Apache Aries. Zoe is also a member of the Apache Software Foundation.



30 March 2011

Also available in Chinese

Introduction

The Spring Framework and its associated projects are a widely used set of Apache licensed libraries that aim to make the Java Standard Edition (Java SE) and Java Enterprise Edition (Java EE) environments more accessible and easier for developers. The Spring libraries enable enterprise technologies (such as the Java Transactions API) to be abstracted, thus applications written to the Spring API and can run in either a Java SE or Java EE runtime without having to change the application logic.

One of the cornerstones of the Spring Framework is the Inversion of Control (IOC) container which provides a flexible, fully featured mechanism for constructing POJOs (Plain Old Java Objects) and performing dependency injection to wire the dependency graph of those objects. This construction and wiring logic is typically provided in XML, however there are a number of other configuration options. The principle value of a Dependency Injection framework such as Spring is that by removing the responsibility for managing object relationships and lifecycle from the application, code can be focussed on directly implementing business logic. Furthermore, well-written application classes can easily be unit tested by providing stub implementations of the injected objects, improving test coverage and code quality.

The Spring Framework has also gained significant adoption due to its Model View Controller (MVC) Web framework, declarative transaction management support, and Data Access Object (DAO) framework. These technologies are typically combined with other Open Source technologies (such as Apache OpenJPA, JBoss, and Apache Tomcat) or with Java EE application servers such as IBM WebSphere Application Server. In combination, these technologies are designed to increase the speed of development of servlet-based Web applications, simplifying their logic and providing enterprise level qualities of service in a lightweight container.

The OSGi Alliance is the standards body responsible for the specification of flexible modularization technology for Java. OSGi provides a framework capable of concurrently running multiple versions of the same code, and a powerful mechanism for expressing the external dependencies and externally provided API of a module, known in OSGi, as a bundle. OSGi defines a number of interfaces and contracts to enable three main enhancements to a Java runtime:

  • The first layer of the OSGi framework is known as the module layer, which defines the ways in which class definitions and resources can be shared in a well encapsulated manner.
  • The second OSGi layer is the lifecycle layer, which defines the ways in which bundles can be dynamically added, removed, started, stopped or modified in a running system. This enables OSGi based systems to be brought up in an orderless fashion, and for bundles to be run independently of one another.
  • The third layer of an OSGi framework is the service layer. The service layer defines a run time mechanism by which object implementations can be dynamically shared in a loosely coupled manner. The services in the service registry can react very quickly to changes in the system, and bundles can actively change their behaviour based upon which services are available in the runtime.

While many development organisations appreciate the value that they can obtain from using frameworks like Spring, they are frequently worried about the burden of maintaining an application container as part of their own application rather than having this provided as part of the underlying middleware platform. These organisations might also be concerned by the large disk and memory footprints that frameworks like Spring add to every application in which they are included.

Furthermore, there are a large number of development organisations that wish to take advantage of the powerful mature modularization technology that OSGi offers, and also wish to leverage the dynamic, flexible application structures that are made possible by the OSGi service registry. These organisations are often discouraged by the perceived difficulty in making use of OSGi, and also by the lack of a clear application programming model.

This article describes how the WebSphere Application Server V7 Feature Pack for OSGi Applications and JPA 2.0 (hereafter referred to as the OSGi feature pack) and its OSGi Blueprint container can be used to gain the benefits of using Spring but in a standards-based fashion with much of the heavy lifting being done by the WebSphere Application Server container. This article also includes detailed guidance on best practices for running and migrating Spring-based applications to OSGi. (See Developing enterprise OSGi applications for WebSphere Application Server for more information on OSGi applications.)

What is the OSGi Blueprint Service?

The Blueprint Service (commonly referred to as Blueprint) is a POJO-based component model for OSGi. It provides a dependency injection container and enables an object's lifecycle to be automatically managed. Blueprint is integrated with the OSGi service registry, enabling managed instances to transparently use, or be exposed as, OSGi services.

For developers who have used the Spring Framework, Blueprint will look extremely familiar. This is because the Blueprint standard was developed in conjunction with SpringSource VMware, the company behind the Spring framework, and is effectively a standardised version of the Spring Dynamic Modules project. As a result, Blueprint offers the same advantages as Spring to application developers, encouraging loose coupling and flexible configuration. There are also a number of other good reasons to use Blueprint (see Resources for an article on OSGi best practices).

The example described in this article will only make use of the basic functions in the Blueprint container, namely dependency injection, exposing beans as services, and referencing services from the OSGi service registry. There are, however, many more advanced functions, including complex object construction, container invoked initialisation and destruction methods, service lifecycle notifications, and service reference lists. Further details can be found in the OSGi Service Platform Enterprise Specification and in the article Building OSGi applications with the Blueprint Container specification, both in Resources.

Prerequisites

This article is written for Spring and Java EE developers who are interested in OSGi and who have a basic understanding of the Spring Framework dependency injection model, as well as a basic understanding of the Java Persistence API (JPA) or a similar object-relational mapping technology. You should be familiar with using Apache Ant build tools and the Java programming language. A modern Java IDE, such as Eclipse, Netbeans, or IntelliJ, although not technically required, will be a great help if you wish to perform the migration steps described here.

To run the examples presented in this article, you need an installation of WebSphere Application Server V7 that has been augmented with the OSGi feature pack. To build the samples, you will need a working copy of Apache Ant at version 1.7.1 or higher.


Introducing the Blether application

This article is structured around migrating a basic social networking application from a Spring-based Java EE application to a Blueprint-based OSGi application. The sample application descriebd here, called Blether, provides a basic social messaging service that enables users to share status updates and to find and track other users.

The next sections provide an overview of the sample application and describes how to build and install it.

Building the Blether application

Initially, the application is a simple Java EE application constructed using Spring and JPA. In the course of this exercise, you will transform it into an OSGi application and then gradually remove the dependency on Spring. Rather than structuring this as one big bang migration, you will see how to migrate individual chunks of the application separately, so that the application remains usable in between the migration steps.

For ease of use, the sample is provided as several "snapshots" corresponding to check points during the migration where the application is fully functional. You can follow the exercise starting from the initial code and migrate the application one piece at a time. Alternatively, you can jump in at any of the other snapshots. The build procedure is identical for all of the stages.

The samples are provided for download in a .zip format file that contains further .zip files, one corresponding to each stage (snapshot) in the development process, and a scripts directory. The scripts directory contains python scripts used to install and uninstall the application. The structure is identical for all snapshot .zip files: a top level sample directory, called sample, contains the top level build file and subdirectories for the persistence and the Web components. Both the subdirectories contain a src directory for the source files as well as a build file and a dependency descriptor (ivy.xml). Both the web and persistence directories contain a resources directory. Most of the configuration files that need to be changed are located in the subdirectories of resources.

To build the application, unzip the appropriate code snapshot from the Download section (beginning with the SampleInitial.zip archive) into your workspace directory. Navigate to sample directory and from there run ant from the command line. This will first bootstrap the project by downloading Apache Ivy and then download the build dependencies. A functional Internet connection is required for this step because all dependencies are retrieved from the central maven repository. After that, the two sub-projects for the persistence JAR (or bundle) and the Web module will be built, and then finally the EAR or EBA will be created at the top level in the build/lib directory.

Although the command line is a great place to build the project, an IDE is a much better choice for actually editing the Java code. The migration steps below assume you are equipped with an IDE that supports basic refactoring, like moving classes into different packages, renaming fields, and fixing imports. To set the project in Eclipse, perform the following steps. The resulting project outline is:

  1. Create a new Java project called Blether.
  2. Import the sample directory that resulted from unzipping SampleInitial.zip above (via Import > File System).
  3. Run the top-level build.xml as described above. In Eclipse, this can be done by right-clicking on the build.xml file and selecting Run As > Ant Build.
  4. Remove the default source folder from the build path and instead add persistence/src and web/src as source folders.
  5. Add all the libraries in persistence/lib and web/lib to the build path.
Figure 1. Eclipse project structure for the Blether application
Figure 1. Eclipse project structure for the Blether application

Installing the Blether application

The scripted installation process is the same for all the snapshots. The first time the sample is installed a database must be created. You can achieve this using scripts/createDb.sql. After that, the scripts/bletherSampleUninstall.py and scripts/bletherSampleInstall.py are used to install and uninstall the application. The steps below are for a Windows® platform. If you use Linux®, just replace *.bat with *.sh.

  1. createDb.sql sets up the database required for the sample application. It only needs to be run once via ${WAS_INSTALL_ROOT}/derby/bin/embedded/ij.bat createDb.sql.
  2. bletherSampleInstall.py installs the sample application and creates the datasources required for a manual installation. To install the sample, run ${WAS_PROFILE_ROOT}/bin/wsadmin.bat -f bletherSampleInstall.py fullInstall <server name> <node name> <path to sample binary>, where the server and node name refer to the names of WebSphere Application Server and node, and "path to sample binary means the fully qualified name of the EAR or EBA file to be installed.
  3. bletherSampleUninstall.py uninstalls the sample application and, optionally, the datasources. To run it, invoke the wsadmin script like this: ${WAS_PROFILE_ROOT}/bin/wsadmin.bat -f bletherSampleUninstall.py <mode>, where the mode can be either by appOnly or full.

Using the Blether application

After you have built the sample, navigate to <WAS_DEFAULT_HOST>/blether. This will present you the welcome panel. The first time you use the application, you will need to create a user ID. To do so, follow the sign up link and fill out the form. After registering your ID, you will be redirected to the main application panel. This main screen shows status messages, both your own and those of users that you track, and has a box for posting new messages.

To verify that the application is working, post a new status message and then log out and create a new user. With the new user, you can track the first user from the users tab at the right. This will list all the users known to the system. Clicking on the first user will bring you to a profile page where you can choose to track the user. The status message that you created initially will appear on your home status tab.

This minimal demonstration of the application's capabilities is sufficient to test the conversion of the application into an OSGi application.


Migration Stage 1: Packaging Blether as an OSGi application

The structure of an OSGi application bundle (or EBA) is very similar to that of a Java EE application archive (EAR). An EBA is made up of zero or more bundles, packaged in a single .zip format archive with an optional application manifest (APPLICATION.MF), and has a .eba extension. In comparison, an EAR file contains one or more modules, packaged in a .zip format archive with an application XML, and has a .ear extension.

Figure 2. The structure of an EAR
Figure 2. The structure of an EAR

The main difference between an EBA and an EAR is that an EAR almost always contains all of the modules inside the archive and must contain the application XML, whereas an EBA typically contains only the application manifest (APPLICATION.MF) and the bundles are ideally retrieved from a bundle repository. (See Best practices for developing and working with OSGi applications, #9.) Referring to bundles from outside the EBA is achieved through configuration in the APPLICATION.MF file. By default, if no APPLICATION.MF or Application-Content header is specified, the application is assumed to be formed from every bundle present inside the EBA. This default behaviour means that it is impossible to have an EBA that contains no bundles and no APPLICATION.MF. It is also invalid for an EBA to contain no bundles if the APPLICATION.MF does not define an Application-Content.

Figure 3. Example structures of an EBA, with and without contained content
Figure 3. Example structures of an EBA, with and without contained content

The first few migration stages described in this article rely on using the default OSGi application behaviour. That is, although you will generate an EBA in this stage, it will not contain an application manifest. You will also be leaving the file sample/resources/META-INF/application.xml in the EBA, as the OSGi feature pack can use the information from this to pre-populate some configuration options.

Before you can make use of any OSGi functionality, you must get the Blether sample running as an OSGi application. This means that the Java EE modules inside the EAR need to be converted into OSGi bundles. This conversion can be performed automatically for a WAR module inside an EBA, although a best practice would be to convert the WAR module into an OSGi bundle before adding it to the EBA. Any other artefacts inside the EAR must be converted manually.

Converting the persistence JAR into a bundle

To convert the Blether application to an OSGi application, start by converting the persistence JAR into an OSGi bundle; more specifically, since this bundle has JPA content, a persistence bundle.

As a first step for converting any kind of utility JAR to a bundle, you need to add an appropriate bundle manifest to the JAR. The bundle manifest defines the identity of the module by its (symbolic) name and version, and by the dependency information for the bundle, such as the packages it provides and the packages required by the classes inside the bundle.

Getting the latter part just right can be somewhat tricky for large JARs. However, there is an excellent tool (Peter Kriens’ bnd tool) for generating or checking this metadata.

In the case of the Blether persistence JAR, the conversion is straightforward and so it is not necessary to use the tool. Looking at the two JPA classes (under the ~sample/persistence/src directory) it is clear that javax.persistence is the only package required beside java.* classes. Because you want to use the two classes from this bundle inside other bundles, you will need to export their packages in the manifest. Listing 1 shows all that is required in the MANIFEST.MF.

Listing 1. A sample persistence bundle manifest
Manifest-Version: 1
Bundle-ManifestVersion: 2
Bundle-SymbolicName: com.ibm.ws.eba.example.blether.persistence
Bundle-Version: 1.0.0
Import-Package: javax.persistence;version="[1.0.0,2.0.0)"
Export-Package: com.ibm.ws.eba.example.springmigration;version="1.0.0"
Meta-Persistence:

Notice that the exported and imported packages have their own version attributes. OSGi enables version to be assigned to packages as well as whole bundles, and furthermore these versions are entirely independent. The package version is the one that is crucial to a user of the package, not that of the bundle, because the package version is used to determine class compatibility. As it is so vital for compatibility between bundles, specifying an explicit version for a package export is a best practice that should always be followed.

The OSGi JPA service specification defines that the JPA 1.0 packages are at version 1.0.0, and best practice versioning policy dictates that any version up to (but not including) the next major version will be compatible. Thus, you specify a version range for the package import, using the standard OSGi syntax indicating that you want a version from 1.0.0 (inclusive) to version 2.0.0 (exclusive). Be aware that the version of a package does not always match the version of the specification that it comes from. In this case, version 1.0.0 of javax.persistence is from JPA 1.0, but JPA 2.0 corresponds to javax.persistence at 1.1.0. (See Best practices for developing and working with OSGi applications, #4.) Know that a version range of "1.0.0" is not the same as "[1.0.0,1.0.0]". A version range with no maximum defined extends to infinity.

In summary, all that is required for this step is to create a file called MANIFEST.MF, with the content shown above, in the persistence/resources/META-INF/ directory.

Changes to the Blether persistence configuration

A persistence JAR (or persistence bundle) defines the persistent classes and the persistence units that the JPA runtime will use to make persistence services available to the application in the form of an EntityManagerFactory.

The persistence configuration file, persistence.xml, can be located anywhere in an OSGi bundle. The default location is META-INF/persistence.xml; to support alternative locations, the JPA extender expects to be told where to find the persistence.xml files using a Meta-Persistence: header in the bundle manifest, as shown above. (A trailing space is required for the header to be valid according to the JAR specification.)

The presence of the Meta-Persistence header with no location, as in the sample manifest, directs the JPA extender to look for the persistence configuration file in its default location, META-INF/persistence.xml. Other locations can be specified using comma separated paths, including paths into nested jars, for example, Meta-Persistence: jpa.xml, persistence.jar!/JPA/persistence.xml.

In general, there are few changes that need to be made to the persistence.xml files to make them work in an OSGi framework. Typically, the only parts that needs changing are the JNDI references for the JTA and non-JTA datasources. In a Java EE scenario, these datasources are often looked up via a java:comp/env namespace lookup, bound via a Web module resource reference. (This is actually a vendor specific option and not defined by the JPA specification, which only requires that global JNDI names be supported in these elements.)

In the sample, the datasource JNDI names will no longer work for a persistence bundle because it has no associated java:comp namespace (as distinct from a Web module, which continues to have one for backwards compatibility). In this sample, you will instead follow the JPA specification and map the datasources directly to global JNDI. There is of course a mechanism to use resource references for persistence bundles, which is actually the best practice, but this would take you too far from the subject of this article. The resulting persistence descriptor is shown in Listing 2.

Listing 2. An OSGi compatible persistence.xml
<persistence xmlns="http://java.sun.com/xml/ns/persistence" 
  version="1.0">
    <persistence-unit name="sampleJPAPersistenceUnit" 
       transaction-type="JTA">
         <jta-data-source>jdbc/userdb</jta-data-source>
         <non-jta-data-source>jdbc/nonXAdb</non-jta-data-source>
         <class>com.ibm.ws.eba.example.springmigration.Post</class>
         <class>com.ibm.ws.eba.example.springmigration.UserInfo</class>
         <exclude-unlisted-classes/>
    </persistence-unit>
</persistence>

In summary, the second step in making changes to the Blether sample is to modify the sample/persistence/resources/META-INF/persistence.xml file to match the sample above.

Changes to the Blether Web module

It is possible to convert the Web module in the EAR into an OSGi bundle using changes similar to those you made to the persistence module. In this case, rather than a Meta-Persistence header indicating that the bundle is a persistence bundle, you would use a Web-ContextPath header to indicate that the bundle is a Web application bundle (WAB). You will perform this step later in the conversion, but for this stage, you will rely upon the automatic conversion that is performed by WebSphere Application Server.

The Spring context in the Blether WAR looks up the EntityManagerFactory that it uses from the Web module's local JNDI, where it is mapped by a persistence-unit-ref in the module's web.xml file. Not surprisingly, as the persistence unit is no longer defined by Java EE, no such name will be populated by the OSGi JPA runtime. Instead, the JPA runtime publishes the persistence unit (that is, the EntityManagerFactory) using the primary OSGi integration mechanism, the OSGi service registry. This is not a problem though because the OSGi JNDI specification enables applications to use JNDI as an integration layer for the OSGi service registry using the osgi:service namespace.

Hence, the only change needed for the Web module is to change this line in the Spring descriptor file sample/web/resources/WEB-INF/springapp-service.xml, from:

<jee:jndi-lookup id="entityManagerFactory" jndi-name="jpa/emf"/>

to:

<jee:jndi-lookup id="entityManagerFactory" lookup-on-startup="false" proxy-interface="javax.persistence.EntityManagerFactory" jndi-name="osgi:service/javax.persistence.EntityManagerFactory"/>

How does this work? First of all, the new JNDI name instructs the handler to return the service from the service registry that is published under the javax.persistence.EntityManagerFactory interface. So, instead of looking up the JPA service by unit name, you are using only the interface here. This lookup is unambiguous because OSGi applications are isolated and Blether defines only a single persistence unit. In a scenario with multiple persistence units, the selection can be constrained by persistence unit name using an LDAP style filter. For example, the JNDI name osgi:service/javax.persistence.EntityManagerFactory/(osgi.unit.name=sampleJPAPersistenceUnit) would select only EntityManagerFactory services with the specification defined property osgi.unit.name set to the name of the persistence unit; in this case to sampleJPAPersistenceUnit.

The change to the Spring application context also adds a layer of laziness to the JNDI lookup by setting lookup-on-startup to false, which also requires the proxy-interface attribute to be specified. With this property set, the Spring container will only retrieve the EntityManagerFactory service when it is first needed. This has been added to eliminate a race condition that exists between the Web module and the persistence bundle. In Java EE, the startup of these two modules is orchestrated so that any resources are created before proceeding to further startup tasks, which occur in a well-defined order. In contrast, OSGi applications and OSGi runtimes use a set of loosely-coupled collaborating bundles, and usually accomplish their respective tasks asynchronously. This enables faster startup, and more flexible and more robust applications, but usually requires additional laziness as there are no guarantees about startup ordering.

At this stage, you can also optionally remove the persistence-unit-ref elements in the web.xml because they no longer serve any purpose. However, their presence does not cause any problems.

Building the Stage 1 sample application

At this point, you have made all the code changes required for Stage 1. The code corresponding to this point in the migration can be found in SampleStage1.zip. However, because you need to build an EBA and not an EAR, you need to make some small changes to the build scripts, sample/persistence/build.xml and sample/build.xml.

In the sample/persistence/build.xml script:

  1. Add a manifest attribute (for example manifest="resources/META-INF/MANIFEST.MF") to the jar task.
  2. Add a line to exclude the MANIFEST.MF in the fileset inside the jar task; for example:

    <fileset dir="${basedir}/resources" includes="**/*">
    <exclude name="META-INF/MANIFEST.MF" />
    </fileset>

In the sample/build.xml script:

  1. Instead of creating blether.ear, create a .zip file called blether.eba; for example, <zip destfile="build/lib/blether.eba">. Remember to replace the terminating /ear> with /zip>.
  2. Remove the three lines that exclude application.mf from the EAR file, and add a line (<fileset dir="resources" includes="**/*" />) that ensures that it is included in the blether.eba.

Rebuilding after these changes, or from the SampleStage1.zip snapshot, will create the new OSGi application binary in build/lib. The application is now fully functional as an OSGi application. Test it in WebSphere Application Server by first using the scripts/bletherSampleUninstall.py script to remove the previous version, followed by the scripts/bletherSampleInstall.py script to install the EBA. Verify that you can run the Blether application.


Migration Stage 2. From Spring to the OSGi Blueprint Container

In the previous section, you converted the Blether sample application to an OSGi application. With these changes in place, the next task is to replace the Spring dependency injection framework with the Blueprint framework. You will also see how common Spring utilities such as JpaTemplate can be replaced using just the standard Java EE APIs.

As in the previous section, you will make changes to the persistence bundle first and then to the Web bundle.

Refactoring the Blether service

There are several changes you must make to the Blether persistence service. First, it depends on the JpaTemplate class. This dependency needs to be removed. Also, the persistence service is implemented in BletherImpl* which is part of the Web module. It would make more sense to separate the presentation layer (the Web module) and the business (or persistence) layer. For that reason, you will move the Blether service to the persistence bundle, from where the Web module can access it via the OSGi service registry. This will also give you the opportunity to introduce the first bit of Blueprint.

Removing the use of JpaTemplate in the Blether implementation

The Blether service uses the JpaTemplate utility class that the Spring framework provides to add additional methods for JPA. JpaTemplate is modelled after the javax.persistence.EntityManager interface but does not actually implement it. This means that the majority of methods on JpaTemplate are also available in the standard interface, in particular the basic facilities: persist, merge, and find.

The first step in removing the dependency is to replace org.springframework.orm.JpaTemplate import with javax.persistence.EntityManager at the start of the Blether service class (BletherImpl.java). It is also necessary to add the import javax.persistence.PersistenceContext;.

Second, you need to change the annotation from @PersistenceUnit, which corresponds to JpaTemplate or javax.persistence.EntityManagerFactory, to @PersistenceContext, which corresponds to javax.persistence.EntityManager. You will also rename the field jpaTemplate to something less misleading, such as persistenceService.

Start with the code shown in Listing 3 and apply the changes listed above. The result should yield the code shown in Listing 4.

Listing 3. Blether service persistence context injection
  @PersistenceUnit(unitName = "sampleJPAPersistenceUnit")
  private JpaTemplate jpaTemplate;

  public void setJpaTemplate(JpaTemplate j)
  {
    jpaTemplate = j;
  }
Listing 4. Modified Blether service persistence context injection
  @PersistenceContext(unitName = "sampleJPAPersistenceUnit")
  private EntityManager persistenceService;

  public void setPersistenceService(EntityManager em)
  {
    persistenceService = em;
  }

After applying these changes, a global replace of jpaTemplate.find with persistenceUnit.find should leave four compilation errors in the file corresponding to cases where you are using additional capabilities of JpaTemplate, which are not available in EntityManager. In particular, you are missing the find(String, Object ...) method. The four methods that require changes are:

  • checkEmailUniqueness
  • getPostsByUser
  • getPostsForUserProfile
  • searchUserName

As an example, the getPostsByUser method needs to change from what is shown in Listing 5 to the code shown in Listing 6.

Listing 5. getPostsByUser using JpaTemplate
  @SuppressWarnings("unchecked")
  public List<Post> getPostsByUser(String username)
  {
    List<Post> result = jpaTemplate.find(getMyPostsQuery, username);
    return result;
  }
Listing 6. getPostsByUser using only JPA standard interfaces
  @SuppressWarnings("unchecked")
  public List<Post> getPostsByUser(String username)
  {
    Query query = persistenceService.createQuery(getMyPostsQuery);
    query.setParameter(1, username);
    return query.getResultList();
  }

Equivalent changes need to be made to fix the remaining three compilation errors. Thus, the method checkEMailUniqueness becomes the code shown in Listing 7.

Listing 7. checkEMailUniqueness using only JPA standard interfaces
  @SuppressWarnings("unchecked")
  public boolean checkEMailAddressUniqueness(String emailAddress)
  {
    boolean result = false;

    Query query = persistenceService.createQuery(uniqueEmailAddressQuery);
    query.setParameter(1, emailAddress);
    List<UserInfo> users = (List<UserInfo>) query.getResultList();

    if (users.isEmpty()) {
      result = true;
    }

The method getPostsForUserProfile becomes the code shown in Listing 8.

Listing 8. getPostsForUserProfile using only JPA standard interfaces
  @SuppressWarnings("unchecked")
  public List<Post> getPostsForUserProfile(String userName)
  {
    Query query = persistenceService.createQuery(getPostsVisibleToUserQuery);
    query.setParameter(1, userName);
    return query.getResultList();
  }

Finally, searchUsername(String usernameFilter) becomes the code shown in Listing 9.

Listing 9. searchUsername using only JPA standard interfaces
  public List<UserInfo> searchUsername(String usernameFilter)
  {
    usernameFilter += "%";
    Query query = persistenceService.createQuery(searchUserNameQuery);
    query.setParameter(1, usernameFilter);
    return query.getResultList();
  }

At this point, you have finished making changes to the BletherImpl.java class.

Moving the Blether service into the persistence bundle

You need to move the application logic that uses the JPA services from theWeb module to the persistence bundle. It's this stage that really mandates a good Java IDE, such as Eclipse. (Describing the changes to the files necessary to refactor the Blether code would add no value to this article, as the IDE takes care of them for you.)

To refactor:

  1. Move the class BletherUserInterface.java into the com.ibm.ws.example.springmigration package in the persistence bundle.
  2. Move the class BletherImpl.java into the com.ibm.ws.example.springmigration.impl in the persistence bundle.

None of these changes require an update to the persistence bundle's manifest because no new packages are exported or imported.

Rewiring the Blether service to use Blueprint

With the Blether service now in the persistence bundle, you need to ensure that it can be reached by servlets in the Blether WAR. As you have seen already, the place (in an OSGi environment) to exchange services is the OSGi service registry. You can temporarily wire to the OSGi registry from Spring via the osgi:service namespace. In the Spring descriptor (sample/web/resources/WEB-INF/springapp-service.xml), remove the bletherService bean, the JNDI lookup of the EntityManagerFactory and the jpatemplate bean. In their place, you want a single lookup for the Blether service as shown in Listing 10.

Listing 10. Obtaining the persistence service from the service registry
  <jee:jndi-lookup lookup-on-startup="false" id="bletherService" 
   proxy-interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" 
   jndi-name="osgi:service/com.ibm.ws.eba.example.springmigration.BletherUserInterface"/>

You will also need to make the Blether service available in the service registry. In plain OSGi, you would accomplish this by adding a bundle activator to the persistence bundle to instantiate the Blether service and then use the API on org.osgi.framework.BundleContext to publish it to the service registry. However, this would be unnecessary scaffolding that can be avoided by using a dependency injection framework like Blueprint.

Instead, you will add a Blueprint descriptor to the persistence bundle. To do this, create sample/persistence/resources/OSGI-INF/blueprint/blueprint.xml with the content shown in Listing 11.

Listing 11. Blueprint descriptor for the persistence bundle
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0">

  <bean id="bletherImpl"
    class="com.ibm.ws.eba.example.springmigration.impl.BletherImpl" />

  <service ref="bletherImpl" 
    interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" />

</blueprint>

As you would expect, the Blueprint descriptor looks very similar to the Spring descriptor. The top level element and namespace declaration have changed, but the bean declaration is essentially the same. The service element is more interesting. This publishes the bletherImpl bean into the service registry under the BletherUserInterface interface. Specifying the interface explicitly might seem a bit constraining and lead to the need to change the XML when the interface is renamed or moved to another package. An alternative would be to specify the attribute auto-export=”interfaces”, which will register the bean as an OSGi service against all the interfaces implemented by the bean. In this example, both would yield the same result.

The final difference is the injection of the JPA service, which is absent from the Blueprint descriptor. With Blueprint and the OSGi JPA, the injection of the EntityManager occurs under the covers based on the @PersistenceContext specified in BletherImpl. (This injection only happens for instances managed by the Blueprint container.)

This requirement might not always be satisfied. For example, there is no way to match up JPA annotations with a bean created by a factory. For such cases, there is a Blueprint extension namespace for injecting JPA services, this mechanism also gets used under the covers in the annotation only case. Listing 12 shows an example of the Blueprint XML required to construct beans from factory classes in Blueprint. (This is shown for reference only and is not used in this migration example.)

Listing 12. Blueprint descriptor for the persistence bundle using JPA extensions
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
  xmlns:jpa="http://aries.apache.org/xmlns/jpa/v1.0.0">

  <bean id="bletherImpl"
    class="com.ibm.ws.eba.example.springmigration.impl.BletherFactory"
    factory-method=”createBletherService”>
    <jpa:context property=”entityManager” unitname=”sampleJPAPersistenceUnit” />
  </bean>
  
  <service ref="bletherImpl" 
    interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" />

</blueprint>

With these changes, the persistence bundle and core application logic is in good (OSGi) shape. You can now rebuild and reinstall the the application to check that everything is still working. The application at this checkpoint is available in the SamepleStage2.zip file.


Migration Stage 3: Changing the Blether Web module to use to Blueprint

With the persistence and application layer migrated, you can now turn your attention to the remaining Web layer. The goal is to replace the use of Spring descriptors and Spring specific APIs with Blueprint and standard APIs. You will tackle this in two steps. First, remove the dependency on Spring's HttpRequestHandler mechanism and then migrate the Spring descriptor.

Replacing the HttpRequestHandler mechanism

A common concern with Spring Web applications is that servlets declared in the Web descriptor are managed by the Web container and not by Spring. This means that they cannot use the benefits provided by the Spring dependency injection framework. Furthermore, bridging logic has to be written for servlets to use beans defined in the Spring descriptor. This is a less than desirable state of affairs. Fortunately, the Spring framework includes a utility, HttpRequestHandlerServlet, that handles the bridging logic and delegates to HttpRequestHandler. These provide Spring users with an equivalent of servlet functionality.

Blueprint as a plain dependency injection framework has exactly the same problem with bridging between the Web container and the Blueprint container. However, there is no standardised facility like the HttpRequestHandlerServlet in this case. Instead, you are going to develop a reusable mechanism to provide the same functionality.

Create the AppRequestHandler interface

You need to create a new interface that encapsulates the same functionality as HttpRequestHandler in /web/src/com/ibm/ws/eba/example/springmigration/web/. The interface defines a generic abstraction of a servlet handling an arbitrary HTTP request (Listing 13).

Listing 13. AppRequestHandler interface
public interface AppRequestHandler {
  void handleRequest(HttpServletRequest arg0, HttpServletResponse arg1) 
       throws ServletException, IOException;
}

Create the class ForwardServlet

You need to replace HttpRequestHandlerServlet because this utility is Spring specific and will not work with Blueprint. HttpRequestHandlerServlet represents the only servlet used by Blether from the perspective of the Web descriptor. It selects the target HttpRequestHandler based on the servlet name and the bean name (which are expected to match exactly). For the moment, you are going to build something slightly simpler, with explicit name matching configuration. Create the ForwardServlet class shown in Listing 14 in /web/src/com/ibm/ws/eba/example/springmigration/web/.

Listing 14. ForwardServlet implementation
public class ForwardServlet extends HttpServlet {

  @Override
  protected void service(HttpServletRequest req, 
                         HttpServletResponse resp)
      throws ServletException, IOException
  {
    try {
      InitialContext ic = new InitialContext();
      AppRequestHandler handler = (AppRequestHandler) 
        ic.lookup("osgi:service/" + 
          AppRequestHandler.class.getName() + "/" +
          "(servlet.name=" + getServletName() + ")");

      if (handler != null) {
        handler.handleRequest(req, resp);
      } else { 
        System.out.println("ERROR: No handler for servlet"+getServletName());
      }
    } catch (NamingException ne) {
      ne.printStackTrace();
    }
  }
}

There is nothing special or new in the code above. The only concern of the ForwardServlet is to look up the correct AppRequestHandler and forward the incoming request to it. As an integration mechanism, you will use the OSGi service registry again, which is accessed, as before, via a JNDI lookup. Alternatively, the BundleContext of the Web bundle is available from the servlet context via the osgi-bundlecontext attribute. In order to distinguish between different request handlers, you require an additional service property servlet.name, which is expected to match the servlet name for the incoming request.

Converting the Spring descriptor to Blueprint

Finally, you are in a position to convert the remaining Spring descriptor wholesale to Blueprint. In addition to converting syntax differences like tag names, you need to also register the request handlers in the service registry with the appropriate servlet.name service property. Finally, Spring extensions like .jee, .tx and .aop need to be replaced entirely by suitable Blueprint extensions.

Creating the Blueprint XML

Currently, the Spring descriptor looks like Listing 15 (details omitted).

Listing 15. Spring descriptor for the Web module
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:tx="http://www.springframework.org/schema/tx"
  xmlns:aop="http://www.springframework.org/schema/aop"
  xmlns:jee="http://www.springframework.org/schema/jee">

  <tx:advice id="txAdvice" transaction-manager="transactionManager">
    <tx:attributes>
      <tx:method name="get*" read-only="true"/>
      <tx:method name="*"/>
    </tx:attributes>
  </tx:advice>

  <aop:config>
    <aop:pointcut id="servletRequest" expression=
      "execution(* com.ibm.ws.eba.example.springmigration.web.*.handleRequest(..))"/>
    <aop:advisor advice-ref="txAdvice" pointcut-ref="servletRequest"/>
  </aop:config>

  <bean id="RegistrationServlet"
    class="com.ibm.ws.eba.example.springmigration.web.Registration">
    <property name="bletherHandle">
      <ref bean="bletherService"/>
    </property>
  </bean>

  ... A lot more request handlers ...

  <jee:jndi-lookup lookup-on-startup="false" id="bletherService" 
    proxy-interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" 
    jndi-name="osgi:service/com.ibm.ws.eba.example.springmigration.BletherUserInterface"
  />

  <tx:jta-transaction-manager/>

</beans>

The next step is to create a Blueprint descriptor; that is, create sample/web/resources/OSGI-INF/blueprint.xml. You can start building up the content of this file by looking at the sample/web/resources/WEB-INF/springapp-service.xml.

  • The .jee extension is only used to look up the Blether service via JNDI. However, the Blether service actually exists in the OSGi service registry and Blueprint defines a more direct way to use a service from OSGi service registry with the reference element. Therefore, the .jee functionalities are not needed and you don't need to create any equivalent in the Blueprint descriptor.
  • The .aop and .tx extensions are used in springapp-service.xml to declare transactions around all request handlers. The mechanism to achieve such declarative transactions is generic and orthogonal based on aspect-orientation. This means all individual pieces can be used independently for various purposes. Blueprint also supports declarative transactions.

    In concrete terms, you need to use the http://aries.apache.org/xmlns/transactions/v1.0.0 namespace, which defines a single element, tx, to declare transactional behaviour for an enclosing bean element.

    Be aware that the extension described here, the .jpa extension used above, and the underlying extension mechanism are specific to the Blueprint implementation of the Apache Aries project, which is shipped as part of the OSGi feature pack. There is currently no defined extension mechanism in the core Blueprint specification.

In addition to these changes, you need to create a service export definition for every request handler. For this, you use the service element already introduced in the persistence bundle's Blueprint descriptor. Finally, there are some minor syntactical differences that need to be changed; for example, the ref element inside a property element has an id attribute instead of the bean attribute used in Spring.

The Blueprint descriptor file will look like Listing 16.

Listing 16. Blueprint descriptor for the Web module
<blueprint xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
           xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0">

  <bean id="RegistrationServlet" 
    class="com.ibm.ws.eba.example.springmigration.web.Registration">
    <tx:transaction method="*" value="Required"/>
    <property name="bletherHandle" ref="bletherService" />
  </bean>

  <service ref="RegistrationServlet" 
    interface="com.ibm.ws.eba.example.springmigration.web.AppRequestHandler">
    <service-properties>
      <entry key="name" value="RegistrationServlet" />
    </service-properties>
  </service>

  ... Repeat the bean definition and service element for each resource...

  <reference id="bletherService" 
    interface="com.ibm.ws.eba.example.springmigration.BletherUserInterface" />

</blueprint>

This is slightly more verbose than the Spring descriptor it replaces. This is mainly due to the additional service elements, and also the fact that transactions are now declared per bean rather than globally - although if you look at the SampleStage3.zip contents you will see that some of the tx elements are not actually required and were removed. As an extension, it is also possible to remove the overhead of the service elements via Blueprint APIs (in particular org.osgi.service.blueprint.container.BlueprintContainer) directly and look up the beans by name (similar to what the HttpRequestHandlerServlet does). The Blueprint container can be retrieved from (not surprisingly) the OSGi service registry. We will return to this at the end of this article.

Cleaning up the web.xml file

The sections that reference Spring can be removed from the web/resources/web.xml file. This just means deleting the <context-param> and <listener> sections.

Cleaning up the ivy.xml file

At this stage, all dependencies on the Spring framework are severed and the corresponding lines can be removed from the Web project's ivy.xml file. Only three dependencies remain: javax.servlet, javax.persistence and com.google.code.gson. The springapp-service.xml file can be removed.

The stage of the application is captured in the SampleStage3.zip snapshot. Should you wish, you will be able to test it by using the uninstall/install scripts as described in preceding sections.


Migration Stage 4: Finishing touches

At this stage, the Blether application has been completely converted from the Spring model to OSGi, and in particular the Blueprint model. But there is a bit of cleanup left to do, which was omitted in the previous steps. Most importantly, the application relies on auto-completion of the application manifest and auto-conversion of the Web module. While these features are very handy for rapid conversion of an application to run using the OSGi feature pack, typically you would want tighter control for an application in production. For example, auto-generated application manifests do not permit fine-grained updates. Also, auto-converted WAR modules treat all dependencies as optional, so that the OSGi resolver will not ensure that all dependencies of the Web module are actually present.

Converting the Web module to a Web application bundle

First, you will convert the Web module into a proper bundle, which is a prerequisite to authoring a complete application manifest. As so often in OSGi, all this involves is carefully crafting the appropriate manifest. Besides determining the correct Import-Package statement, a Web module in particular requires the bundle classpath be set appropriately, as well as the header(s) denoting the bundle as a Web bundle, according to OSGi Web applications specification (OSGi Enterprise spec rev. 4.2, 128). Edit the content shown in Listing 17 into the sample/web/resources/META_INF/MANIFEST.MF file.

Listing 17. Blether Web application bundle manifest
Manifest-Version: 1.0
Bundle-ManifestVersion: 2 
Web-ContextPath: /blether
Bundle-SymbolicName: com.ibm.ws.eba.example.blether.web
Bundle-Version: 1.0.0
Bundle-Classpath: WEB-INF/classes/,WEB-INF/lib/JSON4J.jar
Import-Package: javax.servlet;version=2.5,
 javax.servlet.http;version=2.5,
 javax.el;version=2.1,
 javax.servlet.jsp;version=2.1,
 javax.servlet.jsp.el;version=2.1,
 javax.servlet.jsp.tagext;version=2.1,
 com.ibm.ws.eba.example.springmigration;version="[1.0.0,2.0.0)"

The import package header in the manifest is fairly self-explanatory. You need the servlet API packages and, of course, the persistence entities and the BletherUserInterface class from the persistence bundle. The bundle classpath header is more interesting. This header is required because in a Web application (and therefore also a Web application bundle) classes do not reside at the root of the bundle, but instead in WEB-INF/classes. Also, embedded libraries exist in WEB-INF/lib/*.jar. OSGi provides a standard mechanism through the Bundle-Classpath header to define such non-standard class paths, which needs to be used here because Web application bundles no longer benefit from the conventions used in a Java EE Web container. Since you have removed all the Spring dependencies in previous steps, the only library JAR that needs to be available is JSON. In a further step, it would be easy to extract that dependency into its own bundle (the metadata is already present) and so further OSGi-ify the Web bundle. This is left for you as a later exercise.

Finally, there is the Web-ContextPath header. This serves two functions. First, it specifies the default context path at which the Web application will be installed. On WebSphere Application Server, however, this default can be overridden when installing the application. Second and more importantly, it denotes the bundle as a Web bundle. So, similar to persistence bundles without the Meta-Persistence header, Web bundles without the Web-ContextPath header will not be treated as Web bundles.

Creating the application manifest

The application manifest of an OSGi application, located at META-INF/APPLICATION.MF inside the .eba archive, describes the identity and logical contents of an OSGi application. Listing 18 shows a sample manifest for the Blether application.

Listing 18. Blether application manifest
Manifest-Version: 1.0
Application-Name: Blether spring migration sample
Application-SymbolicName: blether.eba
Application-Version: 1.0.0
Application-Content: com.ibm.ws.eba.example.blether.persistence;version="[1.0.0,2.
 0.0)",com.ibm.ws.eba.example.blether.web;version="[1.0.0,2.0.0)"

The main interesting bit is the application content header. This header defines the logical contents of the application; in this case, the Web bundles and the persistence bundle. After declaring the contents in this way, the bundles no longer need to be contained in the .eba archive itself. Instead, the bundles could be pulled in from an external repository of bundles, or the WebSphere Application Server internal bundle repository. Be aware that with a version range such as com.ibm.ws.eba.example.blether.persistence;[1.0.0,2.0.0), the highest available version of the persistence bundle available between the .eba archive and any defined bundle repositories will be used, and not necessarily the one contained in the .eba. A final purpose of the version ranges is to define the boundaries in which bundles in the application can be updated. For example, in the above manifest the application can be made up of a Web bundle and a persistence bundle at any versions between 1.0.0 (inclusive) and 2.0.0 (exclusive). (See Resources.)

To create an application manifest for the Blether sample, paste the sample code in Listing 18 into sample/resource/META-INF/APPLICATION.MF.

Finishing touches to ForwardServlet

As a final improvement, look again at the ForwardServlet developed in Stage 3. It has rather a lot of boilerplate Blueprint XML to define the request handler beans and then export them to the service registry with the correct service properties. It would be cleaner if the ForwardServlet used a convention-over-configuration approach similar to Spring's HttpRequestHandlerServlet, where the ID of the bean is expected to match the servlet name as defined in web.xml.

It turns out with a tiny rewrite this can be achieved. Listing 19 shows a basic implementation. Instead of looking up a specific AppRequestHandler from the service registry, you obtain the BlueprintContainer for the Web bundle from there. This is then used to obtain the bean instance with ID equal to the servlet name. (This implementation depends on Blueprint interfaces now rather than using just the generic service registry.)

Listing 19. ForwardServlet using convention over configuration
import org.osgi.service.blueprint.container.BlueprintContainer;

public class ForwardServlet extends HttpServlet {

  @Override
  protected void service(HttpServletRequest arg0, 
                         HttpServletResponse arg1)
      throws ServletException, IOException
  {
    try {
      InitialContext ic = new InitialContext();
      BlueprintContainer container = (BlueprintContainer) ic.lookup("osgi:service/" 
        + "org.osgi.service.blueprint.container.BlueprintContainer/" 
        + "(osgi.blueprint.container.symbolicname=com.ibm.ws.eba.example.blether.web)");
      
      AppRequestHandler handler = (AppRequestHandler) 
        container.getComponentInstance(getServletName());
      
      if (handler != null) handler.handleRequest(arg0, arg1);
      else System.out.println("ERROR: No handler for servlet"+getServletName());
      
    } catch (NamingException ne) {
      ne.printStackTrace();
    }
  }
}

In addition to the change to the Java code, you also need to declare the new dependency on Blueprint in the bundle manifest by adding the following line to the package import header:

org.osgi.service.blueprint.container;version="[1.0.0,2.0.0)"

When you add this to the MANIFEST.MF file, don't forget to add a comma at the end of the previous line.

Changes to the Ivy configuration

Before rebuilding the application for the final time, there is another change needed to make the Blueprint API available at compilation stage. To do that, add a dependency declaration:

<dependency org="org.osgi" name="org.osgi.compendium" rev="4.2.0" conf="*->*,!sources"/>

to the ivy.xml in the Web project, and also ensure that the new JAR is not packaged as part of the WAR file. The latter part is actually not crucial because in OSGi package imports are searched for classes instead of the contents of a bundle in case of conflicts.


Conclusion

This concludes the transformation of the Blether Spring application into the Blether OSGi application. Use this example as a template for converting your own Spring applications and leveraging the benefits of OSGi. The final and complete sample code is available in SampleSnapshotFinal.zip download file.


Download

DescriptionNameSize
Sample applicationSpringMigrationStages.zip240KB

Resources

Learn

Get products and technologies

Discuss

Comments

developerWorks: Sign in

Required fields are indicated with an asterisk (*).


Need an IBM ID?
Forgot your IBM ID?


Forgot your password?
Change your password

By clicking Submit, you agree to the developerWorks terms of use.

 


The first time you sign into developerWorks, a profile is created for you. Select information in your profile (name, country/region, and company) is displayed to the public and will accompany any content you post. You may update your IBM account at any time.

All information submitted is secure.

Choose your display name



The first time you sign in to developerWorks, a profile is created for you, so you need to choose a display name. Your display name accompanies the content you post on developerWorks.

Please choose a display name between 3-31 characters. Your display name must be unique in the developerWorks community and should not be your email address for privacy reasons.

Required fields are indicated with an asterisk (*).

(Must be between 3 – 31 characters.)

By clicking Submit, you agree to the developerWorks terms of use.

 


All information submitted is secure.

Dig deeper into WebSphere on developerWorks


static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=WebSphere
ArticleID=643627
ArticleTitle=Migrating from the Spring Framework to OSGi using the WebSphere Application Server V7 Feature Pack for OSGi Applications
publish-date=03302011