Neil Bartlett was absolutely right that my last entry might have been a little too deep so I am simplifying the whole example in this entry by using plain OSGi Declarative Services in GlassFish v3. Declarative Services are described by Neil there and even more concisely by Peter Kriens with bnd at this location.
So I am going to have 2 modules in this example :
- service bundle, this is my plain OSGi bundle, which packages two classes, the API and one implementation of that API.
- webclient, plain war file that use Java EE injection to access the OSGi declared service in the above service bundle.
OSGi Declarative Service bundle
The OSGi bundle project is using maven to build as usual with GlassFish V3, the project contains 5 files
./pom.xml./src./src/main/java/examples/services/api/SimpleService.java./src/main/java/examples/services/impl/SimpleServiceImpl.java./src/main/resources/META-INF/MANIFEST.MF./src/main/resources/OSGI-INF/simpleservice.xml
Let's dive in the content now, first the service definition (unchanged from last entry) :
package examples.services.api;
/**
* Simple service defition
* @author Jerome Dochez
*/
public interface SimpleService {
/**
* Returns a implementation specific string
* @return a String
*/
public String getString();
}
The powerful implementation is using a different package and consist of the following java class :
package examples.services.impl;
import examples.services.api.SimpleService;
public class SimpleServiceImpl implements SimpleService {
public String getString() {
return "Simple Declarative Service implementation at your service !";
}
}
now the serious things can start, first with the manifest file containing the OSGi metadata :
Bundle-Version: 1.0 Bundle-SymbolicName: examples.services.declarative Bundle-Name: Services definition bundle for OSGi Declarative Services in GlassFish Export-Package: examples.services.api Service-Component: OSGI-INF/simpleservice.xml Bundle-ManifestVersion: 2
Two things are worth mentioning, first only the API package is exported, the implementation package is a private entity of the module. Encapsulation is one of the benefits of modularization. Second the "Service-Component" entry points to a separate xml file describing the services provided by this module (hence the name Declarative Services I suppose).
The content of the service definition file, is defined by the OSGi alliance specification, in this example the resulting xml is pretty simple :
<?xml version="1.0"?> <component name="decl-service-1" immediate="true"> <implementation class="examples.services.impl.SimpleServiceImpl"/> <service> <provide interface="examples.services.api.SimpleService"/> </service> </component>
Note that I gave a name (decl-service-1) to that service implementation, the curious reader should now experiment with creating more than services implementations, giving a different name to each of them. Build the project (mvn install).
The Java EE client
Very similar to the last blog entry where I was using Java EE injection (through the @Resource annotation), the webclient has not changed much :
import ...
import examples.services.api.SimpleService;
@WebServlet(urlPatterns={"/hello"})
public class HelloWorld extends HttpServlet {
@Resource(mappedName="decl-service-1") 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);if (simpleService!=null) { pw.println("SimpleService says " + simpleService.getString()); pw.println("<br>"); }} catch(Exception e) { e.printStackTrace(); } } }
Needless to say that the servlet is not importing the implementation package, just the API. In fact even if it tried, it would fail at runtime (not compile time unfortunately until JDK 7 starts supporting modules and OSGi metadata) since only the API package is exported by the bundle. Another mvn install to get the resulting war file.
Getting things together
GlassFish V3 does not ship with all the OSGi services, just the basic runtime, so first, to be able to use OSGi declarative services you need to download the support for Declarative Services for Felix from there , you will need to take the SCR jar file. Once downloaded you should deploy it to the application server and deploy the service bundle as well as the webclient web application (they should be in the respective target directories of your project if you built successfully)
asadmin deploy --type osgi org.apache.felix.scr-1.0.8.jar
asadmin deploy --type osgi simple-declarative-service.jar
asadmin deploy webclient.war
now point your web browser to http://localhost:8080/webclient/hello
Service class is interface examples.services.api.SimpleService<br>
First service is examples.services.impl.SimpleServiceImpl@ec0c06f
SimpleService says Simple Declarative Service implementation at your service !
Now combine this with the example of my last blog entry, you can see that I can now have my service API implemented either with a Spring component, or by a plain old OSGi Declarative Service and the actual implementation location will be immaterial to the servlet client code. Ah ! a good service based architecture can solve many problems just like a level of indirection can often lead to better code...
My next entry ? Maybe implementing my service with an EJB component, and show how one service can be implemented by a Spring bean, by an OSGi Declarative Service and by an EJB and have all three of them injected in the same servlet instance and be invoked transparently by the servlet code... would you be interested ?



