Converting a Spring application to an OSGi application
To convert an application created using the Spring Framework to an OSGi application and move from the Spring Framework to standards-based technologies, modify the application manually. If a Spring application contains only WAR files, we can convert it automatically to run in OSGi Applications, but it still uses the Spring Framework.
In a Spring application, the Spring Framework manages features such as transactions, persistence, and dependency injection, and handles the communication between the servlets in the web container and the classes that handle the business logic of the application.
After you convert the application to an OSGi application that uses OSGi Applications support, the Blueprint Container manages the transactions, persistence and dependency injection.
If the application is an EAR file containing only web application archive (WAR) files, we can convert it automatically. We might convert a Spring application in this way if a WAR file uses other library JAR files that must remain unchanged for the application to work. See Converting a web application archive file to an OSGi web application bundle.
Otherwise, we need to identify the Spring components and replace them with the equivalent code to make them plain old Java™ objects (POJOs), then modify the relevant XML files so that the Blueprint container manages those objects.
To convert a Spring application, you change it as follows:
- Create a well-defined interface to delegate to. The interface represents the classes that handle the business logic of the application.
- Create a service for each class, so that the servlet in the web container can use them.
- Change code that is specific to the Spring Framework in the classes to use the equivalent in Java EE, for example Java EE persistence classes.
- Create a handler servlet and interface to replace those from the Spring Framework.
- Modify XML and manifest files to use syntax that is correct for the Blueprint Container.
At the end of each step, the application is still usable, so we can decide whether to make all, or just some, of these changes.
The following procedure describes these steps in more detail.
- Create a well defined interface to delegate to, for example to replace a dispatcher servlet in the Spring application. The following example replaces the Spring HttpRequestHandler interface in an example application with the MyApplicationHandler interface.
To replace the Spring interface with this new interface, you must also replace the servlet, as described in step 4.
- Create an interface to replace the Spring HttpRequestHandler interface.
package com.ibm.ws.eba.example.springconversion; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; interface MyApplicationHandler { void setMyApplicationHandle(MyApplicationUserInterface b); void handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception; }
- In each class that imports and implements the Spring HttpRequestHandler interface, implement the new interface. For example, change the following code:
implements HttpRequestHandlerto :implements MyApplicationHandler
- If the method creates an exception that is not in a base Java package, for example a ServletException exception, we can change it to use more general exception handling and avoid additional OSGi package imports. For example, change the following code:
handleRequest(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOExceptionto:handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception
- At this point, you could continue to use the Spring classes and use the Blueprint Container to manage those objects. To do this, change the blueprint.xml file, as described in step 7.
The configuration for dependency injection that the Blueprint Container uses is similar to the configuration for the Spring Framework. For example, if the Spring Framework calls the setMyApplicationHandle method to inject the MyApplicationUserInterface variable, this injection continues to work in the Blueprint Container, as long as the blueprint.xml file is configured correctly.
- Change the managed bean that handles persistence to use standard Java EE persistence classes. In the following example, the Spring JpaTemplate interface handles persistence. It is equally valid if using JPA directly through an entity manager.
- Replace the JpaTemplate interface with Java EE persistence classes. For example, remove the following import statement:
import org.springframework.orm.jpa.JpaTemplate;Replace it with the following import statements:import javax.persistence.EntityManager; import javax.persistence.PersistenceContext; import javax.persistence.Query;
- Use the relevant calls that are available in the API for those interfaces. For example, to replace the JpaTemplate code with EntityManager code, remove the following code:
@PersistenceUnit(unitName = "springExample") private JpaTemplate jpaTemplate; public void setJpaTemplate(JpaTemplate j) { jpaTemplate = j; }Replace it with the following code:@PersistenceContext(unitName = "myApplication") private EntityManager em; public void setEntityManager(EntityManager e) { em = e; }
- If a persistence unit is inside a web application, separate the persistence unit from the WAR and create a persistence bundle. The persistence bundle must contain the entity classes and the persistence.xml file. The bundle manifest must have a Meta-Persistence header that points to the persistence unit, as shown in the following example:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: MyPersistenceBundle Bundle-SymbolicName: com.ibm.ws.eba.example.persistence Bundle-Version: 1.0.0 Meta-Persistence: WEB-INF/classes/META-INF/persistence.xml Import-Package: javax.persistence
- Replace each instance of a call from the bean that handles persistence with an equivalent call that uses the EntityManager interface, the Query interface, or both. For example, in the example Spring application, the following code snippet shows two methods that call the jpaTemplate.find method:
@SuppressWarnings("unchecked") public boolean checkEMailAddressUniqueness(String emailAddress) { boolean result = false; List<UserInfo> users = jpaTemplate.find(uniqueEmailAddressQuery, emailAddress); if (users.isEmpty()) { result = true; } return result; } public boolean checkUsernameUniqueness(String username) { boolean result = false; UserInfo user = jpaTemplate.find(UserInfo.class, username); if (user == null) { result = true; } return result; }The following code snippet shows replacement code:private static final String uniqueEmailAddressQuery = "select u from UserInfo u where u.emailAddress = ?1"; @SuppressWarnings("unchecked") public boolean checkEMailAddressUniqueness(String emailAddress) { boolean result = false; Query q = em.createQuery(uniqueEmailAddressQuery); q.setParameter(1, emailAddress); List<UserInfo> users = q.getResultList(); if (users.isEmpty()) { result = true; } return result; } public boolean checkUsernameUniqueness(String username) { boolean result = false; UserInfo user = em.find(UserInfo.class, username); if (user == null) { result = true; } return result; }
- Create and register a handler servlet to replace the one from the Spring Framework.
- Create a servlet to forward requests to the appropriate handler in the Blueprint Container to process the business logic. This servlet is managed by the web container. The JNDI lookup is constructed as follows:
osgi:service/class_name/ldap_filterThe following code snippet creates the MyApplicationHandlerServlet servlet that passes an HTTP request to the MyApplicationHandler class in the Blueprint Container:
package com.ibm.ws.eba.example.springconversion; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyApplicationHandlerServlet extends HttpServlet { private static final long serialVersionUID = -3705932907251688199L; @Override public void service(HttpServletRequest request, HttpServletResponse response) { // Get servlet name String servletName = this.getServletConfig().getServletName(); // Get business logic class from service registry that is // associated with the servlet try { InitialContext ic = new InitialContext(); MyApplicationHandler myApplicationLogicClass = (MyApplicationHandler) ic.lookup("osgi:service/com.ibm.ws.eba.example.springconversion. MyApplicationHandler/(servlet.name="+servletName+")"); myApplicationLogicClass.handleRequest(request, response); } catch (NamingException e) { e.printStackTrace(); } catch (Exception e) { // TODO Auto-generated catch block e.printStackTrace(); } } }
- Register the new servlet by changing the servlet classes to define the servlet you just created, rather than the Spring servlet. To do this, modify web.xml in the WEB-INF folder of the WAR file.
For example, change the following servlet definition in web.xml:
<servlet> <servlet-name>RegistrationServlet</servlet-name> <servlet-class> org.springframework.web.context.support.HttpRequestHandlerServlet </servlet-class> </servlet>to:<servlet> <servlet-name>RegistrationServlet</servlet-name> <servlet-class>com.ibm.ws.eba.example.springconversion.MyApplicationHandlerServlet </servlet-class> </servlet>
- Delete the following elements from web.xml:
<context-param> <listener>
- Separate the persistence unit from the EAR file and create a persistence bundle. The persistence bundle must contain the entity classes and the persistence.xml file. The bundle manifest must have a Meta-Persistence header that points to the persistence unit, as shown in the following example:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: MyPersistenceBundle Bundle-SymbolicName: com.ibm.ws.eba.example.persistence Bundle-Version: 1.0.0 Meta-Persistence: WEB-INF/classes/META-INF/persistence.xml Import-Package: javax.persistence
- If we removed the persistence unit from a WAR file, delete the following element from web.xml in the WEB-INF folder of the WAR file:
<persistence-unit-ref>
- Change the XML so that the Blueprint Container manages the objects, rather than the Spring Framework.
- Delete the springapp-service.xml file in the WEB-INF folder of the WAR file.
- Create the OSGI-INF/blueprint/ directory.
- Create a file named blueprint.xml in the OSGI-INF/blueprint/ directory. The following code shows an example blueprint.xml file:
<blueprint xmlns:tx="http://aries.apache.org/xmlns/transactions/v1.0.0" xmlns:jpa="http://http://aries.apache.org/xmlns/jpa/v1.0.0"> <bean id="myApplicationImpl" class="com.ibm.ws.eba.example.springconversion.impl.MyApplicationImpl"> <jpa:context property="entityManager" unitname="myApplication" /> <tx:transaction method="*" value="Required"/> </bean> <bean id="RegistrationServlet" class="com.ibm.ws.eba.example.springconversion.Registration"> <tx:transaction method="*" value="Required"/> <property name="myApplicationHandle" ref="myApplicationService" /> </bean> <service id="myApplicationService" ref="myApplicationImpl" interface= "com.ibm.ws.eba.example.springconversion.MyApplicationUserInterface" /> <service id="registrationService" ref="RegistrationServlet" interface= "com.ibm.ws.eba.example.springconversion.MyApplicationHandler" > <service-properties> <entry key="servlet.name" value="RegistrationServlet"/> </service-properties> </service> </blueprint>
- Update the persistence.xml file in the war/WEB-INF/classes/META-INF/ directory. For example, we can change the JNDI lookups to use a service from a service registry. The following code is an example:
<persistence xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" version="1.0"> <persistence-unit name="myApplication" transaction-type="JTA"> <description>Persistence unit for the Spring conversion sample application </description> <provider>com.ibm.websphere.persistence.PersistenceProviderImpl </provider> <jta-data-source>osgi:service/javax.sql.DataSource/ (osgi.jndi.service.name=jdbc/userdb) </jta-data-source> <non-jta-data-source>osgi:service/javax.sql.DataSource/ (osgi.jndi.service.name=jdbc/userdbnonjta) </non-jta-data-source> <class>com.ibm.ws.eba.example.springconversion.Post</class> <class>com.ibm.ws.eba.example.springconversion.UserInfo</class> <exclude-unlisted-classes/> </persistence-unit> </persistence>
- Create the enterprise bundle archive (EBA) structure for the application.
- Create a META-INF directory in the root of the project and create an application manifest, APPLICATION.MF. The following code shows an example application manifest file:
Manifest-Version: 1.0 Application-ManifestVersion: 1.0 Application-Name: MyApplication Application-SymbolicName: com.ibm.ws.eba.example.springconversion Application-Version: 1.0 Application-Content: com.ibm.ws.eba.example.springconversion
- Delete the context.xml file from the war/META-INF/ directory.
- In the war/META-INF/ directory, create a bundle manifest file, MANIFEST.MF. The following code shows an example bundle manifest file:
Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: MyApplication Bundle-SymbolicName: com.ibm.ws.eba.example.springconversion Bundle-Version: 1.0.0 Bundle-Vendor: IBM Bundle-ClassPath: WEB-INF/classes, WEB-INF/lib/aspectjrt.jar,WEB-INF/lib/aspectweaver.jar, WEB-INF/lib/commons-collections-3.2.jar,WEB-INF/lib/commons-lang-2.1.jar, WEB-INF/lib/JSON4J.jar,WEB-INF/lib/jstl.jar,WEB-INF/lib/jta.jar, WEB-INF/lib/openjpa-1.3.0-SNAPSHOT.jar,WEB-INF/lib/persistence.jar, WEB-INF/lib/serp-1.13.1.jar,WEB-INF/lib/standard.jar Web-ContextPath: myApplication Import-Package: javax.servlet;version="2.5", javax.servlet.http;version="2.5", javax.servlet.jsp;version="2.1", javax.servlet.jsp.tagext;version="2.1", org.osgi.framework;version="1.5.0", javax.persistence
Parent topic: Converting existing applications to OSGi applications
Related concepts
Web application bundles
Related tasks
Use the transaction service
Converting an enterprise application to an OSGi application