Jan 24 2008

Integrating Flex, BlazeDS, and Scala/Lift

Tag: AMF, Flex, Remoting, Server Push, blazeds, lift, scala, tutorialDerek Wischusen @ 6:00 am

A short while ago Adobe announced that they would be releasing an open source version of LiveCycle Data Services called BlazeDS. In short, BlazeDS is Java technology that makes it really easy to do HTTP streaming and server-side push to Flex and AIR applications. While this announcement is definitely welcome news, there may be a lot of people out there who thought to themselves, “That’s great, but I don’t use Java.” Well, the good news is that just because BlazeDS uses Java the platform doesn’t mean that you necessarily need to use Java the language to take advantage of it.

In fact, Java the platform supports several different languages, including Groovy, Python, Ruby, and Scala. And, of course, ColdFusion. In theory, it should be pretty straight-forward to integrate any of these languages with BlazeDS.

For the rest of this post I am going to focus on just one of them, Scala. Why Scala? Well, it is because recently I decided that I wanted to broaden my programming language horizons and I’ve found that Scala is both extremely interesting and very powerful. It’s both object-oriented and functional, it has mixin style composition, it has an exceptional implementation of Erlang style Actors for handling concurrency, and much more. Lastly, and perhaps most important for my purposes, there is an excellent webapp framework for Scala called Lift.

All right, let’s get started with integrating Flex, BlazeDS, and Scala/Lift.

Prerequisites – latest versions of:

—–

If you’d like to follow along with source files, here they are.

Source Code:

—–

First, let’s create a Lift project named LiftBlaze:

1. Open a terminal and navigate to the directory where you want to create the project.

2. Enter the following command:

mvn archetype:create -U                                    \
-DarchetypeGroupId=net.liftweb                             \
-DarchetypeArtifactId=lift-archetype-blank                 \
-DarchetypeVersion=0.4                                     \
-DremoteRepositories=http://scala-tools.org/repo-releases  \
-DgroupId=org.rxr.liftblaze -DartifactId=liftblaze

3. Next enter:

cd liftblaze

4. Lastly, enter:

mvn jetty:run

If everything worked properly, then you should see a “Hello World” welcome page at http://localhost:8080/

—–

Next, let’s use Maven to create an eclipse project for us:

1. In a terminal, go to lift linkblaze project and enter:

mvn eclipse:eclipse

You should now be able to import the linkblaze project into Eclipse. Once you have the project imported you will need to add the BlazeDS jars to your build path. These jars are located in {blazeds_folder}\tomcat\webapps\blazeds\WEB-INF\lib.

—–

Now let’s start setting up the Lift project to work with Flex and BlazeDS.

1. Open the web.xml file located in {blazeds_folder}\tomcat\webapps\blazeds\WEB-INF

2. Open the web.xml file located in liftblaze\src\main\webapp\WEB-INF.

3. Copy the contents of the web.xml file from the BlazeDS folder into the web.xml file in the liftblaze Lift project.

At this point, it would be useful to talk a bit about how lift handles HTTP requests. Lift is a view first framework. What this means is that by default lift will try to match an HTTP request against one its views. If it finds one it will do some processing on it, and then render it our as HTML. For more info on how Lift handles HTTP requests, check out this page.

For this project, the entire view is going to be in Flex, so we don’t really Lift want to concerned about the view we just want it to handle our web service calls. To achieve this do the following:

4. Inside you newly merged web.xml file, modify the filter-mapping tag like so:

<filter-mapping>
  <filter-name>LiftFilter</filter-name>
  <url-pattern>/webservices/*</url-pattern>
</filter-mapping>

Now Lift will only handle requests that start with webservices. In a later step we will tell Lift how to route these calls so that they invoke methods on a class that we will define.
5. Copy {blazeds_folder}\tomcat\webapps\blazeds\WEB-INF\flex into liftblaze\src\main\webapp\WEB-INF

6. Inside the flex folder, open the messaging-config.xml file and add the following:

 <destination id="notifications">
     <channels>
         <channel ref="my-streaming-amf"/>
     </channels>
 </destination>

This is the messaging destination that our Lift app will publish to and that our Flex app will subscribe to.

—–

All right, let’s start adding some Scala code to our lift project.

1. In Eclipse, create a package called org.rxr.liftblaze.actor

2. In this package, create a Scala Class called Notifier and add the following code to it:

package org.rxr.liftblaze.actor;

import flex.messaging.MessageBroker
import flex.messaging.messages.AsyncMessage
import flex.messaging.util.UUIDUtils
import scala.actors._
import scala.actors.Actor._
import compat.Platform._
import net.liftweb.util._
import org.rxr.liftblaze.model.Notification

class Notifier extends Actor {

  val msgBroker = MessageBroker.getMessageBroker(null)
  val clientID = UUIDUtils.createUUID()
  val msg = new AsyncMessage() 

  var notificationsSent = 0;

  def act{

    ActorPing.schedule(this, Notify, 500L)

    loop {
      react {

        case Notify =>
          msg.setDestination("notifications")
          msg.setClientId(clientID)
          msg.setMessageId(UUIDUtils.createUUID())
          msg.setTimestamp(currentTime)
          msg.setBody(new Notification(notificationsSent, "Hello from Scala/Lift", new java.util.Date()))
          msgBroker.routeMessageToService(msg, null)
          notificationsSent+=1
          println("notification sent at " + currentTime)
          ActorPing.schedule(this, Notify, 500L)       

        case "stop" =>
          exit()
      }
    }
  }

}

case object Notify

This is class that does the majority of the work in this simple application. The first thing to note about it is that it is a Scala Actor. I don’t want to go into too much detail about Actors here. For this tutorial we can grossly oversimplify and say that an Actor is kind of like a thread that can send and receive messages. If you want to get the full story you can click on the preceding link.

Here is a very brief explanation of what is happening in this class. When the Notifier Actor is started (you’ll see where this happens later) the act method is called. The first thing inside the act method is a call to the ActorPing.schedule helper method. This method basically says that I want to send a Notify message to this Actor in 500 milliseconds. Next comes loop which simply creates a loop and then react which has case statements that are used to handle incoming messages. So, when react receives a Notify or a “stop” message all of the code for that case is executed.

When a Notify is received a message is published to the “notifications” destination that we created earlier in the message-config.xml file. Once the message is sent, there is another call to ActorPing.schedule which will send another Notify message in 500 milliseconds. So, until a “stop” method is received, the Notifier Actor will publish a notification every 500 milliseconds.

The body of the message that is being published is a custom Scala class called Notification. One of the benefits of BlazeDS is that you can use the AMF protocol to send strongly typed objects from the server to your Flex client. So I can have BlazeDS serialize my Notification class in Lift down into AMF binary and then have it reappear as a Notification class in Flex, which is pretty handy. Though I have found that there is one small trick to creating classes in Scala that will serialize properly. To illustrate, let’s create the Notification class next.

3. Create a Scala Class named Notification in 0rg.rxr.liftblaze.model and add the following code,

package org.rxr.liftblaze.model

import java.util.Date

class Notification(var id: Int, var message: String, var timesent: Date)
{
    def getId = id
    def setId(id: Int) = this.id = id

    def getMessage = message
    def setMessage(m: String) = message = m

    def getTimesent = timesent
    def setTimesent(t: Date) = timesent = t
}

The main thing to take note of here is that I am using methods that are named like Java (the language) getters and setters, rather than variables. This is because the Scala compiler takes variables defined in Scala and turns them into methods in Java. So,

val greeting = "Hello"

in Scala becomes

public String greeting()
{
  return "Hello"
}

in Java. The same goes for var (FYI, vals are immutable variables, while vars are mutable). So, member variables in Scala won’t serialized by BlazeDS because they turn into methods in Java. The solution, as I have already shown, is just to use methods in Scala that are named like Java getters and setters.

Now that we’ve seen who is sending the messages in Lift (the Notifier Actor) and what is being sent (the Notification class), let’s create the class that starts and stops the Notifier.

4. In org.rxr.liftblaze.controller create a Scala Class called WebServices and then add the following code

package org.rxr.liftblaze.controller

import net.liftweb.http._
import javax.servlet.http.{HttpServlet, HttpServletRequest , HttpServletResponse, HttpSession}
import org.rxr.liftblaze.actor.Notifier

class WebServices (val request: RequestState,val httpRequest: HttpServletRequest) extends SimpleController {

  def start_feed = {
    WebServices.notifier.start()
    XmlResponse(<start_feed success="true"/>)
  }

  def stop_feed = {
    WebServices.notifier ! "stop"
    XmlResponse(<stop_feed success="true"/>)
  }
}

object WebServices {
  val notifier = new Notifier()
}

This is class is pretty much self explanatory. The start_feed method calls start on our Notifier Actor, which as we discussed causes the Notifier to send out a Notification every 500 milliseconds. The stop_feed method sends a “stop” message to the Notifier, which causes it to exit. Both methods return a simple XML response.

We’re almost done with the Lift app, just one more thing to do. In a previous step we modified the web.xml file so that Lift would only handle requests that start with webservices. Now we just need to tell Lift how to process/route those request so that they call the method in our WebServices class. To do this we need to modify the Boot class.

5. Open the Boot class located in bootstrap.liftweb and modify it thusly

package bootstrap.liftweb

import net.liftweb.util._
import net.liftweb.http._
import Helpers._
import org.rxr.liftblaze.controller.WebServices
import javax.servlet.http.{HttpServlet, HttpServletRequest , HttpServletResponse, HttpSession}

/**
  * A class that's instantiated early and run.  It allows the application
  * to modify lift's environment
  */
class Boot {
  def boot {

    val dispatcher: LiftServlet.DispatchPf = {
     // if it's a web service, pass it to the web services invoker
     case RequestMatcher(r, ParsePath("webservices" :: c :: _, _,_),_, _) => invokeWebService(r, c)
    }
    LiftServlet.addDispatchBefore(dispatcher)

  }

  private def invokeWebService(request: RequestState, methodName: String)(req: HttpServletRequest): Can[ResponseIt] =
    createInvoker(methodName, new WebServices(request, req)).flatMap(_() match {
    case Full(ret: ResponseIt) => Full(ret)
    case _ => Empty
  })
}

This code is taken directly from the examples included with the Lift source code. In Basic terms, this code matches a request that starts with webservices and passes the request and method name to be invoked to the invokeWebService method. This method the calls createInvoker which invokes the method on our WebServices class. So, for example http://localhost:8080/webservices/start_feed would call the start_feed method on the WebServices class. You can read more about how this code works here.

That’s it for the Lift App. All in all there really isn’t that much code. Though, of course, this app is pretty simple. Regardless of how simple this app is, I would say that in the short time that I have been using Scala I have found it to be the most concise statically typed language that I have ever used.

—–

Now let’s create the Flex App in Flex Builder.

1. Create a new Flex Project (File -> New -> Flex Project)

2. Fill in the rest as follows:

Project name: LiftBlaze

Application type: Web Application

Application server type: J2EE

Use remote object access service: check this

Root folder: {liftblaze_lift_app}\src\main\webapp

Root URL: http://localhost:8080/

Context root: /

Output folder: {liftblaze_lift_app}\src\main\webapp

Output folder URL: http://localhost:8080/

3. Once you have everything filled out, click Finish

All right, now that we have the project created let’s add some code to it.

4. Open LiftBlaze.mxml and add the following code:

<?xml version="1.0" encoding="utf-8"?>
<mx:Application xmlns:mx="http://www.adobe.com/2006/mxml" layout="vertical" >
    <mx:Script>
        <![CDATA[
        	import org.rxr.liftblaze.model.Notification;
          	import mx.messaging.events.MessageEvent;

            private function messageHandler(message: MessageEvent): void
            {
            	var notification : Notification = message.message.body as Notification;
                pushedValue.htmlText = "<b>id:</b> " + notification.id + " <b>Date:</b> " + notification.timesent;
            }    

        ]]>
    </mx:Script>

    <mx:Consumer
    	id="consumer"
    	destination="notifications"
    	message="messageHandler(event)"
    	channelConnect="trace(event)"
        channelFault="trace(event)"
        fault="trace(event)" />

    <mx:HTTPService
    	id="startFeed"
    	url="webservices/start_feed"
    	result="consumer.subscribe()" />
    <mx:HTTPService
    	id="stopFeed"
    	url="webservices/stop_feed"
    	result="consumer.unsubscribe()" />

 	<mx:Button
 		label="Subscribe to 'notifications'"
 		click="startFeed.send({stop_cache: new Date().getMilliseconds()})"
 		enabled="{!consumer.subscribed}"/>

	<mx:Button
		label="Unsubscribe from 'notifications'"
		click="stopFeed.send({stop_cache: new Date().getMilliseconds()})"
		enabled="{consumer.subscribed}"/>

    <mx:TextInput id="pushedValue" width="300"/>
</mx:Application>

What we’ve create here is an app that has a very simple UI that consists of two buttons and a text input field. The app also has two HTTPServices and Consumer. A couple of things to take note of here. First, the Consumer subscribes to the “notifications” destination, the same destination that our Notifier Actor publishes its messages to. Second, the HTTPServices make calls to webservices/start_feed and webservices/stop_feed. If you’ll recall we set up our Lift app so that these calls would be routed to invoke the methods in our WebServices class.

We just need to add one last piece of code to get everything working properly. In our Lift app the Notifier Actor sends out instances of the Notification class that we created. Now we need to create a corresponding Notification class in our Flex app so that when we receive the data in Flex it will show up as strongly typed class.

5. Create a Class named Notification in org.rxr.liftblaze.model and add the following code

 package org.rxr.liftblaze.model
{
    [Bindable]
    [RemoteClass(alias="org.rxr.liftblaze.model.Notification")]
    public class Notification
    {
        public var id : int;
        public var timesent : Date;
        public var message: String;

        public function Notification()
        {
            super();
        }

    }
}

You’ll notice that this class has the same attributes as the Scala Notification class in our Lift app. The key to mapping this class to our Scala class is the RemoteClass metadata tag located just above the class definition. This tag is used to tell Flex the fully qualified name of the server side class that this class maps to.

—–

That should be it. You now be able to boot up you Lift app, launch the Flex app, click the “Subscribe to ‘notifications’” to start the Notifier Actor and subscribe to the Consumer to the notifications destination. You should then see id number and the time in the text input field get automatically updated every 0.5 seconds. You can the click the “Unsubscribe from ‘notifications’” to stop the Notifier actor and the Consumer to unsubscribe from the notifications destination.

Pretty exciting. With these three technologies it’s really easy to automatically push data from the server to the client in real time. This is obviously a trivial example, but I think it should be relatively straight forward to scale this approach up for more sophisticated apps.

That wraps this up. Please let me know if you have any questions.


Jul 06 2007

RubyAMF 1.0 Released – Now with Rails Remoting

Tag: AMF, Rails, Remoting, RubyAMFDerek Wischusen @ 3:49 am

Aaron Smith just released version 1.0 of RubyAMF – An open-source (MIT) remoting application for Ruby and Rails. This version is stable and it has really nice remoting implementation for Rails that makes it possible to use AMF right inside your controllers (i.e., no need for service classes). You can read all about it here. Aaron has also posted a great screencast tutorial on getting started with RubyAMF on Rails.

Keep up the good work Aaron!


Generic levitra from india
Tramadol 100 mg
Cheap viagra generic
Viagra overnight shipping
Buy tramadol online canada
Cheap viagra canada
Cialis canadian pharmacy
Order xanax online uk
Side effects of viagra
Valium buy online uk
Generic 2mg xanax
Non prescription viagra
Medication phentermine
Cialis low cost
Buy viagra online in south africa
Buy cialis in australia
Canada cialis online
Where to buy levitra online
Cheap viagra uk
Buying propecia
Purchase valium without prescription
Brand name cialis online
Fast delivery cialis
Buy phentermine online without prescription
Propecia online
Cialis professional online
Female viagra uk
Buying cialis online
Online cialis generic
Valium online reviews
Levitra 20 mg price
Propecia without a prescription
Viagra 50 mg price
Cheap valium online
Generic viagra for sale
Prednisone cheap
Genuine cialis no prescription
Buy tramadol cod delivery
About phentermine 37.5 mg
Where to buy phentermine
Free viagra samples uk
Phentermine purchase online
Cheapest generic viagra online
Buy viagra 100mg online
Order valium cheap
Buy valium uk
Generic cialis prices
Canadian pharmacy phentermine
Cost of prednisone
Buy phentermine with mastercard
Tramadol overnight no prescription
Cheap viagra cialis
Best price on cialis
Order cialis online
Tramadol cod fedex
Canadian cialis no prescription
Buying phentermine in the uk
Buy phentermine online with no prescription
Buy viagra professional
Buy cheap cialis online
Viagra prescription free
Purchase xanax online without prescription
Phentermine diet pills
Phentermine usa
Cheapest price viagra
Canadian pharmacy phentermine 37.5
Levitra generic online
Phentermine no prescription needed
How to buy phentermine without a prescription
Cheap viagra online uk
Doses of prednisone
Where can i buy valium in the uk
Purchase viagra without prescription
Viagra online overnight
Cialis 20 mg tablets
Cialis for sale in canada
Order viagra online canada
Cialis pricing
Overnight xanax without prescription
Tramadol hcl 50 mg tab mylan
Phentermine 37.5 manufacturer
Generic tramadol medication
About xanax bars
Propecia without prescription uk
Cialis drug prices
Phentermine pills
Buying viagra in the uk
Cialis india pharmacy
Phentermine tablets 37.5
Phentermine 37.5 without prescription fedex
Low price phentermine
Buy real viagra
Free viagra samples without prescription
Cialis generic 5mg
Buying xanax without a prescription
Buy xanax bars online
Buy viagra online from canada
Viagra usa
Best way to take valium
Buy phentermine hcl
Viagra mastercard
Best viagra generic
Order propecia without prescription
Order cialis online without prescription
Buy phentermine in uk
Levitra cheap
Ordering phentermine online
Cialis 100mg pills
Cheap phentermine no rx
Viagra discount coupons
Xanax generic 2mg
Canadian pharmacy viagra online
Buy cheap tramadol without prescription
Buy propecia uk
Xanax no prescription needed
Prescription valium
Valium purchase
Cheap viagra pills
Cialis from india
20 mg valium effects
Where to buy phentermine without a prescription
Generic valium 5mg
Generic cialis online pharmacy
Cheap generic xanax
Prescription for cialis
Buy phentermine no prescription
Non prescription cialis
Order propecia online no prescription
Phentermine hcl without prescription
How to buy viagra in australia
Valium prescription uk
Order xanax cheap
Valium pills effects
Where can i buy phentermine online
Xanax 1mg dosage
Viagra online sales
Buy cheap viagra uk
Tramadol 50mg tablets
Best place to buy viagra uk
Valium buy no prescription
Order xanax
No prescription valium
Best price for phentermine
About phentermine
Generic phentermine 37.5 without prescription
Buy cheap propecia
Canada viagra online without prescription
Cialis online canada
Buy xanax cod
Buy viagra generic
Levitra lowest price
Viagra for sale usa
Cheapest viagra in uk
Purchase phentermine without a prescription
Xanax pills online
Phentermine no rx
Valium online without prescription
Cheapest viagra professional
Valium without rx
Best generic viagra prices
Valium prescription drug
50 mg tramadol hcl
Cheap viagra in india
Phentermine us pharmacy
Tramadol without a prescription
Cialis 100
Levitra online pharmacy
Cheap phentermine without a prescription
Prednisone pharmacy
Buying prednisone
Online cialis reviews
Cialis online without prescription
Cialis brand
Buy generic valium online
Generic cialis tadalafil
Cialis medicine
Where can i buy generic viagra
Australia viagra prescription
Tramadol pharmacy online
Cialis no rx
Order tramadol online no prescription
Where to buy valium
Cialis over the counter in canada
Xanax no prescription required
Cheap viagra and cialis
Valium overnight delivery
Valium online no prescription

Generic viagra in the uk
Viagra online pharmacy canada
Overnight delivery of phentermine
Xanax drug generic
Genuine viagra
Cialis pills
How to buy viagra in uk
Generic valium
Purchase phentermine online
Prednisone online without prescription
Buy phentermine hcl without prescription
Prednisone online pharmacy
Cialis order
Where can i buy levitra
Phentermine without prescription mastercard
How to buy viagra over the counter
Order valium without a prescription
50 mg viagra online
Tramadol overnight fedex
Viagra discount prices
Buy discount viagra online
Buy generic viagra india
Viagra online with prescription
Prednisone prescription
Viagra to buy in uk
Viagra free samples
Phentermine 37.5 without prescription free shipping
Phentermine hcl 37.5mg
Generic viagra professional
Cheap levitra uk
10 mg valium effects
Buy prednisone no prescription
Viagra prescription australia
Antidepressant tramadol
Mail order phentermine
Propecia generic online
How to buy viagra online without prescription
Phentermine 37.5mg tablets
Valium online
Best generic viagra review
Viagra cheap prices
Xanax cost
Cialis 5mg tablets
Medication prednisone
Pharmacy valium
Viagra fast shipping
Order phentermine without prescription
Viagra pfizer price
Get phentermine without prescription
100mg tramadol effects
Online viagra cheap
Buy discount cialis
Buy 30mg phentermine
Valium no prescription uk
Tramadol no prescription next day
Viagra online generic
Valium without prescription australia
Order levitra without prescription
Buy xanax bars no prescription
Cialis generic online
Buy viagra las vegas
Viagra trial pack