1 2 3 4 Previous Next

ss141213

47 posts

A few days back, Jerome bloggedabout how to deploy OSGi bundles in GlassFish using "asadmin deploy --type=osgi" command. In his blog, he also mentioned why you should not copy your bundles to glassfish/modulesdirectory. In this blog, I will talk about a filesystem based management agent that I have integrated in GlassFish and how you can take advantage of the same. We have integrated Apache Felix File Install in GlassFish v3. It is a simple yet very powerful bundle. It even takes care of the dependencies among bundles. It can use "configuration admin service" to configure bundles by reading configuration data from properties file placed in the watched dir. Quoting from File Install homepage verbatim:It uses a directory in the file system to install and start a bundle when it is first placed there. It updates the bundle when you update the bundle file in the directory and, when the file is deleted, it will stop and uninstall the bundle. File Install can do the same for configuration files. In the default installation of GlassFish v3, we have configured FileInstall to watch a directory called ${domain_dir}/autodeploy-bundles. ${domain_dir} typically expands to glassfish/domains/domain1, but it really depends how you have configured GlassFish. This is very similar to autodeploy directory that exists in GlassFish for non-OSGi JavaEE applications. Basically, it works like this: You copy a bundle (a .jar file) to the watched dir (autodeploy-bundles) -> the bundles gets installed and then get started. You modify the bundle in the watched dir -> the bundle gets updated. You remove the bundle from the watched dir -> the bundle gets stopped and uninstalled. To watch some other directory (let's say /tmp/bundles), create a properties file with following content and name it as org.apache.felix.fileinstall-something.cfg and copy it to autodeploy-bundles dir: # directory to watch felix.fileinstall.dir=/tmp/bundles/ # Time period of fileinstaller thread in ms. felix.fileinstall.poll=5000 # debug level felix.fileinstall.debug=1 # should new bundles be started or installed only? true => start, false => only install felix.fileinstall.bundles.new.start=true You will immediately see fileinstall creating /tmp/bundles and dedicating a thread to watch it. If you want to explore this feature, I suggest you take a look at File Install homepage. This feature is not available in any promoted builds yet; you will find it in trunk nightly builds of GlassFish v3 after 5th May 2009 or in promotion #b47a onwards. Watch out the nightly build area or promoted build area. Happy GlassFish-ing

Java VM has a standard option called -verbose as shown by java -help:

    -verbose[:class|gc|jni]
                  enable verbose output

To diagnose class loading issues, you need to specify -verbose:class. It's straight forward to use if you are launching the Java process yourself. How does a GlassFish user use this?

Option #1: Using java command directly

If you (like me) are starting glassfish using java command directly, then you could easily specify in your command output and you will see each class loading getting logged with a source of the class (You would like to redirect the output as there are just so much output that your terminal buffer may not be sufficient to capture the entire output):

java -verbose:class -jar glassfish/modules/glassfish.jar

Option #2: Starting GlassFish in foreground using start-domain command

Even if you are using start-domain command to start the server, I recommend starting GlassFish in foreground using"-v" option of start-domain so that the jvm log and server log appear in console output like option #1, thus allowing you to corelate them better. You would like to redirect the output  just like previous case as well. In this case, you just need the following setting in domain.xml:

        <jvm-options>-verbose:class</jvm-options>

and start server like this:

asadmin start-domain -v

Option #3: Starting GlassFish in background using start-domain command

If you are using asadmin start-domain command to start GlassFish in background, then you have to add the following jvm options in jvm-options section of your domain.xml:

        <jvm-options>-verbose:class</jvm-options>
        <jvm-options>-XX:LogFile=${com.sun.aas.instanceRoot}/logs/jvm.log</jvm-options>
        <jvm-options>-XX:+LogVMOutput</jvm-options>

In this case, the jvm.log will be next to server.log in logs directory. You are relying on proprietary JVM options here.

Because I forget about these additional options, I decided to write them down here.

Look at the code below where the main() loads a class whose name is specified by first argument. The second argument tells whether the class should be resolved or not. It also contains 5 test classes: A1, A2 and their dependencies: B, B1, B2. We will try to identify how early Bs are needed if I want to use As.

class Main {
  public static void main(String... args) throws Exception {
   Class c = Class.forName(args[0], Boolean.valueOf(args[1]), Main.class.getClassLoader());
  }
}

abstract class B { public abstract void bar(); }

class B1 extends B { public void bar(){} }

class B2 extends B { public void bar(){} }

class A1 {
  void foo(boolean cond) {
   B b = cond ? new B1() : new B2();
  }
}

class A2 {
  void foo(boolean cond) {
   B1 b = new B1();
  }
}


After compilation, we shall delete all the B classes so that we can identify when B1.class and B2.class are required while loading A1 or A2.

$ javac Main.java

$ rm B[12].class

$ ls
A1.class  A2.class  A.class  B.class  Main.class  Main.java

$ java Main A1 false

$ java Main A2 false

$ java Main A2 true

$ java Main A1 true
Exception in thread "main" java.lang.NoClassDefFoundError: B1
    at java.lang.Class.forName0(Native Method)
    at java.lang.Class.forName(Class.java:247)
    at Main.main(Main.java:3)
Caused by: java.lang.ClassNotFoundException: B1
    at java.net.URLClassLoader$1.run(URLClassLoader.java:202)
    at java.security.AccessController.doPrivileged(Native Method)
    at java.net.URLClassLoader.findClass(URLClassLoader.java:190)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
    at sun.misc.Launcher$AppClassLoader.loadClass(Launcher.java:301)
    at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
    ... 3 more

$ java -Xverify:none Main A1 true


This tells us that the byte code verification of A1.class caused some of the referencing classes (B1 in this case) to be loaded. Why did it then not fail when we tried to load A2? I turned to my long time friend and colleague Sundarajan, who works in Java SE group, to discuss this. He explained that since LHS and RHS used different types in the following line:

  B b = cond ? new B1() : new B2();

so, the byte code verifier had to load the types involved to ensure assignment compatibility.

Most developers don't care about this, but a few that do typically are writing some kind of lazily initialized systems where they probably don't want some dependencies to be loaded until the dependent service is actually invoked. Of course, frameworks like OSGi helps a long way in developing such systems. Even those developers have to pay attention to such details to get the desired lazy behavior. Some may say that reflection could avoid the problem, but that's not the point being made here.

I understand this behavior of bytecode verifier is probably implementation specific and can differ across implementations including implementation versions. For the record, my Java version is 1.6.0_21.

Actually I came across this in a much more coimplex scnerio while trying to investigate why certain modules in GlassFish are getting loaded at startup time contrary to my expectation, but basic test case remains same.

Sahoo Guest

OSGi/JMS/MDB Example Blog

Posted by Sahoo Guest Apr 21, 2010

Here is an example of yet another hybrid (OSGi + Java EE) application. This is a complete JMS consumer/producer example using OSGi and GlassFish. You can download the complete sample from here.

How to use the sample:

1. Download osgi-jms-mdb-1.zip and unzip it.

2. cd osgi-jms-mdb-1/

3. mvn clean install

4. Start GlassFish (something like "asadmin start-domain" or "java -jar glassfish.jar" will do)

5. Download OSGi/EJB container support bundle for GlassFish and 
install it by just copying to modules/autostart/:
wget http://download.java.net/maven/glassfish/org/glassfish/osgi-ejb-container/3.1-SNAPSHOT/osgi-ejb-container-3.1-SNAPSHOT.jar
cp osgi-ejb-container-3.1-SNAPSHOT.jar $glassfish/modules/autostart/

6. Create a couple of JMS resources by executing the following commands 
(you can use the admingui if you prefer GUI)

asadmin create-jms-resource --restype javax.jms.Topic jms/osgi.Topic1

asadmin create-jms-resource --restype javax.jms.ConnectionFactory jms/osgi.ConnectionFactory1

7.tail -f $glassfish/domains/domain1/logs/server.log.

8. Deploy the bundles and configure them as shown below 
(Although, you can copy them in any order you like, but I suggest you copy them 
in the following order to them in action and after each step, watch server.log).

cp ./message-consumer/target/osgijms1.consumer.jar $glassfish/domains/domain1/autodeploy/bundles/
cp ./message-producer/target/osgijms1.producer.jar $glassfish/domains/domain1/autodeploy/bundles/
cp ./osgijms1.producer.cfg $glassfish/domains/domain1/autodeploy/bundles/


 

Description of the sample 

As the diagram above shows, there are two bundles, viz:

a) A JMS message consumer bundle - This is also an OSGi bundle. It contains a single class which is the message consumer  or listener. It is implemented as a Message Driven Bean (MDB). There is nothing OSGi-specific in the bean. The jar file contains OSGi metadata and an additional GlassFish specific header called Export-EJB to indicate to the server that the OSGi bundle contains EJBs that need to be processed. Refer to my earlier blogs about deploying ejb jars as OSGi bundles in glassfish.

The relevant metadata for the EJB OSGi bundle looks like this:

[MANIFEST osgijms1.consumer.jar]

Export-EJB                              NONE                                                  
Bundle-ManifestVersion                  2                                                       Bundle-SymbolicName                     sahoo.osgijms1.consumer                              Bundle-Version                          1.0.0.SNAPSHOT                                          Import-Package                          javax.ejb,javax.jms                                     Manifest-Version                        1.0                                                 

The MDB looks like this:

 
@MessageDriven(mappedName = "jms/osgi.Topic1")

public class AnMDB implements MessageListener {
    public void onMessage(Message message) {
        String str = null;
        if (message instanceof TextMessage) {
            try {
                str = TextMessage.class.cast(message).getText();
            } catch (JMSException e) {
                // ignore
            }
        }
        if (str == null) str = message.toString();
        System.out.println("AnMDB Received: " + str);
    }
}
 
                                    

b) A JMS message producer bundle - This contains a single class which is the theBundleActivator.  The bundle activator is configured about JMS destination via OSGi Config Adminservice. Upon configured, it sends messages to the JMS destination. The complete source code for the message producer is given below:

public class Activator1 implements BundleActivator {

    public void start(BundleContext context) throws Exception {
        System.out.println("Message producer started - waiting to be configured with topic name");
        Properties props = new Properties();
        props.put(Constants.SERVICE_PID, "osgijms1.producer");
        context.registerService(ManagedService.class.getName(), new ManagedService() {
            public void updated(Dictionary properties) throws ConfigurationException {
                if (properties != null) {
                    String destinationName = (String) properties.get("osgijms1.Destination");
                    String connectionFactoryName = (String) properties.get("osgijms1.ConnectionFactory");
                    int noOfMsgs = Integer.valueOf((String) properties.get("osgijms1.NoOfMsgs"));
                    sendMessage(connectionFactoryName, destinationName, noOfMsgs);
                }
            }
        }, props);
    }

    private void sendMessage(String connectionFactoryName, String destinationName, int noOfMsgs) {

        Connection connection = null;
        try {
            InitialContext ctx = new InitialContext();

            ConnectionFactory connectionFactory = (ConnectionFactory) ctx.lookup(connectionFactoryName);

            connection = connectionFactory.createConnection();

            Session session = connection.createSession(
                    false,
                    Session.AUTO_ACKNOWLEDGE);

            Destination dest = (Destination) ctx.lookup(destinationName);
            MessageProducer producer = session.createProducer(dest);
            TextMessage message = session.createTextMessage();

            for (int i = 0; i < noOfMsgs; i++) {
                message.setText("This is message " + (i + 1));
                System.out.println("Sending message: " + message.getText());
                producer.send(message);
            }

            /*
             * Send a non-text control message indicating end of
             * messages.
             */
            producer.send(session.createMessage());
        } catch (JMSException e) {
            System.err.println("Exception occurred: " + e.toString());
        } catch (NamingException e) {
            System.err.println("Exception occurred: " + e.toString());
        } finally {
            if (connection != null) {
                try {
                    connection.close();
                } catch (JMSException e) {
                }
            }
        }
    }

    public void stop(BundleContext context) throws Exception {
    }
}

What's coming next

We will make the JMS resources available as OSGi services just like we make JDBC resources available as OSGi services. Once we do that, our message producer can track the service and send message once the resource is deployed.

 

As usual, if you have questions, please ask us in our forum or mailing lists.

Recently a user in GlassFish forum asked about developing JAX-WS web service in an OSGi bundle. Here is a complete sample demonstrating a JAX-WS web service invoking an OSGi service via OSGi service registry. You can download it from here. The diagram below hopefully explains the organisation of the sample:

 

As the above diagram shows, we have three components, viz:

1) osgi-service.jar: This is an OSGi bundle which provides a service to other bundles. It contains two POJOs, viz: a) an interface called sahoo.hybridapp.jaxws1.service.Watch, b) an implementation of the same interface called sahoo.hybridapp.jaxws1.service.WatchImpl.  This bundle also contains a bundle activator called sahoo.hybridapp.jaxws1.service.Activator, which is responsible for registering an instance of WatchImpl in OSGi service registry.

  2) web-service.war: This is a Web Application Bundle. A Web Application Bundle is a hybrid application - it's both a Java EE archive as well as an OSGi bundle. In this case, it is a war file as well as an OSGi bundle. It's a war file, because it contains a Servlet based JAX-WS end point. It's an OSGi bundle, because we want to make use of OSGi service in the implementation of our web service. It contains a single class called sahoo.hybridapp.jaxws1.webservice.WatchWebService which is defined like this:

 

package sahoo.hybridapp.jaxws1.webservice;

import sahoo.hybridapp.jaxws1.service.Watch;
import org.osgi.framework.*;
import javax.jws.*;

@WebService
public class WatchWebService {
   @WebMethod public String currentTime() {
       Watch watch = getService(Watch.class);
       System.out.println("WatchService: OSGi service is: " + watch);
       if (watch == null) {
          return "I don't have a watch";
       } else {
          return watch.currentTime();
       }
   }

   /**
     * 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(WatchWebService.class.getClassLoader()).getBundle().getBundleContext();
       ServiceReference ref = ctx.getServiceReference(type.getName());
       return ref != null ? type.cast(ctx.getService(ref)) : null;
   }
}


The MANIFEST.MF of web-service.war looks like this:

Bundle-ClassPath                        WEB-INF/classes/                       
Bundle-ManifestVersion                  2                                      
Bundle-SymbolicName                     sahoo.hybridapp.jaxws1.web-service     
Bundle-Version                          1.0.0.SNAPSHOT                         
Import-Package                          javax.jws;version="2.0",org.osgi.framework;version="1.5",sahoo.hybridapp.jaxws1.service;version="1.0"
Web-ContextPath                         /hybridapp.jaxws1.web-service       

3) web-service-client.jar: This is a plain jar file which makes use of JAX-WS stack of Java SE environment to invoke our web service. It has a single class called sahoo.hybridapp.jaxws1.webserviceclient.Main. The rest of the classes that are part of this jar are generated by wsdl compiler as part of build.

How to build, deploy and test:

Step 1: Start GlassFish

java -jar glassfish.jar


Step 2: Build and deploy the service bundles

cd hybridapp.jaxws1/

mvn clean install

This will produce two OSGi bundles called osgi-service/target/osgi-service.jar and web-service/target/web-service.war. Deploy these two OSGi bundles to GlassFish by simply copying them to domain1/autodeploy/bundles/ dir as shown below:

cp osgi-service/target/osgi-service.jar web-service/target/web-service.war $glassfish.home/domains/domain1/autodeploy/bundles/

GlassFish will automatically detect that web-service.war is a WAB and will perform necessary deployment of EE artifacts as a result of which a web service end point will be available. You can see something like this appearing in server.log:

WS00018: Webservice Endpoint deployed
WatchWebService  listening at address at http://localhost:8080/hybridapp.jaxws1.web-service/WatchWebServiceService
 


Step 3: Build and run the client

Once the web service is available, run

mvn -f web-service-client/pom.xml

to build web-service-client.jar. This is because the WSDL url, as specified in web-service-client/pom.xml, is not available until the web service is deployed.

To test, simple run:

java -jar web-service-client.jar

You shall see it will print the current time as obtained from the web service which in turn obtrains it from the OSGi service.

Look at the source code and maven pom.xmls to see how various maven plugins help make life easier. This sample uses a WAB to develop web service. You can develop JAX-WS service end points as EJBs as well and in that case you can deploy them as OSGi/EJB bundle in GlassFish. Enjoy developing OSGi enabled Java EE applications in GlassFish!!! As usual,  feddback most welcome.

I will wrap up my experience at eclipsecon. This was my first time at eclipsecon. Folks who had been here earlier told me that this year there was a lot of topics around eclipse runtime projects – so the focus has shifted from tools to runtime. Overall I liked eclipsecon.

My topic, OSGi & Java EE in GlassFish, was also of the same type. The slides of my talk are available here. The source code for the sample used to demonstrate “Modularized Enterprise Java Application” running in GlassFish is now available here. Just download, unzip and run it following the steps listed below.

 

The sample is organized as shown in the UML diagram below:

 

Features of the sample

All the components including the EJB are deployed as separate OSGi bundle.

EJB uses JPA in container managed mode to communicate with the database.

Both the admin client and Web Application Bundle client use the EJB via OSGi service registry.

Deployment order of bundles is irrelevant.

Local EJBs being accessed from other bundles.

 

Steps to run the sample:

asadmin start-domain

cp osgi-ejb-container.jar osgi-jpa,jar from maven repo to ${glassfish.home}/modules//autostart/

asadmin start-database

unzip ejb-as-service.zip

cd ./ejb-as-service

mvn clean install

cp ./common/target/hybridapp2-common.jar ./client/target/hybridapp2-client.jar ./service/target/hybridapp2-service.jar ./webclient/target/hybridapp2-webclient.war $glassfish.home/domains/domain1/autodeploy/bundles/

Open http://localhost:8080/eclipsecon/

 

Look at the source code, the metadata in jar files, and experiment. If you have any questions about OSGi & Java EE, send emails to users@glassfish.dev.java.net.

As promised earlier, I am going to quickly go over the key points I gathered from the talks I attended at eclipsecon and I also want to brief you about my topic at the conference. Because of a migrane attack earlier today which I firmly believe was caused by severe jat lag, I could not attend as much on day #2 as I had earlier decided to, and that's the very reason why I will keep this entry as short as possible. First and foremost, the big news of course is that jdk7 will have a module system which will be able to interoperate with OSGi module system. I know there must be a lot of literatures already written about it, so I won't spend any more time talking about it.

On day #1, there were two talks scheduled right after each other in post lunch session and they were about two projects in incubation: first one called Apache Aries, primarily contributed by IBM engineers, and the second one was Eclipse Gemini project, contributed by Oracle and VMWare (read SpringSource). Both the projects have to do with use of Enterprise OSGi, which encourages use of an “alternate” component model. As an enterprise Java user and developer, I would also like to see more natural support for EE component model. I know some of you reading this piece may already be classifying this into "what a crap" category. I know it is hard to fight perception, but I will try now that we have the right ingredients in the platform. As a platform provider, I feel obliged to send the right message to our users. The fact that there is a notion of what a profile is and an agreement on definition of two standard profiles (viz: web profile & full profile), is good enough start. The profile definition is not just in paper, but there is a reference implementation (see GlassFish Web Profile) of the same and support for seamless upgrade from one to other is proof of the fact that EE platform is not as monolithic as has been portrayed. Add on top it, starting with EE5, there was always an SPI for JPA providers to work in any compatible EE container. With all the hard work experts have put into EJB 3.x spec, the new avataar of EJB looks really interesting. We have seen a lot of enthusiasm about these features among our users in various GlassFish forums. I will rather trust EE platform experts coming up with profile definitions than creating too many fragments and the confusion created thus.

At the same time, I thinkmore can be done to enable modularity in EE applications. And that's where we seeOSGi as a value addition. Modularity has a cost too, so striking the right balance is going to be a key challenge. Modularity just does not mean breaking your big apps into smaller modules; service layers play a very important part as well, and of course with modularity comes the extra attention that application developers have to pay to dynamic behaviors (e.g., modules/services can go and come in any order.). Trust me, it is not easy to write such applications for production use. That's where we feel it is not a all or nothing world. You not only need a migration path, you also need mixed environment. That's what we are experimenting at GlassFish. We enable interaction between OSGi components and Java EE components. Interaction automatically meansbi-directional communication. e.g., we can seamlessly exportEJBs as OSGi services without user having to write any OSGi code. That allows any pure OSGi component, which is running without EE context, to discover the EJB and call it. That in turn allows users to write business components as EJBs so that they can take advantage of things like declarative security, transaction, context dependency and injection, etc., and yet allow them to be accessible to non-EE components. And yes, you can do distributed transactions as well. Since we also make various EE infrastructure services like TransactionManager, Data Sources, as OSGi services (some of this is already being covered by OSGi Enterprise spec), one can start a transaction in your pure OSGi bundle, invoke an EJB as an OSGi service, and the transaction context will propagate. The same holds good for security or persistence context propagation as well. In fact, we allow mix and match in the same application as well. If you still don't like EE component model, GlassFish is extensible enough to be augmented with blueprint containers or something else to support their model. By default, we ship declarative services bundle.We are inclussive. The support for such hybrid applications is not just limited to EJB applications, there is support for hybrid web apps as well. Some of the aforementioned features are already in v3 release, some are in trunk and some are still being baked.

If you are interested in knowing more about these or have different opinions and are present at eclipsecon, then I would be happy to see you at my talk details of which is given below. I hope to demonstrate some of these during the talk:

OSGi & Java EE in GlassFish

Grand Ball Room, Santa Clara Convention Centre

24 Mar 2010 at 1400 PDT

 

I hope you find the information useful,

Sahoo

Statutory Warning: Opinions presented here are my own.

Meeting with OSGi Experts

 

Although I had interacted with a number of active members of OSGi [1] expert group, last week I had the opportunity to meet some of these experts at an OSGi Enterprise Expert Group face2face event held at LinkedIn's Mountain View office. This is also the first time I met Richard S. Hall, the Felix guy (don't confuse with a very active Felix [2] committer by name Felix Meschberger). Richard has been involved in modularisation efforts for last decade or so. GlassFish team owes him and the Felix community a lot. Although Richard later on became part of Sun GlassFish team, we continue to engage with Felix community in the open forum. In the same event, I got to meet another Felix member by name Stuart Mcculloch, who has helped us a lot in our use of maven bnd plugin [3]. Stuart now works for Sonatype, where he is working on things like adding OBR support to Nexus [4]. This is also the first time I met Peter Kriens [5], B J Hargrave [6]. The other IBM rep that I met was Tim Ward, who is actively involved in project Aries. During dinner time, I got to talk to Jboss micro kernel engineers: Thomas Diesler and Aleš Justin. Although I had come across Thomas in Felix forum, I thought I was meeting Ales for the first time, but Ales surprised me when he said that we once were on the same phone conference to discuss about one of the Weld class loading issues we had encountered while integrating Weld [7] as the JSR 299 (CDI) implementation module in GlassFish v3. I have tried to add some feature Weld so that the same Weld runtime canbe shared by multiple applications and that's how it works in GlassFish. I had an interesting discussion with David Savage about Nimble [8]. I am looking forward to seeing a demo of the same at their booth in Eclipsecon. I met Hal Hilderbrand, an architect at Oracle Corp., who is very active in enterprise use of OSGi. Those who are interested in this topic should look at his excellent blog on this topic [9]. Mike Keith, who was the co-spec lead for JPA 1.0, who has been working in the area of object persistence, not just limited to Java persistence, for many many years now, is very actively involved in use of JPA in OSGi environment. I also met the EEG co-chairs: Tim Diekmann, Tibco, and David Bosschaert, Progress. Oh yes, Glyn Normington, from Spring Source, was also there. I try to follow his blogs [9A] as much as possible - they are short, yet very enlightening. 

I am not good at writing diary, so if I missed anyone or misrepresented anything, please excuse me and I appreciate for correcting me.

 

 

GlassFish and Enterprise OSGi

OK, what was I doing there and why am I writing all these? I am bad in writing such stuf any way. I work in GlassFish team, especially in the area of OSGi for last couple of years. GlassFish has a lot to offer to enterprise OSGi users. We actively participate in the OSGi EEG forum so that we can represent our users, who are already experimenting with OSGi enabled Java EE applications, what we call Hybrid Applications. We already have support for:

OSGi enabled Web Applications,

EJB applications.

They can in turn use all the EE frameworks and APIs like JSF, JPA, CDI, etc. We, at GlassFish, don't use any framework proprietary APIs, yet we have got things like byte code weaving/enhancementworking in JPA applications even when they run with an OSGi context. If you are interested in just sticking OSGi APIs and developing applications, we support that as well by implementing various OSGi EE APIs. I am scheduled to talk at Eclipsecon 2010 this Wednesday, the 22 March, about hybrid applications, why one needs them and how one goes about developing them. I hope to find some time to brief about my talk in a later blog. I am right now going to attend some talks in enterprise OSGi area and hope to be able to talk about them in my next blog as well. I look forward to see you at my talk titled “OSGi & Java EE in GlassFish [10].” Please spread the word and help make enterprise OSGi a success.

[1] http://osgi.org

[2] http://felix.apache.org

[3] http://felix.apache.org/site/apache-felix-maven-bundle-plugin-bnd.html

[4] http://www.sonatype.com/people/2009/07/nexus-pro-support-for-osgi-bundle-repositories/

[5] http://aqute.biz/

[6] http://blog.bjhargrave.com/

[7] http://seamframework.org/Weld

[8] https://blogs.paremus.com/category/nimble/

[9] http://www.tensegrity.hellblazer.com/2009/10/all-we-need-to-do-is-take-these-lies-and-make-them-true-somehow.html

[9A] http://underlap.blogspot.com/

[10] http://www.eclipsecon.org/2010/sessions/sessions?id=1392

Typical users of GlassFish use GlassFish in a separate process and they start GlassFish by using commands like the following:

java -jar glassfish.jar
                 or
asadmin start-domain

The above commands first launch an OSGi framework and then deploy the necessary bundles. What if you want toembed GlassFish in an existing OSGi runtime? The bootstrapping code was slightly complicated IMO. Recently when Apache Camel committer Charles Moulliard asked me some questions around embedding GlassFish v3 in an OSGi runtime, I decided to revisit the bootstrap module in order tosimplify it so that I had less explaining to do. I recently did exactly that. In this blog, I show how using a simple BundleActivator, one can embed GlassFish in an OSGi runtime. In the use case below, I first start an instance ofEquinox (plain vanilla Equinox runtime) anddeploy a test bundle which controls life cycle of GlassFish. You can use other launchers like karaf as well.

/tmp/equinox-3.5.1$ java -jar org.eclipse.osgi_3.5.1.R35x_v20090827.jar -console

osgi> install file:///tmp/embeddedgf-1.0-SNAPSHOT.jar
Bundle id is 1

osgi> start 1 
// The above command will start glassfish. You shall some output indicating the same.
// After GlassFish is started, you can use all regular GlassFish commands.
// e.g., to deploy an app, you can run "asadmin deploy ..."

osgi> stop 1
// This will stop GlassFish server. To start again, "start bundle #1"

What is this embeddedgf-1.0-SNAPSHOT.jar?

That's a tiny sample bundle I developed to embed GlassFish. It has a single class called GFActivator. This bundle activator does the following in the start() method:

1. Goes through glassfish modules directory and installs all bundles.

2. It then configures a configuration object with service PIDcom.sun.enterprise.glassfish.bootstrap.GlassFish. This is the PID for the <strong>ManagedService</strong> registered by GlassFish.  The PID name is subject to change. It then updates the configuration object with the following properties:

com.sun.aas.installRoot = /space/ss141213/WS/gf/v3/publish/glassfishv3/glassfish/
com.sun.aas.instanceRoot =  /space/ss141213/WS/gf/v3/publish/glassfishv3/glassfish/domains/domain1/

These two properties are enough for GlassFish to start.

3. It then locates GlassFish main bundle and starts it.

When I stop this bundle, it stops GlassFish server by stopping the main GlassFish bundle. Of course, you canrestart the bundle and GlassFish will be restarted. The java source and pom.xml are all part of this zip file.

GFActivator.java

package sahoo.embeddedgf;

import org.osgi.framework.*;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.service.cm.ConfigurationAdmin;
import org.osgi.service.cm.Configuration;

import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.util.List;
import java.util.ArrayList;
import java.util.Properties;

/**
 * Activator that starts and stops GlassFish.
 * To start glassfish, it does the following:
 * 1. Installs all bundles located in glassfish modules directory.
 * 2. Creates a configuration with a couple of properties:
 * com.sun.aas.installRoot and com.sun.aas.instanceRoot.
 * The former one refers to the directory where glassfish is installed.
 * (e.g., /tmp/glassfishv3/glassfish)
 * The latter one refers to the domain directory - this is a directory containing
 * configuration information and deployed applications, etc.
 * 3. Starts the main bundle of GlassFish. Main bundle is identified by Bundle-SymbolicName
 * org.glassfish.core.glassfish. This main bundle then is used to start any necessary bundle.
 *
 * In order to stop glassfish, we just stop the main bundle.
 *
 * @author Sanjeeb.Sahoo@Sun.COM
 */
public class GFActivator implements BundleActivator
{
    private BundleContext bundleContext;
    // I have hardcoded the glassfish path as this is just an example bundle.
    private String installRoot = "/space/ss141213/WS/gf/v3/publish/glassfishv3/glassfish/";
    private String instanceRoot = "/space/ss141213/WS/gf/v3/publish/glassfishv3/glassfish/domains/domain1/";
    private Bundle mainBundle;
    private static final String mainBundleName = "org.glassfish.core.glassfish";
    private static final String gfpid = "com.sun.enterprise.glassfish.bootstrap.GlassFish";
    private static final String JAR_EXT = ".jar";

    public void start(BundleContext bundleContext) throws Exception
    {
        this.bundleContext = bundleContext;
        // Install all gf bundles
        installBundles();
        configureBundles();
        startBundles();
    }

    public void stop(BundleContext bundleContext) throws Exception
    {
        if (mainBundle != null) {
            mainBundle.stop(Bundle.STOP_TRANSIENT);
        }
    }

    /**
     * Install all bundles located in modules directory and subdirectories under that.
     */
    private void installBundles() {
        for (File jar : findJars()) {
            try {
                bundleContext.installBundle(jar.toURI().toString());
            } catch (BundleException e) {
                // continue processing after logging the exception
                e.printStackTrace();
            }
        }
    }

    /**
     * Returns a list of jar files found in modules directory and subdirectories under that.
     * Any file with extension .jar is considered as a jar file.
     * @return a list of jar files
     */
    private List<File> findJars() {
        File modulesDir = new File(installRoot, "modules/");
        final List<File> jars = new ArrayList<File>();
        modulesDir.listFiles(new FileFilter(){
            public boolean accept(File pathname) {
                if (pathname.isDirectory()) {
                    pathname.listFiles(this);
                } else if (pathname.getName().endsWith(JAR_EXT)){
                    jars.add(pathname);
                }
                return false;
            }
        });
        return jars;
    }

    private void configureBundles() {
        final Properties props = new Properties();
        props.setProperty("com.sun.aas.installRoot", installRoot);
        props.setProperty("com.sun.aas.instanceRoot", instanceRoot);
        ServiceTracker tracker = new ServiceTracker(bundleContext, ConfigurationAdmin.class.getName(), null) {
            @Override
            public Object addingService(ServiceReference reference) {
                ConfigurationAdmin ca = ConfigurationAdmin.class.cast(bundleContext.getService(reference));
                try {
                    Configuration c = ca.getConfiguration(gfpid, null);
                    c.update(props);
                    close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                return super.addingService(reference); //TODO(Sahoo): Not Yet Implemented
            }
        };
        tracker.open();
    }

    /**
     * Starts GlassFish primordial bundle
     */
    private void startBundles() {
        for (Bundle b : bundleContext.getBundles()) {
            if (mainBundleName.equals(b.getSymbolicName())) {
                mainBundle = b;
                try {
                    b.start(Bundle.START_TRANSIENT);
                } catch (BundleException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}
 

Happy GlassFish-ing.

Statutory Warning: The code shown here is all sample code.

Did you know javap ignores Class-Path manifest attribute where as javac honors it? I didn't. Now I know. I have a jar called foo.jar with following manifest entry:

Manifest-Version: 1.0
Class-Path: javax.ejb.jar
Created-By: 1.6.0_16 (Sun Microsystems Inc.)
javax.ejb.jar contains all the EJB APIs and it is in the same directory as foo.jar. Yet, this is what I see when I run javap: 
javap -classpath foo.jar javax.ejb.EJB
ERROR:Could not find javax.ejb.EJB
On the other hand, javac works fine. e.g., I could successfully compile the following file: 
@javax.ejb.EJB 
public class A {}
like this: 
javac -classpath foo.jar A.java
It definitely looks like a bug in javap. The situation that prompted me to this experiment is we are trying to create some empty jars in glassfish/lib directory which would have Class-Path manifest reference to glassfish/modules directory. We need those empty jars as we have advertised about them in previous versions of GlassFish and our users use them in classpath during compilation, so we don't want to break their build scripts. I was trying to find out what java commands can consume those empty jars. At this point, javap can't. I am using Sun JDK 1.6.0_16-b01. It's not that bad considering javac can consume them and that takes care of our existing users' need.  
When my colleague Marina Vatkina sent me some code earlier today hoping a second pair of eyes would spot the obvious error, knowing how thorough Marina typically is, I knew there was no obvious error there. Simplified version of what was being attempted is shown below: 
import java.io.*;
import java.util.*;
import java.util.jar.*;

public class CreateJarWithManifest {
    public static void main(String[] a) throws Exception {
        Manifest manifest = new Manifest();
        manifest.getMainAttributes().putValue("Foo", "Bar");
        FileOutputStream fo = new FileOutputStream(new File("foo.jar"));
        JarOutputStream jo = new JarOutputStream(fo, manifest);
        jo.close();
        fo.close();
        new JarFile("foo.jar").getManifest().write(System.out);
    }
}
As you can see, the above code is creating a new Jar file with a manifest entry Foo: Bar and then reads the manifest from the jar and prints it to output for verification purpose. But, strangely this program prints an empty manifest. I vaguely remembered to have encountered this problem before, but never wrote it down anywhere. It took some time to figure out here and that's why I thought of putting it down here this time. The problem here is that if there is no attribute calledManifest-Version in the main section, the manifest is simply ignored. So, for the above code to work, we need something like this: 
        manifest.getMainAttributes().put(Attributes.Name.MANIFEST_VERSION, "1.0");
I don't understand why new Manifest() does not populate this attribute automatically. Even worse, why is there no warning or exception reported when such a manifest is used.  

While evaluating a GlassFish bug, I discovered a discrepancy in behavior of Class.getAnnotations() between IBM JRE and Sun JRE. the complex GlassFish issue boiled down to a simple test case as discussed below. The question is what should be the behavior of Class.getAnnotations() if one or more annotation class is not available at runtime. Consider the following test case:

// Main.java

import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@interface Bar {}

@Bar
class Foo {}

class Main {
 public static void main(String[] args) throws Exception{
  Annotation[] as = Foo.class.getAnnotations();
  System.out.println("Found " + as.length + " no. of annotations");
 }
}

When you compile this, you shall obviously get Main.class, Foo.class and Bar.class. Remove Bar.class and run:

java Main

On IBM JRE (I am using 1.6.0 SR5 on AIX platform), it results in

Exception in thread "main" java.lang.TypeNotPresentException: Type Bar not present
        at com.ibm.oti.reflect.AnnotationHelper.getAnnotation(AnnotationHelper.java:38)
        at com.ibm.oti.reflect.AnnotationHelper.getDeclaredAnnotations(AnnotationHelper.java:50)
        at java.lang.Class.getDeclaredAnnotations(Class.java:1628)
        at java.lang.Class.getAnnotations(Class.java:1589)
        at Main.main(Main.java:13)
Caused by: java.lang.ClassNotFoundException: Bar
        at java.lang.Class.forName(Class.java:169)
        at com.ibm.oti.reflect.AnnotationHelper.getAnnotation(AnnotationHelper.java:33)
        ... 4 more

where as while using Sun JRE (1.6.0_07), the program prints:

Found 0 no. of annotations

Conclusion:

I believe it is a bug in IBM JRE. There used to be a similar bug in Sun JDK. See http://bugs.sun.com/view_bug.do?bug_id=6322301for details. It says that Class.getAnnotations() is supposed to ignore when a annotation type can't be loaded. Sun JDK has been fixed. Now time for IBM JDK to be fixed.

autodpeloy-bundles directory has been moved to autodeploy/bundles. In my earlier blog titled Using filesystem operations to manage OSGi bundles in GlassFish, I had mentioned the directory name to be${domain_dir}/autodeploy-bundles/. Based on feedbackfrom co-developers and users, I have moved it to${domain_dir}/autodeploy/bundles/. As you may be knowing, ${domain_dir}/autodeploy is already watched by GlassFish server for deployable artifacts. So, having a dedicated subdirectory for OSGi bundles under that directory makes a lot of sense. This change is effective from GlassFish v3 b67. It's just a change in directory name, everything else remains unchanged. So, you can use autodeploy/bundles just like you could use autodeploy-bundles.

 

By the way, in case you have not paid attention, we have also added a new directory under glassfish/modules calledautostart where you can drop OSGi bundles to be automatically part of server runtime. This directory is also watched by Felix FileInstall. bundles from autostart directory are started after server is started. See this discussion for more details.

Introduction

 

What are our options to administer OSGi runtime in GlassFish? We have already talked about

a) a command line tool, and

b) a web console.

We now have a third option. Recently I came across a RESTbased console being developed by Filippo Diotalevi. We already have all the necessary support in GlassFish - the REST console requires OSGi/HTTP Service and we have an implementation of the same in GlassFish. So,  I thought I would give it a try and I found it a smooth ride.

How to install the REST console:

Here are the simple steps to install them:

0. Start GlassFish (I usually run "java -jar glassfish/modules/glassfish.jar"). I suggest you use v3 build #62 or higher available at http://download.java.net/glassfish/v3/promoted

1. Download GlassFish OSGi/HTTP Service bundle and copy it to glassfish/domains/domain1/autodeploy-bundles/.

2. Download the REST Console bundle and copy it to the same directory (i.e., glassfish/domains/domain1/autodeploy-bundles/).

That's all. If you are watching the GlassFish logs, you can see GlassFish detecting the new OSGi bundles and starting them.

Using the REST console


I will show you a couple of commands here. To use the console, you need a HTTP client. You can refer to the usage page for more information. I am using cURL. It is installed by default in many OS.

To list all the bundles in the system, we can run:

curl http://localhost:8080/osgi/restconsole/bundles/.txt

To see the details of a particular bundle (let;'s say bundle with id 100), run:
curl http://localhost:8080/osgi/restconsole/bundles/100.txt

 

The above commands are requesting data in text format. You can get it in XML and have a style sheet to nicely present it in a suitable format.

 

If you refer to the usage page, you will find commands for installing new bundles, controlling life cycle of bundles and querying bundle headers, services, etc.

 

Conclusion

 

This is the true benefit of using OSGi. The bundles are very portable. REST console needs OSGi/HTTP service and GlassFish has one. As long as that requirement is met, it runs successfully.Good job Filippo! I look forward to a proper release of the REST console. Those who are interested in OSGi may like to subcribe to his blog the OSGi Look, where he not only aggregates interesting OSGi related articles but also makes available his own content.

Starting with build #56, GlassFish v3 now comes pre-installed with Apache Felix Service Component Runtime, which is an implementation of OSGi Declarative Service Specification. This bundle is located inmodules/ directory and is started automatically when server starts.

Filter Blog

By date: