TOTD #130: Invoking a OSGi service from a JAX-WS Endpoint - OSGi and Enterprise Java

Sahoo blogged about JAX-WS Web service in an OSGi bundle. This Tip Of The Day (TOTD) provides complete steps to create such an application from scratch.

We will create an OSGi service, a JAX-WS compliant Web service as hybrid application, and a JAX-WS Web service client.

Lets create an OSGi service first.

  1. Create a simple OSGi service as explained in TOTD #36. Generate the maven project as:
    mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes \\
    -DgroupId=org.glassfish.samples.osgi.hello.service -DartifactId=osgi-pure
    


    The updated directory structure looks like:

    osgi-pure
    osgi-pure/pom.xml
    osgi-pure/src
    osgi-pure/src/main
    osgi-pure/src/main/java
    osgi-pure/src/main/java/org
    osgi-pure/src/main/java/org/glassfish
    osgi-pure/src/main/java/org/glassfish/samples
    osgi-pure/src/main/java/org/glassfish/samples/osgi
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service/Hello.java
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service/impl
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service/impl/App.java
    osgi-pure/src/main/java/org/glassfish/samples/osgi/hello/service/impl/HelloImpl.java
    


    "Hello.java" is the OSGi service interface and looks like:

    public interface Hello {
        public String sayHello(String name);
    }
    


    "HelloImpl.java" is a trivial implementation of the service and looks like:

    public class HelloImpl implements Hello {
        public String sayHello(String name) {
            return "Hello " + name;
        }
    }
    


    "App.java" is the Bundle Activator and looks like:

    import org.glassfish.samples.osgi.hello.service.impl.HelloImpl;
    import java.util.Properties;
    import org.glassfish.samples.osgi.hello.service.Hello;
    import org.osgi.framework.BundleActivator;
    import org.osgi.framework.BundleContext;
    
    /\*\*
     \* Hello world!
     \*/
    public class App implements BundleActivator {
    
        public void start(BundleContext bc) throws Exception {
            bc.registerService(Hello.class.getName(), new HelloImpl(), new Properties());
        }
    
        public void stop(BundleContext bc) throws Exception {
            bc.ungetService(bc.getServiceReference(Hello.class.getName()));
        }
    }
    

    The "start" method registers the OSGi service using the name "Hello" and "stop" method un-registers the service.

  2. The updated "maven-bundle-plugin" from TOTD #36 looks like:

    <plugin>
      <groupId>org.apache.felix</groupId>
      <artifactId>maven-bundle-plugin</artifactId>
      <extensions>true</extensions>
      <configuration>
        <instructions>
          <Export-Package>${pom.groupId}</Export-Package>
          <Bundle-Activator>${pom.groupId}.impl.App</Bundle-Activator>
        </instructions>
      </configuration>
    </plugin>
    

    Notice, only the package that contains the service interface, i.e. "org.glassfish.samples.osgi.hello.service", is exported and the activator and service implementation are in a different package.

  3. Create the OSGi service bundle as:

    mvn install
    

    This also installs the OSGi bundle in the local maven repository. Deploy this bundle in GlassFish v3 by copying to "glassfish/domains/domain1/autodeploy/bundles" directory. Make sure GlassFish is running or start it as:

    asadmin start-domain --verbose
    

Lets create a hybrid application that consists of a JAX-WS compliant Web service, queries the OSGi service registry, invokes the OSGi service and return a response to the client.

  1. Create the Maven project as:
    mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes \\
    -DgroupId=org.glassfish.samples.osgi.jaxws.webservice -DartifactId=helloservice
    
    
  2. Change the generated "src/main/java/org/glassfish/samples/osgi/jaxws/webservice/App.java" such that it looks like:

    import javax.jws.WebService;
    import org.glassfish.samples.osgi.hello.service.Hello;
    import org.osgi.framework.BundleContext;
    import org.osgi.framework.BundleReference;
    import org.osgi.framework.ServiceReference;
    
    /\*\*
     \* Hello world!
     \*/
    @WebService
    public class App {
        public String sayHello(String name) {
            Hello service = getService(Hello.class);
            return service.sayHello(name);
        }
    
    
        /\*\*
         \* This method looks up service of given type in OSGi service registry and returns if found.
         \* Returns null if no such service is available,
         \*/
       private static <T> T getService(Class<T> type) 
           BundleContext ctx = BundleReference.class.cast(
               App.class.getClassLoader()).getBundle().getBundleContext();
           ServiceReference ref = ctx.getServiceReference(type.getName());
           return ref != null ? type.cast(ctx.getService(ref)) : null;
       }
    }
    

    The "getService" method queries the OSGI service registry and returns the service reference. The "sayHello" method looks for the "Hello" service and invokes a method on it. The name "Hello" is the same as registered during the OSGi bundle creation earlier.

  3. In the generated "pom.xml":
    1. Change the packaging to "war".
    2. Add the following repository:

      <repositories>
        <repository>
          <id>java.net</id>
          <name>GlassFish Maven Repository</name>
          <url>http://maven.glassfish.org/content/groups/glassfish</url>
        </repository>
      </repositories>
      

      so that Java EE API dependency can be resolved.

    3. Add the following dependencies in "provided" scope:

      <dependency>
        <groupId>javax</groupId>
        <artifactId>javaee-api</artifactId>
        <version>6.0</version>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>org.osgi</groupId>
        <artifactId>org.osgi.core</artifactId>
        <version>4.2.0</version>
        <scope>provided</scope>
      </dependency>
      <dependency>
        <groupId>org.glassfish.samples.osgi.hello.service</groupId>
        <artifactId>osgi-pure</artifactId>
        <version>1.0-SNAPSHOT</version>
        <scope>provided</scope>
      </dependency>
      

      Notice "osgi-pure" bundle is specified as dependency as that is used to invoke the service.
    4. Add the following plugins to "pom.xml" to create the hybrid application:

      <plugins>
        <plugin>
          <groupId>org.apache.maven.plugins</groupId>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>2.0.2</version>
          <configuration>
            <source>1.5</source>
            <target>1.5</target>
          </configuration>
        </plugin>
        <plugin>
          <groupId>org.apache.felix</groupId>
          <artifactId>maven-bundle-plugin</artifactId>
          <configuration>
            <supportedProjectTypes>
              <supportedProjectType>war</supportedProjectType>
              <supportedProjectType>bundle</supportedProjectType>
              <supportedProjectType>jar</supportedProjectType>
            </supportedProjectTypes>
            <instructions>
              <Import-Package>javax.jws; version=2.0, \*</Import-Package>
              <Web-ContextPath>${pom.artifactId}</Web-ContextPath>
            </instructions>
          </configuration>
          <executions>
            <execution>
              <id>bundle-manifest</id>
              <phase>process-classes</phase>
              <goals>
                <goal>manifest</goal>
              </goals>
            </execution>
          </executions>
        </plugin>
        <plugin> <!-- Need to use this plugin to build war files -->
          <artifactId>maven-war-plugin</artifactId>
          <version>2.1-beta-1</version>
          <configuration>
            <archive>
              <!-- add bundle plugin generated manifest to the war -->
              <manifestFile>
                ${project.build.outputDirectory}/META-INF/MANIFEST.MF
              </manifestFile>
              <!-- For some reason, adding Bundle-ClassPath in maven-bundle-plugin
                   confuses that plugin and it generates wrong Import-Package, etc.
                   So, we generate it here.
              -->
              <manifestEntries>
                <Bundle-ClassPath>WEB-INF/classes/</Bundle-ClassPath>
              </manifestEntries>
            </archive>
            <failOnMissingWebXml>false</failOnMissingWebXml>
          </configuration>
        </plugin>
      </plugins>
      

      The "maven-bundle-plugin" is used to generate the appropriate OSGi metadata and "maven-war-plugin" is used to bundle the WAR file.

  4. Generate the deployable archive as:

    mvn clean package
    

    This generates "target/helloservice-1.0-SNAPSHOT.war". The generated manifest in "target/helloservice-1.0-SNAPSHOT/WEB-INF/classes/META-INF/MANIFEST.MF" looks like:

    Manifest-Version: 1.0
    Export-Package: org.glassfish.samples.osgi.jaxws.webservice;uses:="org
     .glassfish.samples.osgi.hello.service,javax.jws,org.osgi.framework";v
     ersion="1.0.0.SNAPSHOT"
    Bundle-Version: 1.0.0.SNAPSHOT
    Tool: Bnd-0.0.357
    Bundle-Name: helloservice
    Bnd-LastModified: 1272315464139
    Created-By: 1.6.0_17 (Apple Inc.)
    Bundle-ManifestVersion: 2
    Bundle-SymbolicName: org.glassfish.samples.osgi.jaxws.webservice.hello
     service
    Web-ContextPath: helloservice
    Import-Package: javax.jws;version="2.0",org.glassfish.samples.osgi.hel
     lo.service,org.glassfish.samples.osgi.jaxws.webservice;version="1.0",
     org.osgi.framework;version="1.5"
    
  5. Now this archive is a hybrid application, i.e. its a WAR file and an OSGi bundle. So lets deploy this file by copying the file to "domains/domain1/autodeploy/bundles" and see a message like:

    WS00018: Webservice Endpoint deployed App listening at address at http://localhost:8080/helloservice/AppService
    
  6. Accessing "http://localhost:8080/helloservice/AppService" in a browser window shows the following page:

Lets create the client project now to invoke this Web service.

  1. Create a new directory "client" and invoke the following command to generate the client-side artifacts:
    wsimport -keep http://localhost:8080/helloservice/AppService?wsdl
    
  2. Create a new directory "client" and a new file "HelloClient" in that directory as:
    package client;
    
    import org.glassfish.samples.osgi.jaxws.webservice.\*
    
    public class HelloClient {
            public static void main(String[] args) throws Exception {
                    App port = new AppService().getAppPort();
                    System.out.println(port.sayHello("Duke"));
            }
    }
    
    
    This "main" method gets a reference to the generated service class, gets the port from it, and then invokes the method by passing an argument.
  3. Compile the client code as:
    javac -d . -cp . client/HelloClient.java
    
  4. Invoke the client as:
    java -cp . client.HelloClient
    

    to see the result as:
    Hello Duke

    This result is coming from the OSGi service implementation.

All the three projects explained above are available in this download.

Using similar concept, a pure OSGi client can invoke a pure OSGi service which can then delegate the actual business method implementation to a JAX-WS endpoint and then use all the goodness of the underlying stack. This way, the benefits of JAX-WS are extended to a pure OSGi client and vice versa.

Also see other OSGi entries on this blog.

Technorati: totd jaxws osgi glassfish v3 webservice

Comments:

Hi, are you able to run this example on Glassfish 3.1?

Regards
Marcin Kwapisz

Posted by Marcin Kwapisz on April 05, 2011 at 06:58 PM PDT #

Marcin,

What error are you getting with GlassFish 3.1 ?

This issue is also discussed at:

http://java.net/projects/glassfish/lists/users/archive/2011-04/message/174

Posted by Arun Gupta on April 08, 2011 at 09:17 AM PDT #

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

profile image
Arun Gupta is a technology enthusiast, a passionate runner, author, and a community guy who works for Oracle Corp.


Java EE 7 Samples

Stay Connected

Search

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