Miles to go …

April 26, 2010

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

Filed under: General — arungupta @ 11:22 pm

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

Share and Enjoy:
  • Print
  • Digg
  • Sphinn
  • del.icio.us
  • Facebook
  • Google Bookmarks
  • DZone
  • email
  • StumbleUpon
  • Technorati
  • Twitter
  • Slashdot
Related posts:
  1. TOTD #124: OSGi Declarative Services in GlassFish – Accessed from a Java EE client
  2. TOTD #12: Invoking a Java EE 5 Web service endpoint from JRuby
  3. TOTD #154: Dynamic OSGi services in GlassFish 3.1 – Using CDI and @OSGiService
  4. TOTD #23: JavaFX Client invoking a Metro endpoint
  5. TOTD #131: Dynamic OSGi services in GlassFish – Using ServiceTracker

2 Comments »

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

    Regards
    Marcin Kwapisz

    Comment by Marcin Kwapisz — April 6, 2011 @ 1:58 am

  2. 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

    Comment by Arun Gupta — April 8, 2011 @ 4:17 pm

RSS feed for comments on this post. TrackBack URL

Leave a comment

The views expressed on this blog are my own and do not necessarily reflect the views of Oracle.
Powered by WordPress