GraniteDS is a free and open source (LGPL’d) alternative to Adobe® LiveCycle® (Flex™ 2+) Data Services for Java EE application servers. Java Message Service (JMS) is a technology designed for handling real-time Publish/Subscribe architectures of which Apache ActiveMQ is an implementer.

By default, the GraniteDS messaging architecture (called Gravity) uses its own internal simple Publish/Subscribe infrastructure. This works great if your front-end only needs to worry about handling clients from Adobe Flex, but what about if you need to send messages from other back end processes and have your flex app update in real-time?

The GraniteDS download package ships with a good example called graniteds_chat. It demonstrates a simple chat application between two or more Flex clients using the built-in Publish/Subscribe architecture of GraniteDS’s Gravity component. My goal is to use this example, but replace the default GraniteDS architecture with the more robust ActiveMQ architecture running in a separate process.

The reasons for doing so are several. First, ActiveMQ can handle persistent messaging. If my server crashes for some reason, I won’t lose messages that were currently in the queues/topics. Secondly, I want to demonstrate running ActiveMQ in a separate process. GraniteDS already has an Embedded ActiveMQ example on their wiki where it loads and runs inside the Tomcat container. I want to break it out as a completely separate process. This gives me additional future scalability if I need to run ActiveMQ on a different box entirely, or setup a multi-machine JMS infrastructure.

First, download the latest package of GraniteDS. Extract out the folder structure, and modify any of the env.properties file to point to your installed Flex SDK. My env.properties inside graniteds/examples/graniteds_chat looks like this:

#==============================================================================
# Granite Data Services Examples build properties (http://www.graniteds.org).
#==============================================================================

# Set 'FLEX_HOME' property to your flex3 sdk installation directory and
# 'FLEX_TASKS_JAR' to your flexTasks.jar location.
FLEX_HOME=C:/Program Files (x86)/Adobe/Flex Builder 3 Plug-in/sdks/3.4.0.9271
FLEX_TASKS_JAR=${FLEX_HOME}/ant/lib/flexTasks.jar

# Set 'SERVER_HOME' property to your JBoss installation directory and
# 'SERVER_HOME_DEPLOY' to your JBoss deploy directory.
#SERVER_HOME=/jboss-4.2.3.GA
#SERVER_HOME_DEPLOY=${SERVER_HOME}/server/default/deploy

# Alternatively, set 'SERVER_HOME' property to your Tomcat installation
# directory and 'SERVER_HOME_DEPLOY' to your Tomcat deploy directory.
SERVER_HOME=C:/apache-tomcat-6.0.16
SERVER_HOME_DEPLOY=${SERVER_HOME}/webapps

Next, run ANT inside the graniteds_chat folder. It will pick up everything that needs to be compiled from the parent folders and deploy a war package to your local tomcat installation. Before starting tomcat and watching the default chat example in action, you’ll first have to modify Tomcat a bit so that it can support non-blocking IO. GraniteDS recommends using either the HTTP APR or the NIO connector in Tomcat to enable non-blocking IO. For our purposes, we’ll use the NIO connector for Tomcat. Inside the Tomcat conf folder, edit server.xml and look for a line like the following:

<Connector port="8080"
           protocol="HTTP/1.1"
           connectionTimeout="20000"
           redirectPort="8443" />

Modify this section so that Tomcat can use the NIO Http connector.

<Connector port="8080"
           protocol="org.apache.coyote.http11.Http11NioProtocol"
           connectionTimeout="20000"
           redirectPort="8443" />

After that change is complete, start Tomcat and navigate with two separate browsers to http://localhost:8080/graniteds-chat. Login with separate usernames and make sure that you can chat successfully between the two browsers. You should see something like the following:
Andrew1_chat
Andrew2_chat

Next, let’s go about integrating ActiveMQ. Download and install ActiveMQ in its own folder. Browse to the bin folder and run activemq.bat. This should be all we need to do on the ActiveMQ side of things. Next, we need to configure GraniteDS to talk to ActiveMQ via JMS instead of its internal messaging architecture.

Stop Tomcat and drill into the installed chat application and modify services-config.xml. Mine is located at C:/apache-tomcat-6.0.16/webapps/graniteds-chat/WEB-INF/flex/services-config.xml. Before editing, you’ll notice that it’s using its own internal SimpleServiceAdapter.

<?xml version="1.0" encoding="UTF-8"?>

<!--
 GRANITE DATA SERVICES
 Copyright (C) 2007-2008 ADEQUATE SYSTEMS SARL

 This file is part of Granite Data Services.

 Granite Data Services is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation; either version 3 of the License, or (at your
 option) any later version.

 Granite Data Services is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 for more details.

 You should have received a copy of the GNU Lesser General Public License
 along with this library; if not, see <http://www.gnu.org/licenses/>.
-->

<services-config>

    <services>
        <service id="messaging-service"
           class="flex.messaging.services.MessagingService"
           messageTypes="flex.messaging.messages.AsyncMessage">
            <adapters>
                <adapter-definition id="default" class="org.granite.gravity.adapters.SimpleServiceAdapter" default="true"/>
            </adapters>

            <destination id="gravity">
                <channels>
                    <channel ref="my-gravityamf"/>
                </channels>
            </destination>
        </service>
    </services>

    <channels>
        <channel-definition id="my-gravityamf" class="org.granite.gravity.channels.GravityChannel">
            <endpoint
               uri="http://{server.name}:{server.port}/{context.root}/gravity/amf"
               class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>

</services-config>

You’re going to want to modify this services-config.xml file so that it uses ActiveMQ and the JMS Adapter.

<?xml version="1.0" encoding="UTF-8"?>

<!--
 GRANITE DATA SERVICES
 Copyright (C) 2007-2008 ADEQUATE SYSTEMS SARL

 This file is part of Granite Data Services.

 Granite Data Services is free software; you can redistribute it and/or modify
 it under the terms of the GNU Lesser General Public License as published by
 the Free Software Foundation; either version 3 of the License, or (at your
 option) any later version.

 Granite Data Services is distributed in the hope that it will be useful, but
 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License
 for more details.

 You should have received a copy of the GNU Lesser General Public License
 along with this library; if not, see <http://www.gnu.org/licenses/>.
-->

<services-config>

    <services>
        <service id="messaging-service"
           class="flex.messaging.services.MessagingService"
           messageTypes="flex.messaging.messages.AsyncMessage">
            <adapters>
                <adapter-definition id="default" class="org.granite.gravity.adapters.JMSServiceAdapter" default="true"/>
            </adapters>

            <destination id="gravity">
                <channels>
                    <channel ref="my-gravityamf"/>
                </channels>
                <properties>
                    <jms>
                        <destination-type>Topic</destination-type>
                        <connection-factory>java:comp/env/jms/flex/TopicConnectionFactory</connection-factory>
                        <destination-jndi-name>java:comp/env/jms/discussion</destination-jndi-name>
                        <destination-name>discussion</destination-name>
                        <acknowledge-mode>AUTO_ACKNOWLEDGE</acknowledge-mode>
                        <transacted-sessions>false</transacted-sessions>
                        <!--
                             JNDI environment. Specify the external JNDI configuration to
                             access a remote JMS provider.
                        -->
                        <initial-context-environment>
                            <property>
                                <name>Context.PROVIDER_URL</name>
                                <value>tcp://localhost:61616</value>
                            </property>
                            <property>
                                <name>Context.INITIAL_CONTEXT_FACTORY</name>
                                <value>org.apache.activemq.jndi.ActiveMQInitialContextFactory</value>
                            </property>
                        </initial-context-environment>
                    </jms>
                </properties>              
            </destination>
        </service>
    </services>

    <channels>
        <channel-definition id="my-gravityamf" class="org.granite.gravity.channels.GravityChannel">
            <endpoint
               uri="http://{server.name}:{server.port}/{context.root}/gravity/amf"
               class="flex.messaging.endpoints.AMFEndpoint"/>
        </channel-definition>
    </channels>

</services-config>

You may have noticed that the GraniteDS JMSServiceAdapter uses JNDI to find and connect to JMS. Since Tomcat has a valid JNDI container, let’s define the necessary info in Tomcat. You can add the JNDI info inside tomcat’s conf/context.xml file.

<?xml version='1.0' encoding='utf-8'?>
<!--
 Licensed to the Apache Software Foundation (ASF) under one or more
 contributor license agreements.  See the NOTICE file distributed with
 this work for additional information regarding copyright ownership.
 The ASF licenses this file to You under the Apache License, Version 2.0
 (the "License"); you may not use this file except in compliance with
 the License.  You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
-->
<!-- The contents of this file will be loaded for each web application -->
<Context>

    <!-- Default set of monitored resources -->
    <WatchedResource>WEB-INF/web.xml</WatchedResource>
   
    <!-- Uncomment this to disable session persistence across Tomcat restarts -->
    <!--
   <Manager pathname="" />
   -->

    <!-- Uncomment this to enable Comet connection tacking (provides events
        on session expiration as well as webapp lifecycle) -->
    <!--
   <Valve className="org.apache.catalina.valves.CometConnectionManagerValve" />
   -->
   
    <Resource name="jms/flex/TopicConnectionFactory"
              type="org.apache.activemq.ActiveMQConnectionFactory"
              description="JMS Connection Factory"
              factory="org.apache.activemq.jndi.JNDIReferenceFactory"
              brokerURL="tcp://localhost:61616"
              brokerName="activeMQBroker" />
    <Resource name="jms/discussion"
              type="org.apache.activemq.command.ActiveMQTopic"
              description="Discussion topic for GraniteDS"
              factory="org.apache.activemq.jndi.JNDIReferenceFactory"
              physicalName="discussion" />
   

</Context>

Finally, copy activemq-all-5.3.0.jar from your ActiveMQ install folder to apache-tomcat-6.0.16/webapps/graniteds-chat/WEB-INF/lib. This will ensure that JNDI has access to the ActiveMQ classes it needs to communicate to the ActiveMQ JMS broker. Start tomcat and refresh your two browser windows running the chat application. Send a few messages back and forth to ensure everything is working.

Now, we need to prove to ourselves that messages are indeed going through ActiveMQ and not just through GraniteDS. Open a new browser tab to the ActiveMQ monitor page http://localhost:8161/admin/topics.jsp . Make sure that the topic "discussion" exists and has some enqueued and dequeued messages.
active_mq_topics

If you really want to test out the system, create a simple Java program that can send a JMS message to ActiveMQ. Make sure the resulting message ends up in your Flex app.

import javax.jms.Connection;
import javax.jms.Message;
import javax.jms.MessageProducer;
import javax.jms.Session;
import javax.jms.Topic;

import org.apache.activemq.ActiveMQConnectionFactory;

public class ChatTest
{

    /**
     * @param args
     */

    public static void main(String[] args)
    {
        try
        {
            ActiveMQConnectionFactory connectionFactory = new ActiveMQConnectionFactory("tcp://localhost:61616");
            Connection connection = connectionFactory.createTopicConnection();
            Session session = connection.createSession(false, Session.AUTO_ACKNOWLEDGE);
            // create our request and response queues
            Topic topic = session.createTopic("discussion");
            // and attach a producer to the topic
            MessageProducer producer = session.createProducer(topic);
            // and start your engines...
            connection.start();
           
            // now send our test JMS message
            Message message = session.createObjectMessage(new String("Hello from JMS"));
            producer.send(message);
           
            //sleep a bit to make sure our message sent ok
            Thread.sleep(10000);
           
            session.close();
            connection.close();
            System.out.println("Sent message ok");
        }
        catch (Throwable e)
        {
            e.printStackTrace();
        }

    }

}

Post to Twitter

Posted by Andrew, filed under Data, Flex. Date: November 16, 2009, 11:44 am | 6 Comments »