Friday Dec 11, 2009

GlassFish V3 Extensions, part 5 : Wombat container

With the release of the GlassFish v3, we have built a Java EE 6 application server that is :

  • Modular : based on OSGi, GlassFish v3 is a modular application server with a set of 200+ modules loaded on demand when users deploy applications that use such features.
  • Extensible : well, that's obvious that anything built on OSGi is extensible at the very least, extensible the OSGi way, but we have also added a set of APIs for fine control over extensibility points added to the product.
  • Embeddable : want to test your web app in maven ? you got it...

I am not going to dwell on a long laundry list of features we support or changed in this release, this would require a book (seriously, we rewrote so many parts, it's monumental) so I will just demonstrate some of these extension points by adding a new container to GlassFish. This new container will run a new component type called the Wombat component. Therefore I am proposing to write a wombat-container implementation in this entry. Sources can be found here.

Wombat Component

A wombat component is just a POJO annotated with a @Wombat annotation. The annotation looks like :

@Retention(java.lang.annotation.RetentionPolicy.RUNTIME)
public @interface Wombat {
    /\*\*
     \* Name to disambiguate different wombats
     \*
     \* @return a good wombat name
     \*/
    public String name();
} 

Nothing fancy here, there is a mandatory name attribute that need to be set to disambiguate various wombat components your application might package. Each wombat component has a set of public methods that make up its interface and can be called from any client code. The lifecycle of the wombat components is managed by the wombat container and such wombat components should be injectable in any Java EE components (that supports injection of course) without much effort.

Let's have a look at my first Wombat component :

@Wombat(name="simple")
public class SimpleWombat {
     public String saySomething() {
        return "Bonjour";
    }
}

Wombat Container, core functionalities

When implementing a GlassFish container extension, you need to implement 4 distinct interfaces, let's review them now :

  1. Sniffer : a sniffer is a singleton object that is instantiated when the first deployment (or application reload on startup) is invoked. Its main responsibility is to sniff the deployable artifacts and indicate to the deployment backend whether or not the deployable artifact contains components it is interested in. To make it clear, it sniffs the jars you deploy and check wether or not it contains Wombat components. The idea behind the sniffers is that they should be extremely lightweight since all sniffers get a change to check anything deployed, so you certainly don't want the sniffer to spend too much time doing so or loading too many modules to do their work.
  2. Container : a container is a singleton object again that represents the lifecycle of the component container. It's instantiated at the first deployment of a component (basically when the sniffer claims that at least one component of a supported type has been found in the deployable artifact). So here, first time you deploy an application (.jar) that contains at least one annotated class with @Wombat, the container is instantiated. In theory when the last component is un-deployed, we could destroy the container but many containers do not support being restarted so it stays alive until next startup when it won't be instantiated again (since there are no more component to serve). 
  3. Deployer : a deployer is also a singleton, thread safe object that deploys components to the container. In this case, the deployer is responsible for loading the component class, ensuring that everything is fine with the components inside the deployable artifacts and return a instance of a ApplicationContainer. 
  4. ApplicationContainer : there is one instance of that type per application and per container. So for instance, say you deploy 2 war files with wombat components embedded in, each deployment cycle will create a new instance of ApplicationContainer. These instances are responsible for suspend/resume and start/stop operations on the components. 

Still with me ? Good, it's almost over with the theory, one more thing : It's important to understand how everything works when dealing with applications containing several component types. For instance, take a war file containing a servlet and a wombat component. In such cases, each sniffer will pick up the application, a container of each type will be instantiated and the application will be deployed using each deployer returned by each container. At the end, deployment will end up with a number of ApplicationContainer instances (2 in the war example above). 

 Let's look now how this is implemented, first the sniffer :

@Service(name="wombat")
public class WombatSniffer implements Sniffer {
    public boolean handles(ReadableArchive source, ClassLoader loader) {
        return false;
    }
    public Class<? extends Annotation>[] getAnnotationTypes() {
        Class<? extends Annotation>[] a = (Class<? extends Annotation>[]) 
						Array.newInstance(Class.class, 1);
        a[0] = Wombat.class;
        return a;
    }
    public String[] getContainersNames() {
        String[] c = { WombatContainer.class.getName() };
        return c;
    } 

The interesting methods are handles() and getAnnotationTypes(). getAnnotationTypes() should return all annotations that identify components implemented by this container. In our example it's the @Wombat annotation type. You can imaging for instance that the EJBSniffer is returning @Stateless and other EJB related annotations. The returned annotation types are used by the deployment infrastructure to scan for all annotations in one pass rather than relying on each sniffer doing a pass for its annotation types. handles() should return true if the sniffing of the deployable artifact revealed any component supported by this container, this is useful when component can be defined in xml deployment descriptors. In this particular case, it returns false all the time since we only use annotations.

 Once the sniffer handles() returns true or if one of the annotation returned by getAnnotationTypes() has been spotted, the Container identified by getContainerNames() is instantiated. Let's look at that code : 

@Service(name="org.glassfish.examples.extension.WombatContainer")
public class WombatContainer implements Container {
    public Class<? extends Deployer> getDeployer() {
        return WombatDeployer.class;
    }
    public String getName() {
        return "wombat";
    }

As you can see the wombat container does not have many things to do when it's brought into memory, our web container for instance is slightly more complicated... The interesting method is the getDeployer(), let's look at the deployer code now :

@Service
public class WombatDeployer implements Deployer<WombatContainer, WombatAppContainer> {
    public boolean prepare(DeploymentContext context) {
        return false;
    }
    public WombatAppContainer load(WombatContainer container, DeploymentContext context) {
        WombatAppContainer appCtr = new WombatAppContainer(container);
        ClassLoader cl = context.getClassLoader();
        ReadableArchive ra = context.getOriginalSource();
        Enumeration<String> entries = ra.entries();
        while (entries.hasMoreElements()) {
            String entry = entries.nextElement();
            if (entry.endsWith(".class")) {
                String className = entryToClass(entry);
                try {
                    Class componentClass = cl.loadClass(className);
                    // ensure it is one of our component
                    if (componentClass.isAnnotationPresent(Wombat.class)) {
                        appCtr.addComponent(componentClass);
                    }
                } catch(Exception e) {
                    throw new RuntimeException(e);
                }
            }
        }
        return appCtr;
    }
    public void unload(WombatAppContainer appContainer, DeploymentContext context) { }
    public void clean(DeploymentContext context) { }
    private String entryToClass(String entry) {
        String str = entry.substring("WEB-INF/classes/".length(), entry.length()-6);
        return str.replaceAll("/", ".");
    }

As you can see, Deployer has 4 important methods, prepare, load, unload and clean. Prepare is called when the application is loaded, application containers cannot rely on the final class loader to be available during the prepare phase. The load phase loads the component container but does not start the component, it should not be possible to make client invocations yet. Unload and clean are the undeployment/unloading counterparts of the prepare and load methods. The load() method implementation is rather not sophisticated, it loads all classes from the archive and checks if the class is annotated with the @Wombat annotation. If so, it keeps track of all classes that are wombat components. 

Finally, the last part, the ApplicationContainer implementation :

public class WombatAppContainer implements ApplicationContainer {
    final WombatContainer ctr;
    final List<Class> componentClasses = new ArrayList<Class>();    
    public WombatAppContainer(WombatContainer ctr) {
        this.ctr = ctr;
    }
    void addComponent(Class componentClass) {
        componentClasses.add(componentClass);
    }                      
    public boolean start(ApplicationContext startupContext) throws Exception {
        for (Class componentClass : componentClasses) {
            try {
                Object component = componentClass.newInstance();
                Wombat wombat = (Wombat) componentClass.getAnnotation(Wombat.class);
                ctr.habitat.addComponent(wombat.name(), component);
            } catch(Exception e) {
                throw new RuntimeException(e);
            }
        }
        return true;
    }
    public boolean stop(ApplicationContext stopContext) {
        for (Class componentClass : componentClasses) {
            ctr.habitat.removeAllByType(componentClass);
        }
        return true;
    }
    public boolean suspend() {
        return false;
    }
    public boolean resume() throws Exception {
        return false;
    }
} 

As you can see the code is fairly straightforward once again. On start, we instantiate all wombat components the deployer found, then we add them to the habitat so they can be looked up using the NamingManager. The stop command undo what the start command did and we don't support suspend/resume in this example.

We now have all the code necessary to implement our wombat container. Let's look now at the optional features one might want to add to a GlassFish V3 container. 

Configuration

it's of course important to add configuration for our new wombat container and have that configuration store in our central configuration file, the domain.xml. This is particularly important to users so that they feel the extension is well integrated with the rest of GlassFish. Also by using the configuration infrastructure, you get REST access to the configuration for free and other freebies. To be able to save your configuration to the domain.xml, you only need to declare a few annotated interfaces (similar to JAXB interfaces). So let's take the following configuration format :

<wombat-container-config number-of-instances="5">
    <wombat-element foo="something" bar="anything"/>
</wombat-container-config>

As you can see, this xml snippet is just defining the content of the wombat container, it is agnostic under which element in the domain.xml it will added to or even where it will be stored, that magic is handled by the GlassFish configuration.

Let's have a look at our 2 annotated interfaces defining this configuration :

@Configured
public interface WombatContainerConfig extends Container {
    @Attribute
    public String getNumberOfInstances();
    public void setNumberOfInstances(String instances) throws PropertyVetoException;
    @Element
    public WombatElement getElement();
    public void setElement(WombatElement element) throws PropertyVetoException;
} 
@Configured
public interface WombatElement extends ConfigBeanProxy {
    @Attribute
    public String getFoo();
    @Attribute
    public String getBar();
}

It's pretty much self-explanatory, 2 classes for the 2 elements. The parent element define a sub-element annotated with @Element interface, the attributes are annotated with @Attribute. This is all you need to do read and store configuration data from our domain.xml. The configuration backend will take care of implementing these interfaces with the necessary hooks to read and write the XML correctly (or whatever format we might choose in a future release). The wombat container can use the getter methods to access the configuration as it is defined in the domain.xml it is running with. 

One last thing, how can you get the configuration added to the domain.xml when the container has been newly added (first time access). Let's revisit the sniffer class with some new methods since it is the natural place to handle first time initializations. I am not repeating the methods I already mentioned above : 

@Service(name="wombat")
public class WombatSniffer implements Sniffer {
    @Inject(optional=true)
    WombatContainerConfig config=null;
    @Inject
    ConfigParser configParser;
    @Inject
    Habitat habitat;
    public Module[] setup(String containerHome, Logger logger) throws IOException {
        if (config==null) {
            URL url = this.getClass().getClassLoader().getResource("init.xml");
            if (url!=null) {
               configParser.parseContainerConfig(habitat, url, WombatContainerConfig.class);
            }
        }
        return null;
    } 

 The key here is that we have an optional dependency on WombatContainerConfig so if is not present in the domain.xml, the instance variable will remain null. When the first wombat deployment is under way, deployment infrastructure will call setup(). Within the setup method implementation, if the instance is null, that mean there is no wombat container configuration present in the domain.xml and we add it using the small xml snippet mentioned above (and packaged in the container jar file in the init.xml). The config parser is a utility API that can be used to parse a random xml snippet and add it to the right location in the domain.xml. By the way, location, location, yes but how is that location defined ? Look at WombatContainerConfig, it extends the Container interface, that's the marker interface that tell the configuration backend that it's a container configuration (we have a few documented extension hooks like this). After set up is called, the domain.xml will look like : 

<domain....>
.... 
   <configs>
    <config name="server-config">
      <wombat-container-config number-of-instances="5">
        <wombat-element foo="something" bar="anything" />
      </wombat-container-config>
      <http-service>
.... 
</domain>  

as you can see wombat-container-config element was added to the right location under config so that when clustering is enabled, the wombat container config can be referenced or have values specific to a node or a configuration.

Extras

 Now that we have added configuration, it would be nice to be able to change it. The simplest way to do that is to add a CLI command, but you can also add an admin GUI plugin if you feel more adventurous, it's not any harder. Let's look at the CLI admin command implementation.

@Service(name="wombat-config")
public class WombatConfigCommand implements AdminCommand {
    @Param
    String instances;
    @Inject
    WombatContainerConfig config;
    public void execute(AdminCommandContext adminCommandContext) {
        try {
            ConfigSupport.apply(new SingleConfigCode<WombatContainerConfig>() {
                public Object run(WombatContainerConfig wombatContainerConfig) 
			throws PropertyVetoException, TransactionFailure {
                    wombatContainerConfig.setNumberOfInstances(instances);
                    return null;
                }
            }, config);
        } catch(TransactionFailure e) {            
        }
    }
}

As you can see, he too gets the Wombat configuration injected and the command is defining a single parameter (instances) which will be used to change the number of instances attribute on the wombat configuration. Like any configuration change in GlassFish V3, the command must use a Configuration change transaction to ensure some ACID properties to the configuration change. 

This command can be invoked by doing :

asadmin wombat-config --instances 10

and you will see the wombat-config xml snippet in domains/domain1/config/domain.xml change from 5 (it's initial value) to 10. There are more extensions capability like I mentioned earlier, you can add an admin GUI plugin, you can have some code run when the user creates a new domain (to get once again some default configuration stored in the newly created domain.xml) and few other advanced goodies.

The client

So we have now a complete container implementation for wombat components, let's build it and install it in glassfish :

cd wombat-container
mvn install
cp target/wombat-container.jar <glassfishv3>/glassfish/domains/domain1/autodeploy/bundles

Now we need to create some wombat components and use them inside a client like a servlet. To do that, I am defining a web application which will contain a simple servlet and a single wombat component. Please note that this servlet is a converged application since it contains both Java EE artifacts like the servlet and foreign components like the Wombat component. The structure of this application is however a simple war file, no need to have to package the wombat components in a different file.

./pom.xml
./src
./src/main
./src/main/java
./src/main/java/components
./src/main/java/components/SimpleWombat.java
./src/main/java/HelloWorld.java
./src/main/webapp
./src/main/webapp/WEB-INF
./src/main/webapp/WEB-INF/web.xml

let's look at the sources, SimpleWombat.java first :

@Wombat(name="simple")
public class SimpleWombat {
    public String saySomething() {
        return "Bonjour";
    }
}

and the servlet :

@WebServlet(urlPatterns={"/hello"})
public class HelloWorld extends HttpServlet {
    @Resource(name="Simple")
    SimpleWombat wombat;  
    public void doGet(HttpServletRequest req, HttpServletResponse res)
            throws IOException, ServletException {
        PrintWriter pw = res.getWriter();
        try {
            pw.println("Injected service is " + wombat);
            if (wombat!=null) {
                pw.println("SimpleService says " + wombat.saySomething());
                pw.println("<br>");
            }
  	} catch(Exception e) {
        	e.printStackTrace();
        }
    }
} 

to use the client, just build and deploy

cd webclient
mvn install
asadmin deploy target/webclient.war

and access the page at http://localhost:8080/webclient/hello 

As you can see, I reuse the same @Resource type of dependency I used to inject my OSGi declarative services in my previous blog so you can really use only Java EE APIs to use extended features.  

Conclusion

 In this blog entry, I demonstrated how easy it can be to add a new container for new component types to GlassFish V3. You might ask yourself why would you ever need to do that and in all honesty, very few people will need to but the truth is that certain domain specific problems could be easily resolved by added domain specific components with their specific lifecycle, responsibilities or invocation methods. Also with the various web related framework flourishing, the scripting languages bases solutions, there are more and more application types these days. One more thing, we used the infrastructure described above to implement all of the Java EE containers (like EJB, Web, JPA, WebServices...) and others like our Rails containers so I am pretty confident you can leverage these features, we tested them well...

Tuesday Jun 09, 2009

GlassFish V3 Extensions, part 4 : OSGi Declarative Services

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.
Files can be downloaded there, there are two sub directories for each module. 

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 ?

Tuesday Apr 21, 2009

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


        
    

Thursday Apr 09, 2009

GlassFish V3 Extensions, part 2 : Managed OSGi bundles

     I am back this time to talk about a feature I just added to GlassFish v3. When extending GlassFish, you really have a few different ways to do so, you can write a plain old jar file and put it in the lib directory but preferably, you should write an OSGi bundle. The best for maximum portability is to write a jar which can be an OSGi bundle and also a plain old jar when possible. So once you have your brand new OSGi bundle, how can you install it in GlassFish v3 so it becomes part of the ecosystem ?

    There are basically two ways to do that today :

  • place the bundle in the glassfish/modules directory.

  • deploys the bundle just like you deploy any other application.

    Let's look at the disadvantages of each solution : 

  1. modules directory : Using the modules directory solution is easy and quick, and seems to be the natural way of adding functionality to GlassFish. However it has a few disadvantages :

    • no module management, you need to manage the files in modules yourself (adding, updating, removing).

    • no clustering support. we don't have clustering support in v3 yet but using the modules directory will not guarantee that all server instances will be updated when you add or remove bundles from the modules directory.

    • hard to figure out which bundles in modules directory are yours versus the ones we delivered. 

    • bundles are not started in OSGi automatically in V3, they are just installed, so if they have a bundle activator for instance, you need to start them manually. One easy way to do that is to change the autostart list in the felix configuration at glassfish/osgi/felix/config/config.properties.

  2. Deployment : using deployment method you need to manage dependencies correctly when you deploy. So far you can only deploy one bundle at a time (ok, if you don't like that, file an RFE) so if your bundles import other bundles and so on, you better deploy them in the right order.

I think solution 2 has many advantages, it's a managed operation which is protected by the admin password if you set up on, it also does not require to have access to the application server file system and it's remotable. To illustrate solution 2, I am now going to build a new OSGi bundle and deploy using the normal "asadmin deploy" command. Let's start with building a new bundle using maven, something really simple, an API bundle containing one service definition that looks like this :

package examples.services.api;
/\*\*
 \* Simple service defition
 \* @author Jerome Dochez
 \*/
public interface SimpleService {
    /\*\*
     \* Returns a implementation specific string
     \* @return a String
     \*/
    public String getString();
}


So nothing that will get me a Nobel prize here ;-) Let's see the project files now.

./pom.xml
./src/main/java/examples/services/api/SimpleService.java
./src/main/resources/META-INF/MANIFEST.MF


Due to the simplicity of the project, I manually coded the manifest file rather than using BND, also it helps the no-magic theme here.

Bundle-Version: 1.0
Bundle-SymbolicName: examples.services.simple-bundle-api
Bundle-Name: Services definition bundle for OSGi integration in GlassFish
Export-Package: examples.services.api
Bundle-ManifestVersion: 2


Finally, the complete pom.xml file looks like this...

 <?xml version="1.0"?>
<project>
    <modelVersion>4.0.0</modelVersion>
    <groupId>examples.services</groupId>
    <artifactId>simple-service-api</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>jar</packaging>
    <name>Simple Service Provider API definition</name>
    <developers>
        <developer>
            <id>dochez</id>
            <name>Jerome Dochez</name>
            <email>jerome.dochez@sun.com</email>
            <url>blogs.sun.com/dochez</url>
        </developer>
    </developers>
    <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>
</project>

Build your code

mvn install 

Ok so time to deploy your new bundle in V3, this is really hard, just do 

 >asadmin deploy --type osgi target/simple-bundle-api.jar

--type osgi is necessary so the bundles is installed in the OSGi runtime rather than in Java EE containers. now let's check if the bundle is installed correctly, let's start the felix console :

>telnet localhost 6666
Tower:impl dochez$ telnet localhost 6666
Trying 127.0.0.1...
Connected to localhost.
Escape character is '\^]'.

Felix Remote Shell Console:
============================

-> 

type "ps" and you should get a list of all the installed bundles, one should be our newly deployed OSGi bundle (note that your bundle ID might be different) 

[ 188] [Active     ] [    1] Services definition bundle for OSGi integration in GlassFish (1.0)

lets make sure this is how bundle :

-> headers 188

Services definition bundle for OSGi integration in GlassFish (188)
-------------------------------------------------------------------------
Manifest-Version = 1.0
Archiver-Version = Plexus Archiver
Bundle-Name = Services definition bundle for OSGi integration in GlassFish
Built-By = dochez
Bundle-SymbolicName = examples.services.simple-bundle-api
Export-Package = examples.services.api
Bundle-Version = 1.0
Build-Jdk = 1.6.0_07
Created-By = Apache Maven
Bundle-ManifestVersion = 2

when you are done with the bundle, just undeploy :

>asadmin undeploy simple-bundle-api

Voila !


Next article will be a lot more interesting, we will be using our newly deployed OSGi bundle with a mix of Spring, OSGi and Java EE 6 code...

Saturday Mar 07, 2009

GlassFish V3 Extensions, part 1 : Grizzly adapters support

I am embarking on an attempt to describe some of the extensibility points that GlassFish v3 has to offer. In this first article, I will describe how you can deploy raw unmodified grizzly adapters like any other GlassFish application. 

As you probably know, GlassFish v3 is built on top of grizzly, yet up to now, it was not possibly to deploy a native Grizzly adapter  directly inside V3.

Jean Francois was asking me to add this support some time back so people could leverage investments they made in developing grizzly extensions by using them in an application server that will soon support clustering and other goodies out of reach for the grizzly community.

As usual with GlassFish V3, now Grizzly applications are treated just like any other applications and you can use familiar tools to manage them.

So, to run your favorite adapter, you simply have to declare it using a small XML file, using the following format

<adapters>
   <adapter context-root="/cometd" class-name="com.foo.bar.CometdAdapter">
   </adapter>
</adapters> 

 you can also initialize the adapter with the following Beans type of pattern :

<adapter ...>
 <property name="foo" value="bar"/>
</adapter>

Internally Adapter.set<name> = <value> will be called, like:

setFoo(bar)

Of course, you can place as many adapters as you want in the xml file. Finally, place the xml file inside your jar at META-INF/grizzly-glassfish.xml and then using the latest snasphot build , just deploy your jar file or directory like any other GlassFish application :

asadmin deploy adapter.jar
OR asadmin deploy myadapterdirectory 

 Please note that the context-root parameter on the deploy command is not applying to grizzly applications since they can have multiple adapters at multiple context-roots.

 Now, Jean Francois has already developed some extensions so folks can also start deploying grizzlet directly into GlassFish, more to come from him. The source code for the grizzly container is about 5 classes, and it is a great example on how to extend the GlassFish application server to add support for new application types, find it here.

Friday Apr 11, 2008

GlassFish V3 runs on OSGi

As some of you may remember when I introduced the HK2 project last year, I described it as a friendly environment to OSGi where we would eventually be capable of running GlassFish V3 on top of an OSGi runtime. Thanks to the good work of Sahoo, this vision has finally been completed and I am happy to report that since last week, we are now capable to run GlassFish V3 on top of Apache Felix, one of the OSGi runtime available to the open source community.


In fact, we have also tried KnopflerFish (I like the name of course) and it runs fine so we are pretty confident that any OSGi runtime will be supported with minimum effort.


Now the interesting question that everyone will be asking soon, are we switching to OSGi as our underlying module subsystem ? Today I can say yes, we will. Some people might say that we changed our mind about OSGi, we didn't. From the beginning I always said we wanted to be friendly to OSGi, we just realized that vision... It is pretty clear that there is a big industry support for OSGi and it is important that GlassFish can be part of that excitement. I cannot commit on which implementation we will eventually use because we are still experimenting with some of them and we need to following conditions to be met :


    \* open source

    \* friendly license to one of our dual open source licenses as well as to our Java EE licensees.

    \* good community (forums, mailing list) to get our questions answered

    \* possibility of a commiter to be able to push our bug fixes.


Whichever implementation we choose will get a huge boost from this endorsement because we will certainly have engineers capable of fixing bugs, adding features but also we have top performance engineers at Sun that will help with the overall performance of the OSGi runtime. 


Sahoo will probably blog in a day or two explaining in details the technicalities of the solution we adopted but let me introduce it here. We are still capable of running in both HK2 mode and in OSGi mode, I am not sure how long we will maintain the HK2 mode but so far the startup is a lot faster in HK2 (1 second) versus OSGi (2 seconds). Ok no big deal I suppose but we will work on that. It's hardly surprising that HK2 is faster, it is not meant to be a generic modular subsystem like OSGi, it is quite optimized for our V3 work !


None of GlassFish code depends on OSGi libraries (or very very little), we isolated those dependencies in HK2 which makes it very easy for us to switch OSGi runtimes or even module management runtime with no code changes. The HK2 project will continue as it is offering a lot more than just module management, in particular we have the following features that we continue to use heavily :


    \* module management isolation layer

    \* module management through Repositories (coming in OSGi R5)

    \* lightweight component model

    \* dependency injection 

    \* configuration handling


So if you want to play with the OSGi version of GlassFish V3, I would recommend downloading the latest binaries from there.

It's our latest build, don't expect miracle and please file bugs when you find one.

 

and to run GlassFish in OSGi mode, simply do from your installation directory

    	java -DGlassFish_Platform=Felix -jar modules/glassfish-10.0-SNAPSHOT.jar

to run in hk2 mode, just do

	java -jar modules/glassfish-10.0-SNAPSHOT.jar 
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