Categories

Archives

WebORB and Grails Integration

We first announced Grails integration back when we released WebORB for Java 4.1. Today we have uploaded a new set of WebORB for Java Docs, which include the documentation describing the integration between WebORB for Java and Grails. The docs also include a detailed tutorial providing step-by-step instructions for creating a Grails application, deploying WebORB and developing a sample service. The tutorial is available at: http://www.themidnightcoders.com/fileadmin/docs/java/v4/grails_example_application.htm

Finally, the Grails integration has been reviewed in a webinar we conducted back in March. The video where the Grails integration is reviewed is linked below, make sure to fast forward to 5m:30sec into the video:

WebORB for Java v.4.0 is Here!

The wait is finally over! You can now download WebORB for Java 4.0.  Just as we did with WebORB for .NET 4.0, we’ve added many new developer productivity tools and feature enhancements to ease the process of developing and integrating online applications.  We’ve also fixed a number of reported bugs and are now working to update the documentation.  We’re very excited for you to give the new release a try and let us know what you think.  Some of the major new and enhanced features include:

  • New Management Console – now WebORB for Java has a new look and feel making it easier to navigate for repeatable processes.  The console also provides full server configuration via the graphical configuration panels.  This includes configuration for logging categories, service browsers, code generators, response compression, configuration hot deployment, data type mappings, security settings and more.
  • Pluggable Code Generation – WebORB provides a very powerful code generation framework that can generate any type of code or documentation based on the deployed Java classes.  Starting with WebORB 4, developers can add their own custom code generators that leverage all the features of the framework.
  • New Remoting Code Generators – added support for Swiz, Mate, Web Services, and Spring Beans.
  • Code Generation Enhancements – improves testing and delivery of better quality code
    • Added DataTypeInitializer to all code generators
    • Added HTML wrapper for all Flex-based code generation projects
    • Added support for Java enums
  • WebORB Data Management for Flex (WDMF) Enhancements
    • Added automatic compile for WDMF code generation
    • Added support for UnitofWork API for better handling of database transactions
  • Support for Messaging Sub-Topics

You can Download the latest release and then check back as we add documentation.  Feel free to comment or ask questions in the Developer Forum or visit our JIRA Bug Tracking System to watch or participate in our WebORB for Java development.

CURRENT ENTERPRISE CUSTOMERS: Please contact your account manager to receive a new v.4.0 license key since this is a major point release.

RTMP Data Push from Java to Flex

Data push is a mechanism of data delivery from server to the connected client applications. Data push between Java and Flex can be accomplished via HTTP polling or using a dedicated connection from the client program to the server-side process. The dedicated connection can use the RTMP protocol supported by Flash and Flex and also implemented by WebORB for Java. The example reviewed in this post uses the latter approach. The example establishes a connection from a Flex client to the WebORB for Java server process. The server-side code schedules a timer task to push a Java object to all connected clients. When the client program receives the object sent by the server it updates the user interface with the received information.

The example’s server-side code runs inside of the WebORB server. For the RTMP applications, WebORB requires a Java class which is the main handler of all events and actions for the given messaging application. Consider the following server-side Java code. The code is the handler class for the example:

package samples.datapush;

import org.red5.server.api.IConnection;
import org.red5.server.api.IScope;
import org.red5.server.api.service.IServiceCapableConnection;
import weborb.messaging.WebORBApplication;

import java.util.*;

public class AppHandler extends WebORBApplication
  {
  private Timer pushTimer;

  public boolean appStart( IScope app )
    {
    boolean started = super.appStart( app );

    if( started )
      {
      pushTimer = new Timer( true );
      pushTimer.scheduleAtFixedRate( new DataPushTask(), 0, 1000 );
      }

    return started;
    }

  protected void pushDataToClients( StockQuote quote )
    {
    Object[] args = new Object[] { quote };
    Iterator<IConnection> connectionsIterator = this.scope.getConnections();

    while( connectionsIterator.hasNext() )
      {
      IConnection connection = connectionsIterator.next();

      if( connection instanceof IServiceCapableConnection )
        ((IServiceCapableConnection) connection).invoke( "quotePushed", args );
      }
    }

  class DataPushTask extends TimerTask
    {
    @Override
    public final void run()
      {
      Random rand = new Random( System.currentTimeMillis() );
      StockQuote quote = new StockQuote();
      quote.bid = rand.nextDouble() * 100;
      quote.ask = quote.bid + 1;
      quote.last = quote.ask - 0.5;
      quote.symbol = "MSFT";
      quote.transactionDate = new Date();
      pushDataToClients( quote );
      }
    }
  }
public class StockQuote
  {
  public double bid;
  public double ask;
  public double last;
  public String symbol;
  public Date transactionDate;
  }

The class shown above extends from weborb.messaging.WebORBApplication, so it can plug in into the product’s messaging framework. The appStart method is invoked when the WebORB server process is started (or when the application server is launched if WebORB is deployed into a 3rd party server). The implementation of the appStart method creates a timer which executes DataPushTask every second. The task creates an instance of the StockQuote class and delivers it to all connected clients by calling the pushDataToClients( StockQuote ) method. The actual data push occurs in the pushDataToClients method. The code obtains a collection of the client connections and iterates over it. For each connection the code invokes the “invoke” method. The method arguments are the name of the client-side function to invoke and the argument list to send to the client.

There are a few steps to deploy the Java code:

  1. Compile the code and copy the .class files into the corresponding folders under WEB-INF/classes. Specifically, the class files for the code shown above must be copied to the WEB-INF/classes/samples/datapush directory.
  2. Create and save the messaging application deployment file in WEB-INF/classes. Save the file with the name of datapushexample-web.xml. The file must contain the following:
    <?xml version="1.0" encoding="UTF-8"?>
    <!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
    <beans>
    
        <bean id="datapushexample.context" class="org.red5.server.Context"
            autowire="byType" />
    
        <bean id="datapushexample.scope" class="org.red5.server.WebScope"
             init-method="register">
            <property name="server" ref="red5.server" />
            <property name="parent" ref="global.scope" />
            <property name="context" ref="datapushexample.context" />
            <property name="handler" ref="datapushexample.handler" />
            <property name="contextPath" value="/SampleDataPush" />
            <property name="virtualHosts" value="*,localhost, localhost:1935, localhost:8080, 127.0.0.1:8080" />
        </bean>
    
        <bean id="datapushexample.handler"
            class="samples.datapush.AppHandler"
            singleton="true" />
    </beans>

    Notice the “datapushexample.” prefix in various IDs throughout the document. The prefix must be unique between all deployed messaging applications. Additionally, the bean definition with the “datapushexample.handler” ID references the handler class reviewed above. Finally, the “contextPath” property specifies the path of the application which will be used in the client code.

  3. Update beanRefContext.xml located in WEB-INF/classes, so it references datapushexample-web.xml from item #2.
  4. Restart WebORB for Java. If you are using the standalone version of the product, run “java -jar weborb.jar” from the command prompt (make sure to set the current directory to the WebORB’s installation directory).

At this point you are done with the server-side, so we’re shifting to Flex. Run Flex/Flash Builder and create a new Flex application project. See the article describing how to create a Flex/Flash Builder project to work with WebORB. The code below is the complete Flex application which connects to the server and processes data push messages:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" creationComplete="init()">
	<mx:Script>
		<![CDATA[
		import mx.collections.ArrayCollection;
		import mx.controls.Alert;

		private var serverConnection:NetConnection;

		[Bindable]
		private var historicalQuotes:ArrayCollection = new ArrayCollection();

		public function init():void
		{
			// create new NetConnection
			serverConnection = new NetConnection();

			// set the object which will receive the callbacks from server
			serverConnection.client = this;

			// set the encoding to AMF0 - looks like RTMP requires that
	    	        serverConnection.objectEncoding = ObjectEncoding.AMF0;

	    	        // register the listener so we know if the connection has succeeded
	    	        serverConnection.addEventListener( NetStatusEvent.NET_STATUS, handleNetStatus );

	    	        // now connect to the server-side app
	    	        serverConnection.connect( "rtmp://localhost:1935/SampleDataPush" );

	    	        // initialize historical quotes to zeros
	    	        for( var i:int = 0; i < 50; i++ )
	    		  historicalQuotes.addItem( 0 );
		}

		private function handleNetStatus( event:NetStatusEvent ):void
		{
			switch( event.info.code )
			{
				case "NetConnection.Connect.Success":
					// all is good, the connection is made
					break;
				case "NetConnection.Connect.Failed":
					Alert.show( "Unable to connect to WebORB", "Connection Error" );
					break;
			}
		}

		// this method is invoked by the server-side code through WebORB
		public function quotePushed( quote:StockQuote ):void
		{
			symbolTextField.text = quote.symbol;
			bidTextField.text = usdFormatter.format( quote.bid );
			askTextField.text = usdFormatter.format( quote.ask );
			lastPriceTextField.text = usdFormatter.format( quote.last );
			lastTimestampTextField.text = quote.transactionDate.toTimeString();
			historicalQuotes.removeItemAt( 0 );
			historicalQuotes.addItem( quote.last );
		}

		]]>
	</mx:Script>
	<mx:Panel x="10" y="10" width="357" height="269" layout="absolute" title="Data Push Example">
		<mx:Label x="83" y="10" text="Stock symbol:"/>
		<mx:Text x="175" y="10" fontWeight="bold" id="symbolTextField"/>
		<mx:Label x="110" y="36" text="Bid price:"/>
		<mx:Label x="107" y="62" text="Ask price:"/>
		<mx:Label x="104" y="88" text="Last price:"/>
		<mx:Label x="10" y="114" text="Last transaction date/time:"/>
		<mx:Text x="175" y="36" id="bidTextField"/>
		<mx:Text x="175" y="62" id="askTextField"/>
		<mx:Text x="175" y="88" id="lastPriceTextField"/>
		<mx:Text x="175" y="114" id="lastTimestampTextField"/>
		<mx:LineChart x="10" y="140" id="stockPriceChart" width="317" height="79" dataProvider="{historicalQuotes}">
			<mx:series>
				<mx:LineSeries displayName="Series 1" yField=""/>
			</mx:series>
		</mx:LineChart>
	</mx:Panel>
 	<mx:CurrencyFormatter id="usdFormatter" precision="2"
        currencySymbol="$" decimalSeparatorFrom="."
        decimalSeparatorTo="." useNegativeSign="true"
        useThousandsSeparator="true" alignSymbol="left"/>
</mx:Application>

Notice the quotePushed function. The function is invoked by the server and that is where the data push takes place. The StockQuote class referenced in the code is below:

package
{
	[RemoteClass(alias="samples.datapush.StockQuote")]
	public class StockQuote
	{
		public var bid:Number;
		public var ask:Number;
		public var last:Number;
		public var symbol:String;
		public var transactionDate:Date;
	}
}

Below is a screenshot of the application in action: