Using AMF and Web Services

You can pack and send your data using AMF even if you don’t use the remoting services. In this post I will show several ways to do that using HTTP services. Why would you want to do that ? There are organizations or departments that expose only REST and SOAP services and it can be hard to persuade them to add the remoting services. In this case, when using web services, you have two solutions: you can create XML from your object graph (and for that you’ll have to write extra code) and apply a compression algorithm on it, or you can apply AMF serialization on the object graph, add the resulting byte code in the service body, and deserialize the AMF format on the client side.

Below is a sample how to do that. First, the Java code on the server:

    public void service(ServletRequest arg0, ServletResponse response)
            throws ServletException, IOException {

        Product product = new Product("name","description");        

        SerializationContext context = new SerializationContext();
        context.instantiateTypes = true;

        ByteArrayOutputStream baos = new ByteArrayOutputStream(64*1024);
        Amf3Output amf3Output = new Amf3Output(context);
        amf3Output.setOutputStream(baos);

        amf3Output.writeObject(product);
        amf3Output.flush();

        response.getOutputStream().write(baos.toByteArray());
    }

On the client you have two options. The easier one is to use an URLLoader.

        private function loaderCompleteHandler(event:Event):void {
            //rebuild the object                            
            var product:Product = loader.data.readObject();
        }

        function sendData(event:Event){
            var request:URLRequest = new URLRequest("/test/loaddata/");
            loader = new URLLoader();
            loader.dataFormat = URLLoaderDataFormat.BINARY;
            loader.load(request);
            loader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
            loader.addEventListener(Event.COMPLETE, loaderCompleteHandler);
        }

If you want to use an HTTPService you have a problem, because by default it does not handle binary data. However it’s easy to extend the class and to add binary support.

    public class HTTPServiceExt extends HTTPService{
        protected static var binaryChannel:Channel;
        protected static var binaryChannelSet:ChannelSet;
        public var binaryData:Boolean;

        public function HTTPServiceExt(){
        }
        override public function send(parameters:Object = null):AsyncToken{
            if (( useProxy == false ) && (binaryData)){
                if ( binaryChannelSet == null ){
                    var dcs:ChannelSet = new ChannelSet();
                    binaryChannel =
                    new DirectHTTPBinaryChannel("directhttpbinarychannel");
                    dcs.addChannel(binaryChannel);
                    channelSet = dcs;
                    binaryChannelSet = dcs;
                }
                else if ( channelSet != binaryChannelSet ){
                    channelSet = binaryChannelSet;
                }
            }
            return super.send(parameters);
        }
    }

The class DirectHTTPBinaryChannel is taken from Anirudh Sasikumar’s blog. It extends DirectHTTPChannel and it configures the internal URLoader to work with binary data.

Now you have an HTTPService class that knows how to receive binary data also. In order to use it you have to add the binaryData parameter:

    function resultCall(event:ResultEvent):void{
       var product:Product = event.result.readObject();
    }
    <local:HTTPServiceExt binaryData="true" id="srv"
        url="/test/loaddata/"
        result="resultCall(event)"
        fault="faultCall(event)"/>

That’s all. You can refine the code; for example you may want to create only one URL for all the resources and to pass parameters, or you can use SOAP services and add the AMF binary format as an attachment.

I also uploaded a Flex/WTP project here containing all the source code.

4 Responses to “Using AMF and Web Services”

  1. Anirudh Sasikumar Says:

    Hi Cornel,

    Nice blog post.

    Just FYI, I had earlier isolated the java classes required for AMF serialization alone into a small jar (http://fxstruts.googlecode.com/files/fxstruts-serialization-0.2.jar) for FxStruts. So this can be used without having all the additional jars in place.

    Thanks,
    Anirudh

  2. Sebastian Says:

    Hi Cornel,

    You’ve mentioned REST early in your POST but it is not obvious to me from your comments how HTTPServiceExt is compensating for HTTPService’s lack of support for REST: verb restriction (GET, POST) and full HTTP response status codes (my RESTful API returns 201, 203, .. ). Can you elaborate on this? Thanks.

    Sebastian

  3. cornel Says:

    Thanks Anirudh for the info

  4. cornel Says:

    Hi Sebastian,

    No, the class HTTPServiceExt cannot compensate for the the Flash Player restrictions (note that I wrote Flash Player and not HttpService). So in order to support fully REST you need to use proxy=”true” and to configure a destination in BlazeDS/LCDS/Granite etc (so the call will be made by the proxy server and not the directly by the Flash Player).

Leave a Reply