June 15, 2003

flash speaks java

This weekend I picked up Danny Goodman's JavaScript Bible Gold for my weekend reading, and boy, am I glad I did. In section V of the book, Putting JavaScript to Work, Goodman talks about the faceless applet and how that could be used to make the "applet do the heavy algorithmic lifting" while html does the UI.

In his example, he creates an applet which retrieves a file's content from the hard drive and stores it in a java variable. This is then available to be used by javascript functions through calls to the applet's methods. It got me thinking right away. First thing that came to my mind was Colin Moock's excellent tutorial on flash-javascript communication with fscommand, and aptly so, that I had read sometime earlier.

For you and I, what Goodman's words coupled with Moock's outlines effectively mean is that java-flash communication is possible with fscommands through javascript; and that we can let java do the "heavy algorithmic lifting" while flash does the UI. Well, this isn't an entirely new idea, I can hear you say. Flash remoting does this already, chants another. XMLSockets already provide this functionality, says yet another. Can thy humble servant indulge thee? For starters, please pick up Colin Moock's tutorial--print it out or open another page for it, but please whatever you do, do not close my page ;)]--as he's done all the base work for us. We will just make few changes to his work. Also I assume you have some basic knowledge of java and java applets as I will not attempt to break them down here.

The applet I present is just a modification of the original source from Goodman to cater for changes/deprecations in the java api. Please download the files used for this post.

I will go ahead an post the source for the applet.
// -----------------------------------------------------------------------------
// @library   FileReader.java
// @author    Danny Goodman (original) 
// @author    Emmanuel Okyere (modifications)
//            Reimplented class to take care of deprecated IO and other thread
//            safety issues, ie.
//            1. DataInputStream( in) <- BufferedReader( InputStreamReader( in))
//            2. Changed run() and stop() methods to conform to current stds
// @created   n/a
// @modified  06-14-2003
// @reference Goodman, D. 2001. JavaScript Bible, Gold Edition. Wiley & Sons.
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// This java applet reads a file from disk and stores its contents in a var
// It has accessor methods for stored values. The main methods of interest
// are getFile() and fetchText()
//------------------------------------------------------------------------------
//------------------------------------------------------------------------------
// @disclaimer the usual suspects
//------------------------------------------------------------------------------

import java.net.*;
import java.io.*;

public class FileReader extends java.applet.Applet implements Runnable {
     Thread thread;
     URL url;
     String output;
     String fileName = "foo.txt";
     
 
     public void getFile( String fileName) throws IOException {
          String result, line;
          InputStream connection;
          BufferedReader br;
          StringBuffer buffer = new StringBuffer();
          
          try {
               url = new URL( getDocumentBase(), fileName);
           } catch ( MalformedURLException e){
               output = "AppletError " + e;
           }
          
          try {
               connection = url.openStream();
               br = new BufferedReader( new InputStreamReader( connection));
               while ( (line = br.readLine()) != null)
                   buffer.append( line + "\n");
               result = buffer.toString();
           } catch (IOException e){
               result = "AppletError: " + e;
           }
          output = result;
      }
 
 
     public String fetchText(){
          return output;
      }
 
 
     public void init(){}
 
 
     public void start(){
          if ( thread == null){
               thread = new Thread( this);
               thread.start();
           }
      }
     
     
     public void stop(){
          thread = null;
      }
     
 
     public void run(){
          Thread t = Thread.currentThread();
          while ( thread == t)
              try {
                   getFile(fileName);
               } catch (IOException e){
                   output = "AppletError: " + e;
               }
      }
}
You can save this as FileReader.class and compile or alternatively used the class file already compiled by me. The class will attempt to open a text file, named foo.txt which should be in the same directory. Now let me present the FSCommandHandler utility class:
// -----------------------------------------------------------------------------
// @library    FSCHandler.as
// @author     Emmanuel Okyere <http://eostudios.port5.com | eokyere@cox.net>
// @created    06-14-2003
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// created to allow for notification and callbacks when fscommands return values
// to falsh to save me the use of the 'notorius frameloop'. Intances emulate 
// Threads--implemented here with the setInterval and clearInterval functions
// -----------------------------------------------------------------------------
// -----------------------------------------------------------------------------
// @disclaimer the usual suspects
// -----------------------------------------------------------------------------

FSCHandler = function(){
     this.init.apply( this, arguments);
}

// -----------------------------------------------------------------------------
// Constructor/init funciton for FSCHandler objects. It takes objects that want
// to listen for callbacks from it as arguments and adds them to its
// listeners list. The default listener is _root. Refer code
// -----------------------------------------------------------------------------
FSCHandler.prototype.init = function(){
     AsBroadcaster.initialize( this);
     this.addListener( _root );
     // arguments, if any are listeners for this instance apart from _root 
     var n = arguments.length;
     for( var i = 0; i != n; ++i )
         this.addListener( arguments[ i]);
     
     this.fsc_method = null;
     this.fsc_args   = null;
}



// -----------------------------------------------------------------------------
// This is the main routine that runs the 'thread'. In this implementation, it
// broadcasts the fsc_method, fsc_args data members when they are not null and
// resets them to null to wait for a new value. Developers should look at 
// extending this if additional functionality is needed
// 
// @param  o         FSCHandler Object to derefence and use as needed,
//                   passed from start(). Default implementation passes *this*
// @see    start()
// @return void
// -----------------------------------------------------------------------------
FSCHandler.prototype.run = function( o ){
     // trace( "running: " + o);
     if ( o.fsc_method == null)
         return;
     trace( "got a msg:" + o.fsc_method);
     o.broadcastMessage( o.fsc_method, o.fsc_args);
     o.fsc_method = null;
     o.fsc_args   = null;
}


// -----------------------------------------------------------------------------
// Creates an intervalID for the instance and initiates setInterval. It also
// starts the run method for the instance.
//
// @param  o         FSCHandler Object to derefence and use as needed,
//                   Default o is *this*
// @param  t         Periodic interval for run method, default is 1000ms
// @see    run()
// @return void
// -----------------------------------------------------------------------------
FSCHandler.prototype.start = function( t, o ){
     if ( t == null)
         t = 1000;
     if ( o == null)
         o = this;
     this.run();
     this.intervalID = setInterval( this.run, t, o);
}


// -----------------------------------------------------------------------------
// method used to terminate execution of thread--effectively, stops the run
// method of the instance.
//
// @return void
// -----------------------------------------------------------------------------
FSCHandler.prototype.stop = function(){
     clearInterval( this.intervalID);
}


// -----------------------------------------------------------------------------
// static String representation of class for debug purposes
// -----------------------------------------------------------------------------
FSCHandler.prototype.toString = function(){
     return "FSCHandler";
}



/*
function hehe(){
    trace( "hehehehe");
}

foo = new FSCHandler();
trace( "foo created");
foo.fsc_method = "hehe";
foo.fsc_args = "";

foo.start();
*/
We will use the movie.SetVariable( var, val ) method (which is part of the set of fscommands) to set the fsc_method and fsc_args data members of the FSCHandler class instancea from javascript to flash. This class runs itself periodically by use of the setInterval function. When it's fsc_method data member is not null, it knows it has a new value to broadcast, so it broadcasts the fsc_method (which is effectively an onXXXXX handler) with fsc_args as an argument. To put it short, the class facilitates the returning of values from javascript to flash and provides for easy notification of such returns. Your fla should provide a dynamic text field (i call it txtMessage) to hold the contents of the file returned from disk, and two buttons--btnGetFile and btnClear--whose names, I am sure, define their behaviors. the actionscript for the fla (should be in frame 1) follows:
#include "fsc-handler.as"

fschFileReader = new FSCHandler();

// invoked from a broadcast from fschF
onGetFile = function( file ){
     txtMessage.text = file;
};

btnClear.onRelease = function(){
     txtMessage.text = "";
}

btnGetFile.onRelease = function(){
     fscommand( "getFile");
}

fschFileReader.start();
With the flash side and the java applet all set up, all that is needed is the javascript (with fscommands) to marry the two. As we will see in the javascript, getFile passed from the fscommand, is used to instruct the javascript to retrieve the contents of the foo.txt from the applet and set fsc_args in flash to that. We retrieve and set fsc_args first, because the moment fsc_method is set and the FSCHandler instance is on a run cycle, it will broadcast the fsc_method (with fsc_args being null) and that is undesirable. The html (with javascript) to holds it all together:
<HTML>
<HEAD>
    <meta http-equiv=Content-Type content="text/html;  charset=ISO-8859-1">
    <TITLE>FileReader::Flash &lt;: == :> JavaScript &lt;: == :> Java</TITLE>

    <!-- scripts to take care of fscommands -->

    <SCRIPT LANGUAGE="VBScript">
    Sub FileReader_FSCommand( ByVal command, ByVal args )
        Call FileReader_DoFSCommand( command, args)
    End Sub

    </SCRIPT>
    <SCRIPT LANGUAGE="JavaScript"> 
    function FileReader_DoFSCommand( command, args){ 
         switch ( command){
              case "getFile":
                  FileReader.setVariable( "/fschFileReader:fsc_args",
                                                    document.readerApplet.fetchText());
                  FileReader.setVariable( "/fschFileReader:fsc_method", "onGetFile" );
              default:
                  alert( "flash messeage: " + command + " --> " + args );
          }
     }
    </SCRIPT>

</HEAD>
<BODY bgcolor="#FFFFFF">
    <!-- URL's used in the movie-->
    <!-- text used in the movie-->
    <OBJECT classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"
            codebase=
"http://download.macromedia.com/pub/shockwave/cabs/flash/swflash.cab#version=6,0,0,0"
            WIDTH="100%" HEIGHT="100%" ALIGN=""
            id="FileReader" >
         <PARAM NAME=movie VALUE="FileReader.swf"> 
         <PARAM NAME=quality VALUE=high> 
         <PARAM NAME=bgcolor VALUE=#FFFFFF> 
         <EMBED src="FileReader.swf" 
                quality=high 
                bgcolor=#FFFFFF  
                WIDTH="100%" 
                HEIGHT="100%" 
                NAME="FileReader" 
                ALIGN=""
                TYPE="application/x-shockwave-flash" 
                swLiveConnect="true"
                PLUGINSPAGE="http://www.macromedia.com/go/getflashplayer">
        </EMBED>
    </OBJECT>
    <!-- using a faceless applet as illustrated in JavaScript Gold, Goodman -->
    <APPLET CODE="FileReader.class" NAME="readerApplet" WIDTH=1 HEIGHT=1></APPLET>
</BODY>
</HTML>

Granted, this is a humble example of flash-java communications, but as you can see, the sky is the limit, as long as you take issues such as latency etc. into consideration. I have always loved the way java handles Sockets and with this, I will probe further and see what I can come up with. As always comments and suggestions are welcome. Also, If you had any problems with this post or the files, you can always leave a msg. Happy flashing. :)

References
1. Goodman, D. 2001. JavaScript Bible, Gold Edition. Wiley & Sons.
2. MM Support. Publishing & Exporting. Flash Methods.Available online (06/03)
3. Moock, C. 1999-2003. Your First FSCommand. Available Online (06/03) Posted by eokyere at June 15, 2003 12:35 AM | TrackBack
Comments

GREAT work, Emmanuel!

You could also use the Object.watch method to check for changes in the variable:

FSCHandler.prototype.run = function(i, oldV, newV, o){
    if(newV == null) return;
    o.broadcastMessage(o.fsc_method, o.fsc_args);
    o.fsc_method = o.fsc_args = null;
    return null;
}
FSCHandler.prototype.start = function(o){
    if(o == null) o = this;
    o.watch("fsc_method", this.run);
};

Posted by: Jonas Galvez at June 15, 2003 02:30 PM

Ooops... that should be:
o.watch("fsc_method", this.run, o);

Posted by: Jonas Galvez at June 15, 2003 02:33 PM

i had never used the Object.watch method. thx a lot for pointing it out, jonas, and thx for coming.

Posted by: eokyere at June 15, 2003 04:08 PM

Excellent work!!!

Another thing that could be useful is doing ASBroadcaster initialization in the prototype. So all instance of the class always have broadcast ability.

like,
AsBroadcaster.initialize (FSCHandler.prototype);

only thing to make sure is you override the _listeners object in the init function like,

_listeners = [];

since we dont want to end up with reference to the _listeners in the prototype.

peace
tim

Posted by: tim at June 16, 2003 02:57 AM

i actually use ASBroadcaster.initialize( this) in the init method which is sort of a 'delegate constructor'... and yes, you raise a very good point with the _listeners... eventually, on the cleanup, one should adopt the library from such people as bokel and bhall (which afaik are optimized as well)

thanks a lot for your visit and comments... come again soon

Posted by: eokyere at June 16, 2003 07:55 AM
Post a comment









Remember personal info?