Migrating from BlazeDS to GraniteDS

Ignoring the fact that the BlazeDS project has not been updated for almost 3 years and can probably be considered as dead, there are many reasons why you would want to switch to GraniteDS from BlazeDS, as described in this SlideShare presentation.

The technical reasons can be more or less sorted in 3 main groups :

  1. Benefit from a better messaging scalability by using the asynchronous servlet support of your application server (for example with the APR native library of Tomcat or a Servlet 3 compliant container)
  2. Avoid the famous LazyInitializationException and similar errors when serializing JPA detached objects
  3. Using the advanced data management features

These 3 reasons imply different levels of complexity of the migration. This post is going to show how to implement 1 and 2, this is quite easy and mainly implies a few changes in the configuration. Implementing 3 is a bit more involving and greatly depends on your application architecture, however in general it will allow massive cuts in the usual boilerplate code needed in typical data-intensive applications.

Using Gravity support for asynchronous processing

Starting from a working BlazeDS project, here are the steps that are needed to use GraniteDS :

  • Replace the BlazeDS libraries and their dependencies by granite-server.jar in WEB-INF/lib
  • Configure the GraniteDS servlets in web.xml (and remove the BlazeDS MessageBroker)
  • Configure the messaging destination in WEB-INF/flex/services-config.xml (this file follows the same format as default BlazeDS configuration files) but use the org.granite.gravity.channels.GravityChannel implementation for the messaging channel
  • Add the granite-client-flex.swc library as a compilation dependency for the Flex project using the linking mode -include-libraries
  • Replace all occurences of mx.messaging.Consumer and mx.messaging.Producer with org.granite.gravity.Consumer and org.granite.gravity.Producer

Each of these steps is quite easy, but to help understand what all this means, let’s do it on an existing project. The easiest is to start from an example of the BlazeDS turnkey server that you can find here. As a first example, we’re going to migrate the Collaboration Dashboard sample project to Gravity. You should also download the latest distribution of GraniteDS here.

First start the turnkey server database by executing blazeds-turnkey/sampledb/startdb.bat on Windows or startdb.sh on Linux/OSX, and the built-in Tomcat server by executing blazeds-turnkey/tomcat/bin/startup.bat or startup.sh. You should now have the turnkey server up and running and you can browse the url http://localhost:8400/samples. If you are a BlazeDS user, you most likely already know these examples.

Migrating the Collaboration Dashboard

Then import the project dashboard in Flash Builder by following the instructions that you will find at http://localhost:8400/samples/fb-project-setup.htm.

Now to keep the BlazeDS samples working, make a copy of the tomcat/webapps/samples folder to tomcat/webapps/samplesgds. We can now follow the 5 steps described above :

  1. Remove all BlazeDS libraries from WEB-INF/lib (in fact remove all libs) and replace them by granite-server-{version}.jar (that you can find in the libraries/server folder of the GDS distribution)
  2. Configure the Gravity servlet and listener for Tomcat in WEB-INF/web.xml:
    <web-app>
        <display-name>GraniteDS Samples</display-name>
        <description>GraniteDS Sample Application</description>
    
        <!-- GraniteDS startup/shutdown listener -->
        <listener>
            <listener-class>org.granite.config.GraniteConfigListener</listener-class>
        </listener>
    
        <!-- The Gravity messaging servlet for Tomcat -->
        <servlet>
            <servlet-name>GravityServlet</servlet-name>
            <servlet-class>org.granite.gravity.tomcat.GravityTomcatServlet</servlet-class>
            <load-on-startup>1</load-on-startup>
        </servlet>
        <servlet-mapping>
            <servlet-name>GravityServlet</servlet-name>
            <url-pattern>/gravityamf/*</url-pattern>
        </servlet-mapping>
    
        <welcome-file-list>
            <welcome-file>index.html</welcome-file>
        </welcome-file-list>
    </web-app>
    

    As you can see, it’s very similar to the declarations in BlazeDS, the only differences being the names of the implementations of the servlet/listeners and the fact that the servlet implementation itself depends on the target server. The reason is that it uses the specific CometProcessor API of Tomcat that allows asynchronous processing using Java NIO or the APR native library and is a lot more scalable than standard blocking servlets. If you want to use the newer Tomcat 7 or better, which we highly recommend, use the following standard declaration:

    <servlet>
        <servlet-name>GravityServlet</servlet-name>
        <servlet-class>org.granite.gravity.servlet3.GravityAsyncServlet</servlet-class>
        <load-on-startup>1</load-on-startup>
        <async-supported>true</async-supported>
    </servlet>
    
  3. Configure the destination in WEB-INF/flex/services-config.xml:
    <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="dashboard">
                    <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}/gravityamf/amf"
                    class="flex.messaging.endpoints.AMFEndpoint"/>
            </channel-definition>
        </channels>
    </services-config>
    

    As with web.xml, this is very similar to the BlazeDS services-config.xml or messaging-config.xml, but simpler. The main differences are the implementation of the channel which is GravityChannel and the implementation of the default adapter SimpleAdapter. Also note that we have put everything in one single services-config.xml file because GraniteDS does not support file includes. This is not really an issue as the configuration of GraniteDS is usually very short.

  4. Add granite-client-flex.swc as a compilation dependency in Flash Builder (this swc can be found in the libraries/flex-client folder of the GDS distribution), you should just have to copy the file in the libs folder of the project. It includes the specific implementations of Gravity messaging channels, consumer and producer.
  5. Add the gds namespace xmlns:gds=”org.granite.gravity.*” in the <mx:Application> declaration and replace the occurences of <mx:Producer> and <mx:Consumer> with <gds:Producer> and <gds:Consumer>. Last details, the GDS Consumer does not directly expose the message event in mxml, you will have to add consumer.addEventListener(MessageEvent.MESSAGE, messageHandler) just after consumer.subscribe().
    private function initApp():void {
    	consumer.subscribe();
    	consumer.addEventListener(MessageEvent.MESSAGE, messageHandler);
    }
    ...
    <gds:Producer id="producer" destination="dashboard"/>
    <gds:Consumer id="consumer" destination="dashboard"/>
    

Now you can reconfigure the server for the Flex application by unchecking “Use remote object access service” in Flex Server. Also set the target folder to the turnkey server deployment folder :

Finally restart Tomcat and browse http://localhost:8400/samplesgds/dashboard, you can check that the application works exactly the same. The main difference is that it now uses a lot less resources and can handle many thousands users instead of many hundreds.

Migrating the Trader Desktop

As a next example, we are going to migrate the Trader Desktop example.

Fortunately most of the configuration has already been done for the previous example so we can skip steps 1 and 2. For step 3, we are simply going to add a new destination in services-config.xml for the data feed :

<destination id="market-data-feed">
    <channels>
        <channel ref="my-gravityamf"/>
    </channels>
</destination>

Note that we won’t migrate the 3 different channel types present in the BlazeDS example (my-polling-amf, my-streaming-amf and per-client-qos-polling-amf) because the only channel type provided by GraniteDS is the long-polling Gravity channel which can advantageously replace all of the 3 others.

Then follow steps 4 and 5. Note that the property subtopic of the mx:Consumer is named topic on the Gravity Consumer. Here is the part we have to change:

private function subscribe(symbol:String):void {
    var consumer:Consumer = new Consumer();
    consumer.destination = 'market-data-feed';
    consumer.topic = symbol;
    consumer.channelSet = new ChannelSet([channels.selectedItem]);
    consumer.addEventListener(MessageEvent.MESSAGE, messageHandler);
    consumer.subscribe();
    consumers[symbol] = consumer;
}

We’re close to the end, but note that this example uses the MessageBroker API to send messages from the server. We have to update this server thread to use the Gravity API. To do the changes quickly, we are simply going to create a new Java project (for example named samplesgds or whatever name you like) in Eclipse and use the path blazeds-turnkey-4.0.0.14931\tomcat\webapps\samplesgds as location for the project, so it will find the existing sources and compile them directly in WEB-INF/classes.

Once you do this, there will probably we some compilation errors that are normal because we removed the BlazeDS libs and the existing samples expect these libs, so for now you may just delete the classes that do not compile. We just need the classes in the package flex.samples.marketdata for this example.

The feed provider is the simple class flex.samples.marketdata.Feed. It you look at the source, it uses MessageBroker.getMessageBroker(null) to retrieve the current BlazeDS message broker. It’s a bit different in GraniteDS because the object Gravity which is the equivalent of MessageBroker is not stored as a static instance but in the servlet context. So we have to pass it from the startfeed.jsp and stopfeed.jsp which can easily access the servlet context. Here are the modified Feed which the changes highlighted in blue :

public class Feed {

    private static FeedThread thread;

    private final Gravity gravity;

    public Feed(Gravity gravity) {
        this.gravity = gravity;
    }

    public void start() {
        if (thread == null) {
            thread = new FeedThread(gravity);
            thread.start();
        }
    }

    public void stop() {
        thread.running = false;
        thread = null;
    }

    public static class FeedThread extends Thread {

        private Gravity gravity;

        public FeedThread(Gravity gravity) {
            this.gravity = gravity;
        }

        public boolean running = true;

        private Random random;

        public void run() {
            Portfolio portfolio = new Portfolio();
            List stocks = portfolio.getStocks();
            int size = stocks.size();
            int index = 0;

            random = new Random();

            Stock stock;

            while (running) {

                stock = (Stock) stocks.get(index);
                simulateChange(stock);

                index++;
                if (index >= size) {
                    index = 0;
                }

                AsyncMessage msg = new AsyncMessage();
                msg.setDestination("market-data-feed");
                msg.setHeader(AsyncMessage.SUBTOPIC_HEADER, stock.getSymbol());
                msg.setTimestamp(System.currentTimeMillis());
                msg.setBody(stock);
                gravity.publishMessage(msg);

                try {
                    Thread.sleep(20);
                } catch (InterruptedException e) {
                }
            }
        }

        private void simulateChange(Stock stock) {
            ...
        }
    }
}

Then the changes in the jsp files:

<%@page import="flex.samples.marketdata.Feed"%>
<%@page import="org.granite.gravity.*" %>
<%
	try {
		Feed feed = new Feed(GravityManager.getGravity(application));
		feed.start();
		out.println("Feed Started");
	} catch (Exception e) {
		out.println("A problem occured while starting the feed: " + e.getMessage());
	}
%>

You can see that the API is once again very similar to BlazeDS with only the slight change of using ServletContext. Note that when using the Spring integration for example, the Gravity object is a managed bean and can be directly injected in any Spring bean without having to access the ServletContext.

Now restart Tomcat and you should be able to start/stop the data feed and to access the example at?http://localhost:8400/samplesgds/traderdesktop/traderdesktop.html.

Using Remoting

The BlazeDS turnkey does not include any advanced example using Hibernate or similar so we cannot easily demonstrate how to migrate this kind of applications. However we can simply show how to migrate a simple application using remoting, the Inventory example.

Switching from BlazeDS remoting to GraniteDS remoting is by far the simplest of the 3 examples we wanted to demonstrate as there is stricly no change required in the source code, neither Flex nor Java.

What we have to do is simply add the remoting filter and servlet in web.xml (provided you have already followed the previous examples) :

<filter>
	<filter-name>AMFMessageFilter</filter-name>
	<filter-class>org.granite.messaging.webapp.AMFMessageFilter</filter-class>
</filter>
<filter-mapping>
	<filter-name>AMFMessageFilter</filter-name>
	<url-pattern>/graniteamf/*</url-pattern>
</filter-mapping>
<servlet>
	<servlet-name>AMFMessageServlet</servlet-name>
	<servlet-class>org.granite.messaging.webapp.AMFMessageServlet</servlet-class>
	<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
	<servlet-name>AMFMessageServlet</servlet-name>
	<url-pattern>/graniteamf/*</url-pattern>
</servlet-mapping>

and configure the remoting destination and channel in services-config.xml:

<service id="granite-service" messageTypes="flex.messaging.messages.RemotingMessage">
     <destination id="product">
        <channels>
            <channel ref="my-graniteamf"/>
        </channels>
        <properties>
             <source>flex.samples.product.ProductService</source>
        </properties>
    </destination>
</service>
<channels>
    <channel-definition id="my-graniteamf">
        <endpoint uri="http://{server.name}:{server.port}/{context.root}/graniteamf/amf"/>
    </channel-definition>
</channels>

That’s all, the example should work exactly the same with just this configuration change. Once you are here, you can then try the other features or GraniteDS such as automatic destination scanning which avoids the need to define destinations manually in the XML configuration, or externalizers which make possible to properly serialize Java enum, BigInteger and others.

Conclusion

If you are used to RemoteObject and Consumer/Producer, you should be able to use GraniteDS in no time, and benefit from more advanced features incrementally.

In a future document, we will try to demonstrate how to migrate a more complex BlazeDS/Spring/Hibernate application to GraniteDS. If you have an idea of an existing project (with public source code) that could be used as a starting point for the article, don’t hesitate to give us your suggestions.

Author: Franck

This post has 2 Comments

  1. Edgar Rivera on April 6, 2014 at 6:14 pm Reply

    This is quite interesting article, but reality is many projects are using SpringBlaze integration, rather than just simple remote objects, so will be more helpful to have an example about how to migrate from spring-flex to spring-graniteds and keep using spring-security, spring-core, etc.

    Thanks, good job !!!

  2. Franck on April 30, 2014 at 8:35 am Reply

    You can find a comprehensive documentation on Spring / GraniteDS here: http://www.granitedataservices.com/public/docs/latest/docs/reference/flex/graniteds-refguide-flex.html#graniteds.spring.

    There is also a tutorial (though a bit outdated) on Spring / GraniteDS on DZone here: http://java.dzone.com/articles/enterprise-ria-spring-3-flex-4.

    But you’re definitely right, we need to write a up-to-date migration guide from spring-blazeds to spring-graniteds.

Leave a Comment