GlassFish V3 Extensions, part 3 : Spring, Java EE 6 and OSGi

Few months ago, I met with Yan Pujante from LinkedIn to show him some progress of GlassFish V3, I saw him again at the EclipseConference few weeks back where he made an interesting presentation on the difficulty of moving monolithic web applications (several megabytes) into a service based architecture. You can find his slides there. I have decided the devote this entry on how you can use GlassFish V3 to help solving that issue with OSGi and Spring. 

First, remember from my last entry, I created an OSGi bundle which contains a simple interface, this represents the service interface. Today, I will implement this interface with two Spring DM, and access such services from a Servlet without having any OSGi/Spring dependency introduced...

I am pretty bad at explaining things, hopefully this diagram will explain it all... 


As you can see, I have 3 bundles in play, a pure OSGi bundle called API which contains my service definition (just a plain java interface), a Spring OSGi application called IMPL which contains 2 Spring beans and finally a Java EE 6 web application called WEBCLIENT that looks up services implementation and invoke their methods.

Project Files

I have included all the project files to reproduce this, there are three directories and a top level pom.xml to build the three projects

  1. api : contains the simple service definition I blogged in my last entry
  2. impl : contains the Spring Beans implementations
  3. webclient : contains the Java EE 6 web application to access my services.
Here is a list of the files and the sources are here.

Spring integration

First, we need to extend GlassFish v3 with the Spring container, go to SpringSource and download the GA release 1.1.3, you will find a number of jar files in the lib directory, you do not need all of them, in particular you already have a web container with GlassFish so all you really need is the Spring core platform, find below the list of bundles I came up with that allowed me to run my beans (you might need more as you add features).

I have tried the 1.2 release but it seems it requires to run on Equinox rather than Felix due to some bundle fragment support missing. This will be a good opportunity for a follow up blog to show how to run GlassFish v3 with Equinox and Spring 1.2. For now, pick 1.1.3, that will work fine.

To install these bundles in glassfish you can just copy them inside the modules directory or you can create a subdirectory under modules (call it spring).  This is my modules/spring directory content :

com.springsource.org.aopalliance-1.0.0.jar
com.springsource.slf4j.api-1.5.0.jar
com.springsource.slf4j.log4j-1.5.0.jar
com.springsource.slf4j.org.apache.commons.logging-1.5.0.jar
log4j.osgi-1.2.15-SNAPSHOT.jar
spring-aop-2.5.5.jar
spring-beans-2.5.5.jar
spring-context-2.5.5.jar
spring-context-support-2.5.5.jar
spring-core-2.5.5.jar
spring-osgi-core-1.1.3.jar
spring-osgi-io-1.1.3.jar
Once you have done that, you can start your GlassFish instance :
asadmin start-domain

Now let's deploy the extender bundle from Spring. I am not copying that bundle inside modules/spring directory like the other ones because I need this bundle to be automatically started (remember from my last entry that bundles in modules are not started until explicitly tagged in the felix config file). Deploying the bundle will take care of starting it for me so just do 

asadmin deploy --type osgi dist/spring-osgi-extender-1.1.3.jar

The extender bundle will basically listen to any OSGi bundle installation and check wether or not that bundle contains Spring artifacts. If it does, it deploys those in the Spring container so all we have to do is to install Spring application in our OSGi runtime for the Spring container to pick up the tab.

Build the Spring beans implementation

Now is time to build and deploy the implementation of my service as defined in my last entry. Remember to deploy the API bundle first otherwise deploying the implementation will fail since the API bundle will be missing (once again, described here).

Reminder : asadmin deploy --type osgi api/target/simple-service-api.jar

For the sake of making that blog entry as long and painful as possible, I have added 2 implementations of the interface deployed with my API bundle.

package examples.services.impl;
import examples.services.api.SimpleService;
/\*\*
 \* Implementation of the simple service, returns a hard-coded
 \* String value
 \* 
 \* @author Jerome Dochez
 \*/
public class MyServiceImpl implements SimpleService {
    public String getString() {
        return "simple service at your service";
    }
}

and

package examples.services.impl;
import examples.services.api.SimpleService;
/\*\*
 \* Implementation of the simple service, returns a hard-coded
 \* String value
 \* 
 \* @author Jerome Dochez
 \*/
public class SecondServiceImpl implements SimpleService {
    public String getString() {
        return "second service at your service";
    }
}

 This is how I defined my Spring Beans :

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
  <bean name="simpleService" class="examples.services.impl.MyServiceImpl" />
  <bean name="secondService" class="examples.services.impl.SecondServiceImpl" />
</beans>

Now I need to make that Spring application OSGi aware, for that I need to add 2 more files, the manifest file for the plain OSGi metadata, and a new Spring deployment descriptor to specify which beans gets registered in the OSGi registry.

My OSGi manifest looks like this :

Bundle-Version: 1.0
Bundle-SymbolicName: examples.services.impl.simple-service-bundle
Bundle-Name: Implementation of some services using Spring Beans framework
Import-Package: examples.services.api
Export-Package: examples.services.impl
Bundle-ManifestVersion: 2

As you can see, I am importing the API bundle, and exporting my implementation classes.

My simple-service-osgi.xml is a bit complicated but looks like this...

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:osgi="http://www.springframework.org/schema/osgi"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
                      http://www.springframework.org/schema/osgi http://www.springframework.org/schema/osgi/spring-osgi.xsd">

  <!-- Export the simpleService bean (defined in a separate
       config file in this case) as an OSGi service -->

  <osgi:service id="simpleServiceOsgi" ref="simpleService"
    interface="examples.services.api.SimpleService" />
 
  <osgi:service id="secondServiceOsgi" ref="secondService"
    interface="examples.services.api.SimpleService" />
</beans>

The two Spring beans implementing the SimpleService contract will be registered in the OSGi registry under the simpleService and secondService names, this will be important when it is time to look them up.

Finally let's build this using maven, this is the pom file

<?xml version="1.0"?><project>
  <modelVersion>4.0.0</modelVersion>
  <groupId>examples.services.impl</groupId>
  <artifactId>simple-service-bundle</artifactId>
  <version>1.0</version>
  <packaging>jar</packaging>
  <name>Simple Service Provider implementation</name>
 
  <dependencies>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-test</artifactId>
      <version>2.5.5</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-core</artifactId>
      <version>2.5.5</version>      
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-context</artifactId>
      <version>2.5.5</version>      
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-beans</artifactId>
      <version>2.5.5</version>      
      <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>examples.services</groupId>
        <artifactId>simple-service-api</artifactId>
        <version>1.0-SNAPSHOT</version>
    </dependency>
  </dependencies>
 
  <build>
      <finalName>${project.artifactId}</finalName>
     <plugins>
    <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
          <archive>
            <manifestFile>src/main/resources/META-INF/MANIFEST.MF</manifestFile>
          </archive>
        </configuration>
      </plugin>
    </plugins>
    </build>

Deploying the Spring application

Ok that was rough, but if you are still with me, it should get pretty easy from now on, from the impl directory, do

mvn install
asadmin deploy --type osgi target/simple-service-bundle.jar 

Deploying the implementation using the --type osgi parameter installs the bundle inside the OSGi runtime. The Spring extender bundle is automatically notified of such installations and will deploy my beans.

The Client 

Now that we have deployed our services beans in the Spring beans container, I need to have a client to access such beans. One good opportunity to try the new Java EE 6 servlet features ! I love annotations, and getting rid of xml deployment descriptors is a blessing to me (I love everything to be close to the code so I don't have to cross reference too much). 

A Java EE 6 servlet is not that different than previous platform versions, it is now annotated with @WebServlet giving a URL pattern (here it is /hello). We now have a GlassFish specific feature :  Services registered in the OSGi registry can be looked up using the standard Java EE @Resource annotation. So one important thing to notice here is that the servlet is not using any OSGi APIs nor any Spring APIs nor does it know where the implementation of such services resides.

import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.annotation.WebServlet; 
import javax.servlet.\*;
import javax.servlet.http.\*;
import javax.annotation.Resource;
import examples.services.api.SimpleService;

@WebServlet(urlPatterns={"/hello"})       
public class HelloWorld extends HttpServlet {

    @Resource(mappedName="secondService")
    SimpleService secondService;

    @Resource(mappedName="simpleService")
    SimpleService simpleService;

    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
 
        PrintWriter pw = res.getWriter();
        try {
            pw.println("Service class is " + SimpleService.class + "<br>");
            pw.println("First service is " + simpleService);
            pw.println("<br>");
            pw.println("Second service is " + secondService);
            pw.println("<br>");

            if (simpleService!=null) {
                pw.println("SimpleService says " + simpleService.getString());
                pw.println("<br>");
            }
            if (secondService!=null) {
                pw.println("SecondService says " + secondService.getString());
                pw.println("<br>");
            }
        } catch(Exception e) {
            e.printStackTrace();
        }
    }
}

maven war plugin is not uptodate with Java EE 6 and wants a web.xml no matter what, so I included an empty one in the project files.

Finally the pom.xml which has the war packaging type and the servlet 3.0 APIs as well as my service definition APIs dependencies.
...
    <groupId>examples.services</groupId>
    <artifactId>webclient</artifactId>
    <packaging>war</packaging>
    <version>1.0-SNAPSHOT</version>
    <name>Web client accessing Simple Provider Services</name>
...
    <dependencies>
        <dependency>
            <groupId>examples.services</groupId>
            <artifactId>simple-service-api</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
        <dependency>
            <groupId>org.glassfish</groupId>
            <artifactId>javax.servlet</artifactId>
            <version>3.0-SNAPSHOT</version>
            <scope>provided</scope>
        </dependency>
    </dependencies>
...

Ok so again let's build and deploy this war fil (note there is no --type osgi parameter to the deploy command as we are deploying a plain Java EE application)

mvn install
asadmin deploy target/webclient.war

Everything together

We have deployed our three bundles (hopefully successfully if I have not missed some crucial information along to way) and it's time to see if our web application can be injected successfully with my services implementation, point your browser to

http://localhost:8080/webclient/hello

and you should see...


Victoire !

Thanks for following me up to here, remember the few points I have demonstrated here  :

  • Spring integration into an umodified GlassFish
  • Deployment of Spring Applications using the GlassFish deployment backend
  • Service based lookup of OSGi services using standard Java EE annotations
  • Deployment of OSGi bundles for extending GlassFish runtime.
Salut


        
    
Comments:

Hi Jerome, it's great to see GlassFish embracing the services model in OSGi now. Of course your example might have been simpler if you used Declarative Services instead of Spring-DM!

I'm very interested in your use of JEE annotations to inject service references. This sounds like a good approach but how do you deal with cardinality and dynamics? Do you use proxies as in Spring-DM, or bytecode manipulation as in iPOJO?

Thanks,
Neil

Posted by Neil Bartlett on April 21, 2009 at 08:18 PM PDT #

Hello Neil

Great idea about the declarative services, I am planning on a follow up blog where I was implementing my services using an EJB but I will also throw in a pure declarative services example.
As for the injection, we use neither proxies or code manipulation since we own the lifecycles of the injected components, we just lookup the service in the OSGi registry (Hk2 in reality which front OSGi) and we inject the looked up resource in the newly instantiated component. Of course it has some limitations so far, we don't do per thread/per request type of proxying to the actual service, this is all singleton services access.
It's really a baby step, more to come over the next few months...

Posted by Jerome Dochez on April 22, 2009 at 07:00 AM PDT #

Hi Jerome,
I am new to this osgi and glassfish, I started the glassfish server and tried to follow your blog and deploy OSGI bundle in glassfish by executing the below cmd but i got this error. I downloaded the glassfishv3-prelude.zip and extracted it to E:/ dir. I have set the PATH to E:\\glassfishv3-prelude\\glassfish\\bin dir. Could you please help me in deploying this .jar file ?

E:\\glassfishv3-prelude\\glassfish\\bin>asadmin deploy --type osgi E:\\OSGI\\spring-osgi-1.1.3-with-dependencies\\spring-osgi-1.1.3
\\dist\\spring-osgi-extender-1.1.3.jar

remote failure: Invalid option: type
Usage: deploy [--virtualservers virtual_servers] [--contextroot context_root] [--force=false] [--precompilejsp=false] [--name
component_name] [--upload=true] [--enabled=true] [--libraries jar_file[(pathseparator)jar_file]\*] file_archive| filepath

Command deploy failed.
E:\\glassfishv3-prelude\\glassfish\\bin>

Thanks,
Puneeth

Posted by Puneeth on May 04, 2009 at 10:27 PM PDT #

Puneeth, you need to use a recent promoted build of GlassFish v3. Prelude is (already!) too old.
Get it here: http://download.java.net/glassfish/v3/promoted/

Posted by Alexis MP on May 05, 2009 at 05:01 PM PDT #

Post a Comment:
Comments are closed for this entry.
About

dochez

Search

Categories
Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today