Skip to main content

OSGi/JMS/MDB Example

Posted by ss141213 on April 22, 2010 at 12:39 AM PDT

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 the BundleActivator.  The bundle activator is configured about JMS destination via OSGi Config Admin service. 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.

AttachmentSize
uml.png9.93 KB
osgi-jms-mdb-1.zip20.25 KB

Comments

Little problem with bundle osgi-ejb-container bundle

Salutation !

When I'm starting glassfish, it's displaying every 5 seconds in server.log :

[#|2010-04-26T16:09:19.051+0200|INFO|glassfishv3.0|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=21;_ThreadName=Thread-1;|Error while starting bundle: file:/julien/osgi/glassfish/glassfishv3/glassfish/modules/autostart/osgi-ejb-container-3.1-SNAPSHOT.jar: org.osgi.framework.BundleException: Unresolved constraint in bundle org.glassfish.osgi-ejb-container [217]: package; (&(package=com.sun.enterprise.deploy.shared)(version>=3.1.0))|#]

[#|2010-04-26T16:09:19.051+0200|INFO|glassfishv3.0|javax.enterprise.system.std.com.sun.enterprise.v3.services.impl|_ThreadID=21;_ThreadName=Thread-1;|org.osgi.framework.BundleException: Unresolved constraint in bundle org.glassfish.osgi-ejb-container [217]: package; (&(package=com.sun.enterprise.deploy.shared)(version>=3.1.0)) at org.apache.felix.framework.Felix.resolveBundle(Felix.java:3263) at org.apache.felix.framework.Felix.startBundle(Felix.java:1597) at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:915) at org.apache.felix.framework.BundleImpl.start(BundleImpl.java:902) at org.apache.felix.fileinstall.internal.DirectoryWatcher.start(DirectoryWatcher.java:1027) at org.apache.felix.fileinstall.internal.DirectoryWatcher.start(DirectoryWatcher.java:1013) at org.apache.felix.fileinstall.internal.DirectoryWatcher.startAllBundles(DirectoryWatcher.java:1006) at org.apache.felix.fileinstall.internal.DirectoryWatcher.process(DirectoryWatcher.java:396) at org.apache.felix.fileinstall.internal.DirectoryWatcher.run(DirectoryWatcher.java:206) |#]

One or many other bundles are they necessary for autostart ?