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.
- 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.
-
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.
-
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.
- Create the Maven project as:
mvn archetype:create -DarchetypeGroupId=org.apache.maven.archetypes \ -DgroupId=org.glassfish.samples.osgi.jaxws.webservice -DartifactId=helloservice
-
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 getService(Class 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.
- In the generated "pom.xml":
- Change the packaging to "war".
-
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.
-
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.
-
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.
-
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"
- 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
-
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.
- Create a new directory "client" and invoke the following command to generate the client-side artifacts:
wsimport -keep http://localhost:8080/helloservice/AppService?wsdl
- 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.
- Compile the client code as:
javac -d . -cp . client/HelloClient.java
- 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
Related posts:- TOTD #124: OSGi Declarative Services in GlassFish – Accessed from a Java EE client
- TOTD #12: Invoking a Java EE 5 Web service endpoint from JRuby
- TOTD #131: Dynamic OSGi services in GlassFish – Using ServiceTracker
- TOTD #23: JavaFX Client invoking a Metro endpoint
- TOTD #125: Creating an OSGi bundles using NetBeans and deploying in GlassFish