Googolflex!!
  • Home
  • About
  • Contracting

Recent Posts

  • Sprint’s new “Simply ‘Almost’ Everything®” Plans
  • CSS Changes in Flex 4
  • Dotted Underline LinkButton (Flex)

About The Author : jwd

This is John Dusbabek's tech blog. John is a software engineer and Flex developer in Provo, UT, where he lives with his lovely wife and four sons.

Recent Comments

  • Nikos on Flex: Binding to an Interface
  • Iain Hosking on Apache mod_proxy_balancer: No Protocol handler was valid

Archive for Flex 3

May
21

Installing Red5 Media Server on CentOS 5.5 / Fedora

Posted by: jwd | Comments (1)

This post details the steps I took to install Red5 from source on a CentOS 5.5 base server.


1. Install Java using yum. (The -y flag provides a ‘yes’ answer to all prompts.)

yum -y install java-1.6.0-openjdk java-1.6.0-openjdk-devel


2. Install the Apache ant binary. I downloaded the most recent release one directly from Apache’s archives. The version installed using yum (and default repositories) will not compile Red5.

cd /usr/src
wget http://archive.apache.org/dist/ant/binaries/apache-ant-1.8.1-bin.tar.bz2
tar jxvf apache-ant-1.8.1-bin.tar.bz2
mv apache-ant-1.8.1 /usr/local/ant


3. Set important Java environment variables.

export ANT_HOME=/usr/local/ant
export JAVA_HOME=/usr/lib/jvm/java
export PATH=$PATH:/usr/local/ant/bin
export CLASSPATH=.:$JAVA_HOME/lib/classes.zip


4. You should also add them to bashrc so they’re available the next time you log in.

echo 'export ANT_HOME=/usr/local/ant' >> /etc/bashrc
echo 'export JAVA_HOME=/usr/lib/jvm/java' >> /etc/bashrc
echo 'export PATH=$PATH:/usr/local/ant/bin' >> /etc/bashrc
echo 'export CLASSPATH=.:$JAVA_HOME/lib/classes.zip' >> /etc/bashrc


5. Install subversion with yum. If you did a base install of CentOS, subversion will not be preinstalled.

yum -y install subversion


6. Check out the Red5 source.

cd /usr/src
svn checkout http://red5.googlecode.com/svn/java/server/trunk/ red5


7. Build Red5 with ant.

mv red5 /usr/local/
cd /usr/local/red5
ant prepare
ant dist


8. Start Red5.

cp -r dist/conf .
./red5.sh


9. Create a startup script (optional):

vi /etc/init.d/red5

Then enter this text into the file.

#!/bin/bash
# chkconfig: 2345 80 80
# description: Red5 streaming server
# processname: red5

. /etc/rc.d/init.d/functions

[ -r /etc/sysconfig/red5 ] && . /etc/sysconfig/red5

RETVAL=0

case "$1" in
	start)
	echo -n "Starting red5: "
	cd /usr/local/red5
	/usr/local/red5/red5.sh >/dev/null 2>/dev/null &&
	RETVAL=$?
	if [ $RETVAL -eq 0 ]; then
		echo $! > /var/run/red5.pid
		touch /var/lock/subsys/red5
	fi
	[ $RETVAL -eq 0 ] && success $"red5 startup" || failure $"red5 startup"
	echo
	;;
	stop)
	echo -n $"Stopping down red5: "
	killproc -p /var/run/red5.pid
	RETVAL=$?
	echo
	[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/red5
	;;
	restart)
	$0 stop
	$0 start
	;;
	status)
	status red5 -p /var/run/red5.pid
	RETVAL=$?
	;;
	*)
	echo $"Usage: $0 {start|stop|restart|status}"
	RETVAL=1
esac

exit $RETVAL


10. Set permissions on the script.

chmod a+x /etc/init.d/red5
chkconfig red5 on


11. Add necessary ports to the iptables file.

vi /etc/sysconfig/iptables

Then add the following lines:

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 5080 -j ACCEPT
-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 1935 -j ACCEPT

You may also want to add exceptions for 8443, 8088, 9035, 1936, and 9999 if necessary for your application,

service iptables restart

You may want to restart the server, and the Red5 test page can be accessed at http://localhost(or servername):5080/

Categories : Flex 3, Red5
Comments (1)
Feb
09

Flex Socket Connections : Socket Policy File

Posted by: jwd | Comments (0)

Starting with certain versions in the 9.0’s of Flash player, socket communication in Flex began adding additional security measures. The one I am going to discuss in the post is the socket policy file. In short, the socket policy file is an XML file that is served by default from port 843 and contains information regarding which ports on _this_ server that Flash may connect to. Additionally it allows you to specify from which domains you wish to allow connections.


Loading the Policy File From Flex

The policy file can be explicitly requested by making the call:

Security.loadPolicyFile("host.withpolicyfile.com:843");

Or you can trust it will implicitly make the request when you attempt a socket connection. The policy is valid for a particular IP address over the life of the SWF. A policy request consists of the following line, nothing more:

<policy-file-request/>

And the correct response is the policy file, followed by a null byte. My example policy server file will not be so picky about it’s request, use it at your own risk. Adobe has one that actually checks to see if the request was formatted correctly before sending the response. Furthermore, rather than reading in an actual policy file, my example hard codes it into the policy server.


Policy File Format

Here is a sample policy file, it is provided by Adobe. You can make whatever changes you need to, as I did in mine:

<?xml version="1.0"?>
<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">

<!-- Policy file for xmlsocket://socks.example.com -->
<cross-domain-policy> 

   <!-- This is a master socket policy file -->
   <!-- No other socket policies on the host will be permitted -->
   <site-control permitted-cross-domain-policies="master-only"/>

   <!-- Instead of setting to-ports="*", administrator's can use ranges and commas -->
   <!-- This will allow access to ports 123, 456, 457 and 458 -->
   <!--allow-access-from domain="swf.example.com" to-ports="123,456-458" /-->
   <allow-access-from domain="*" to-ports="80" />
</cross-domain-policy>


Policy File Server

And here is the Perl code that runs the policy server. You can see it is just a basic socket server. Adobe’s version of this (which I based mine off) allows you to pass in the port as well as the path to the policy file. This is a stripped down version of that server, with most of the essentials hard coded.

use Socket;

my $NULLBYTE = pack('c', 0);
my $port = 843;
my $content ='<?xml version="1.0"?>'."\n" .
'<!DOCTYPE cross-domain-policy SYSTEM "/xml/dtds/cross-domain-policy.dtd">'."\n" .
'<cross-domain-policy>' . "\n" .
   '<site-control permitted-cross-domain-policies="master-only"/>'."\n" .
   '<allow-access-from domain="*" to-ports="80" />'."\n" .
'</cross-domain-policy>'."\n";

socket    (LISTENSOCK, PF_INET, SOCK_STREAM, getprotobyname('tcp'))
          or die "socket() error: $!";
setsockopt(LISTENSOCK, SOL_SOCKET, SO_REUSEADDR, pack('l', 1))
          or die "setsockopt() error: $!";
bind      (LISTENSOCK, sockaddr_in($port, INADDR_ANY))
          or die "bind() error: $!";
listen    (LISTENSOCK, SOMAXCONN)
          or die "listen() error: $!";

while ( my $clientAddr = accept(CONNSOCK, LISTENSOCK)) {
    my ($clientPort, $clientIp)= sockaddr_in($clientAddr);
    my $clientIpStr = inet_ntoa($clientIp);

   # Consume the request
    local $/ = $NULLBYTE;
    my $request = <CONNSOCK>;
    chomp $request;

   # Send the policy file
    print CONNSOCK $content;
    print CONNSOCK $NULLBYTE;
    close CONNSOCK;
}
}


Opening A Port

Remember to open port 843 (in Fedora Core) by adding the following line in /etc/sysconfig/iptables :

-A RH-Firewall-1-INPUT -m state --state NEW -m tcp -p tcp --dport 843 -j ACCEPT

Then reload the iptables:

/etc/init.d/iptables restart
Categories : Actionscript, Application Servers, Flex 3, Perl, client/server
Comments (0)
Feb
08

Simple Flex Socket Client

Posted by: jwd | Comments (2)

Client-server programming is one of my passions, and I enjoy doing it in almost any language. Which makes it fitting that Flex (ActionScript) is one of my favorite languages to develop in, because it truly is a client-side technology which pretty much means it’s open to just about any type of backend server. I suppose you could make that argument with just about any language depending on how it’s being used, but since Flex isn’t processed and returned to the browser as HTML, that makes it a little different in my opinion.

Even in Flex, most applications will connect to a backend server that’s running an HTTP service (which queries the database on behalf of Flex), but I’m even going to depart from that in this post. I’m going to be demonstrating how to make socket calls to another service. You could apply this to making HTTP requests on port 80, make requests directly to a MySQL server running on port 3306. In this particular post, I will keep it simple by demonstrating how to make an HTTP request.


Declaring and initializing the Socket

You declare socket like this:

import flash.net.Socket;

private var socket : Socket;

Inside an initialization method, we will add a listener for the CONNECT event, and the SOCKET_DATA event. The CONNECT event alerts us when the connection has been made, and allows us to send our request. The SOCKET_DATA event is triggered whenever there is data waiting to be read from the socket.

socket = new Socket();
socket.addEventListener( Event.CONNECT, onConnect);
socker.addEventListener( ProgressEvent.SOCKET_DATA, onSocketData);

Connecting and Sending Data

At some point you will need to call connect on the socket, which accepts the URL of the server and the port it will be connecting to. In the onConnect() method, you will write the request using one of the writeXXX() methods, and call flush() which actually sends the data over the wire.

socket.connect(txtUrl.text, 80);

private function onConnect(event : Event) : void {
   var requestString : String = getHttp_1_1Request();
   socket.writeUTFBytes(requestString);
   socket.flush();
}

Reading Data

As not all socket data will be available at once, as in most client programs, you will want to read the data in a loop, using one of the readXXX() methods.

private function onSocketData(event : ProgressEvent) : void {
   while (socket.bytesAvailable) {
      txtResponse.text += socket.readUTFBytes(socket.bytesAvailable);
   }
   socket.close();
}

In general you would be checking for the end of the response you’re expecting (based on protocol), to simplify things I simply close the socket after the while loop exits the first time. If you leave the socket open for too long without reading data, I have noticed that I receive the following error, regardless of whether I have the correct policy file including cross-domain.xml:

Error #2044: Unhandled securityError:. filename.swf text=Error #2048: Security sandbox violation cannot load data from the.host.name

In this example, the raw output received from the socket request will be shown in the text box. You can download the complete source to this example here (where you may also view a screencast demo of this procedure).

Depending on what version of the Flash Player you’re running, this example may not run as-is if the server you are connecting to does not implement the policy-file (served from port 843. I will be discussing this in an upcoming post where I delve into a little more advanced socket programming in Flex, including how and where to deploy a master policy file.

Categories : Application Servers, Architecture, Flex 3, Web Services, client/server
Comments (2)
Dec
22

JUICE chat (Stratus Peer to Peer)

Posted by: jwd | Comments (0)

One of my ongoing weekend projects is an AIR chat application, recently rechristened “JUICE” a.k.a. “John’s Ultimate Internet Chat Experience”. I realize that may only be the case for myself, but the project has been very educational and entertaining for me.

The initial release used AIR/Flex as the front-end, ColdFusion and MySQL for the backend, and BlazeDS messaging for message transmission and “peer discovery”.  I worked on it over a couple weeks adding features like encryption, toast-style notifications, as well as a few other less notable things.

I knew the architecture was awful and would never scale beyond a couple hundred users (if that many). Here’s the basics of how it worked:

  1. The user would login, ColdFusion/MySQL would send back a list of chat buddies.
  2. Each logged in user would ping the messaging server every 10-30 seconds to announce “I’m still here!”
  3. The pings would be broadcast to everyone logged in (and connected to the messaging server). The application would filter out the ones it was interested in based on user id.
  4. Sending messages worked similarly, the user would send the message (which included information about the intended recipient), which would then be broadcast to EVERYONE.
  5. Everyone would filter this information, and the recipient would know it was for him, and would display it.

You can see why this would not scale well, the amount of traffic and pinging going on.  Eventually even a user with only 1 or 2 buddies would notice a significant drop in performance as the number of total users increased.  There are some applications of “chat” for which BlazeDS messaging and polling is well suited for (like a chat room).  A chat application involving “buddies”  is much better suited for P2P.

Enter Stratus.  I discovered Stratus a few months ago but was unwilling to delve into it on account of my hectic schedule.  Fortunately one of my friends went and did all the dirty work figuring out how to get it up and running (turns out it’s pretty easy) and I was able to apply it to my chat client.

I’m very pleased with the results, though it’s still very much a work in progress.  I’ve even been able to add video and voice chat features, which is why I went through the trouble in the first place.  Here is how my peer discovery mechanism operates.  It requires only 3 calls to the server per client (which I could easily condense to 2 if I wanted).

  1. The user logs in.
  2. If the user successfully authenticated, he connects to Stratus and receives his peer ID.
  3. The user updates his peer ID in the database (which was set to 0) and gets a list of his buddies and their peer IDs.  I also update information like whether or not I have a camera/mic.
  4. The user processes his buddy.  If the peer ID is something other than 0, then I know this buddy logged in before me, so I must send him my peer ID.
  5. Any buddies that have a peer ID of 0 are not logged in, I need to listen for them though since they will get my peer ID when they log in.
  6. When I log out or close the application, I call the server and reset my peer ID to 0.  I also notify all of my connected peers I’m logging out.

There are a few more steps in the process, but it decreases the load on the server significantly, transferring the “burden of proof” onto the clients.

Interesting to note is the way I get the peer ID of a client who connects after me (step 4).  I don’t actually have to make a call for this, rather he simply has to start broadcasting to me on the NetStream I set up to listen for him (step 5).  When the connection is made, an event will be dispatched by the NetStream class.  In fact, 3 events will be fired, I just chose to act on one of them.

The peer ID can be obtained from the peerStreams array on the publishing NetStream.  I then use that information to set up my subscribing NetSream.

if (this.peerId == null || this.peerId == &amp;quot;0&amp;quot;) {
    this.peerId = publishNetStream.peerStreams[0].farID;

    this.subscribeNetStream = new NetStream(model.netConnection, this.peerId);
    this.subscribeNetStream.client = new NetStreamClient();
    this.subscribeNetStream.play(streamName);
}

Anyway,I’m planning on doing another post to outline my video and voice handshake protocols.  They’re nothing to write home about, but I’d love some feedback if there’s a better way.  I’m also planning on releasing JUICE after I bring it’s level of functionality back up to where the original version was, so stay tuned and you should see my install badge at the bottom of my blog.

Categories : AIR, Actionscript, Architecture, Cold Fusion, ColdFusion Enterprise Server, Flex 3, Messaging, Scalability
Comments (0)
Aug
19

Contextual Shortcut Manager

Posted by: jwd | Comments (0)

I’m about ready to sleep on my ShortcutManager, I figured I might as well finish off the series. I added context to it, while still maintaining backward compatibility with the non-context enabled version. I don’t think I posted it, so it probably won’t matter so much to my readers. For those who haven’t read the previous posts on my ShortcutManager, the “context” I’m referring to is the context in which a keyboard shortcut might be used; such that a given shortcut might have several functions associated with it depending on its context.

In short I needed four things to implement the context aware shortcut manager.

1. The IKeyboardContext interface

This is a simple interface that a model or class can implement that allows a keyboardContext to be set and retrieved. A keyboardContext is simply a string that identifies the current context. These contexts will be associated with a shortcut in the manager. Here is the entire interface:

package com.googolflex.gflib.interfaces {

	public interface IKeyboardContext {

		[Bindable(event="propertyChange")]
		function get keyboardContext() : String;
		function set keyboardContext(v : String) : void;
	}
}

2. A reference to an IKeyboardContext needed to be added to the ShortcutManager

As the keyboardContext changes in the model, the ShortcutManager needs to be able to access it, so it can retrieve the correct function for a given keycode-flags-context tuple. The IKeyboardContext was left null, which helps with the backward compatibility (the implementer will be forced to set it if she wants to use it). Here is the line I added, which is pretty trivial:

public static var model : IKeyboardContext = null;

3. Add the context to the addShortcut method

I gave it a default value, which incidentally was “default”, so that every function added to the Dictionary would have some kind of context. Here is the new addShortcut() method:

public static function addShortcut(keycode : uint, func : Function, flags : uint, context : String = "default") : void {
	if (flags > 0 && flags < 8)
		functionMap[keycode + "-" + flags + "-" + context] = func;
}

4. Finally, the shortcutHandler() needed to incorporate the context

I first check if the IKeyboardContext is null, if it is I know to use “default” as the context. If no context was ever specified, and no IKeyboardContext was ever set, then it still works. Here is the modified code:

public static function shortcutHandler(event : KeyboardEvent) : void {
	var flags : uint = getFlags(event);
	var context : String = (model!=null) ? model.keyboardContext : "default";

	if ( functionMap[event.keyCode + "-" + flags + "-" + context] != null )
		(functionMap[event.keyCode + "-" + flags + "-" + context] as Function).apply();
}

And was pretty much it. I will post the modified code, along with a demonstration application at the end of the post. I do want to make a couple points to be aware of when actually incorporating this into a project.

First, your model/class will need to implement IKeyboardContext. Second, you need to remember to set ShortcutManager.model = myIKeyboardContext somewhere. Third, you’re on your own when it comes to maintaining your context. Maybe the “SimpleContextManager” will be next, who knows? And fourth, you’ll have to specify a context when you call addShortcut(). Remember if you download the demo, it will need to have some kind of reference to the ShortcutManager code.

Code
ShortcutManager.zip
KeyboardContextDemo.zip

Categories : Actionscript, Architecture, Flex 3, Flex 4, Scalability
Comments (0)
Aug
19

Simple Flex ShortcutManager (revisited)

Posted by: jwd | Comments (0)

I’ve put some more thought into my shortcut manager, and have decided on a way to implement the shortcut context, as well as curb the Dictionary explosion that my previous architecture would have had.

The new version uses one Dictionary to store all functions, the keyCode, combo keys, and context is all encoded into the dictionary key. To illustrate here is my new addShortcut() method:

public static function addShortcut(keycode : uint, func : Function, pair : String) : void {
	var flags : int = getFlagsFromString(pair);

	if (flags > 0)
		functionMap[keycode + "-" + flags] = func;
}

It accepts the same parameters, the only difference is that the pair string is now a dash delimited string, containing the key combinations (i.e. “ctrl“, “ctrl-alt“, “shift-ctrl“, etc). Those are parsed by a method getFlagsFromString() which I will probably get rid of in favor of some named constants on the ShortcutManager class. That will eliminate the need for the additional method, and pair (which will be one of said constants) can be added to the key as-is.

The removeShortcut() is very similar. I haven’t implemented “contexts” yet, but you can visualize how it will be done:

functionMap[keycode + "-" + flags + "-" + context] = func;

Now that there is only one Dictionary, the shortcutHandler() method has become almost trivial. Here it is, in all of its 5-lined glory:

public static function shortcutHandler(event : KeyboardEvent) : void {
	var flags : uint = getFlags(event);

	if (functionMap[event.keyCode + "-" + flags] != null)
		(functionMap[event.keyCode + "-" + flags] as Function).apply();
}

Like I said… trivial.

Categories : Actionscript, Architecture, Flex 3, Flex 4, Scalability
Comments (0)
Aug
19

Using Bit-wise Math to Simplify Logic

Posted by: jwd | Comments (0)

Strictly speaking, bit strings are not nearly as easy to understand as named boolean variables; but there are situations when they can simplify or eliminate the need for long boolean expressions.

Consider my recent post about the ShortcutManager. There are three control keys that may or may not be pressed at any given time. This amounts to 8 possible key combinations (7 if you ignore when none of them are pressed). Here’s what this would look like if you used a boolean expression to check for each of the possible combinations:

var ctrl : Boolean = event.ctrlKey;
var alt : Boolean = event.altKey;
var shift : Boolean = event.shiftKey;

if (!shift && !alt && ctrl) {
	// CTRL
}
else if (!shift && alt && !ctrl) {
	// ALT
}
else if (!shift && alt && ctrl) {
	// CTRL-ALT
}
else if (shift && !alt && !ctrl) {
	// SHIFT
}
else if (shift && !alt && ctrl) {
	// CTRL-SHIFT
}
else if (shift && alt && !ctrl) {
	// SHIFT-ALT
}
else if (shift && alt && ctrl) {
	// CTRL-SHIFT-ALT
}

That might not seem too bad, and it isn’t that hard to understand what’s going on (even without the comments). But what happens when you’ve got 4 variables (16 possibilities)? The number of combinations increase exponentially. Even if the combinations you are actually interested in are sparse, who wants to type out 10 lines each with 32 boolean variables?

With a simple method to construct my “bit string”, we can greatly simplify this. ActionScript doesn’t have a binary number type, so you’ll have to use integers. If you can count by powers of two you won’t have any trouble. Here’s a method that constructs my “bit string” integer based on selected control keys:

private static function getFlags(event : KeyboardEvent) : uint {
	var flags : uint = 0;
	flags += (event.ctrlKey) ? 1 : 0;
	flags += (event.altKey) ? 2 : 0;
	flags += (event.shiftKey) ? 4 : 0;

	return flags;
}

Having this integer allows me to simplify the logic as follows (which isn’t harder to understand at all, if you use comments):

switch ( getFlags(event) ) {
	case 1: // CTRL
		break;
	case 2: // ALT
		break;
	case 3: // CTRL-ALT
		break;
	case 4: // SHIFT
		break;
	case 5: // CTRL-SHIFT
		break;
	case 6: // SHIFT-ALT
		break;
	case 7: // CTRL-SHIFT-ALT
		break;
}

This is powerful, but it’s just scratching the surface. I haven’t even discussed the bitwise operators &, | and ^. Or the bitwise shift operators >> and <<. These come in handy when you store a series of options as a bit string and need to test whether a certain option is on or off.

For example, if we needed to test if value returned by getFlags() had the SHIFT bit set, we could use the following boolean expression to test it. Remember SHIFT = 4:

if (getFlags(event) & 4 == 4)

Of course this doesn’t help us out much now in the ShortcutManager, but assume I was to add 3 possible contexts that these shortcuts might be used in (different screens, for example). They would hold the 8, 16, and 32 bit places. I could use bitwise math to split them if I only needed to manipulate one them at a time (a stretch, I know).

var screenOnly: uint = flags & 56;
var keysOnly: uint = flags & 7;

They have a variety of uses and are there when you need them. There’s an excellent explanation of various bitwise operations and their uses here.

Categories : Actionscript, Design Patterns, Flex 3, Flex 4, Scalability
Comments (0)
Aug
18

Simple Flex ShortcutManager

Posted by: jwd | Comments (0)

Just for fun I wrote a simple ShortcutManager this evening, it turned out to be an interesting little project and taught me a few things about Functions. It’s by no means finished, I’ll detail a few planned improvements after I go over it’s usage.

My goals were to have a static registry that the application could register a keyCode, along with a qualifying key combination (I refer to this as “pair” in the code) like CTRL, or CTRL-ALT. I left out ALT, as Windows seems to have issues with giving up control of the ALT key when used alone. Along with the keyCode, I needed to be able to register a function that would be called when the key combination was pressed. I also wanted to be able to remove a key combination at runtime.

My data structure was simple, I used a separate Dictionary for each of the key combinations I was supporting.

private static var ctrlFunctionMap : Dictionary = new Dictionary();
private static var ctrlAltFunctionMap : Dictionary = new Dictionary();

I wrote an addShortcut method that accepted the keyCode integer, the function to be called, and a string declaring the combination key(s). In it I simply associated the keycode and the function in the appropriate Dictionary. Here is the addShortcut():

public static function addShortcut(keycode : uint, func : Function, pair : String = "ctrl") : void {
	if (pair == "ctrl") {
		ctrlFunctionMap[keycode] = func;
	}
	else if (pair == "ctrlAlt") {
		ctrlAltFunctionMap[keycode] = func;
	}
	else {
		trace("Key combination not supported.");
	}
}

The removeShortcut() method has a similar structure, with one less parameter since we don’t need to know the function. We simply set the appropriate function for the appropriate keycode and combination key to null. Here is the removeShortcut() function:

public static function removeShortcut(keycode : uint, pair : String = "ctrl") : void {
	if (pair == "ctrl") {
		ctrlFunctionMap[keycode] = null;
	}
	else if (pair == "ctrlAlt") {
		ctrlAltFunctionMap[keycode] = null;
	}
	else {
		trace("Key combination not supported.");
	}
}

Finally we need a method that accepts a KeyboardEvent and determines if one of the functions needs to be executed, and then call it. Here is the shortcutHandler() function that does this:

public static function shortcutHandler(event : KeyboardEvent) : void {
	if ( event.ctrlKey && !event.altKey && (ctrlFunctionMap[event.keyCode] != null) ) {
		(ctrlFunctionMap[event.keyCode] as Function).apply();
	}
	else if (event.ctrlKey && event.altKey && (ctrlAltFunctionMap[event.keyCode] != null) ) {
		(ctrlAltFunctionMap[event.keyCode] as Function).apply();
	}
}

I learned an important lesson when I first tried to implement this method. I tried casting the value from the map as a Function, like this:

Function(ctrlFunctionMap[event.keyCode]).apply();

which didn’t work, and spat the error:

EvalError: Error #1066: The form function('function body') is not  supported.

Fortunately, one of ActionScript’s more descriptive error messages (I could tell by the parentheses what was wrong) and was able to correct it using the as operator. I didn’t try it, but I might have been able to not bother casting it at all.

Here’s my ShortcutManagerDemo application, demonstrating how it can be used with a variety of functions and function literals.

import com.googolflex.gflib.managers.ShortcutManager;

private function onCreationComplete() : void {
	ShortcutManager.addShortcut(89, onCtrlY, "ctrl");
	ShortcutManager.addShortcut(89, onAltY, "ctrlAlt");

	var f : Function = function():void { trace("Ctrl-U pressed."); }
	ShortcutManager.addShortcut(85, f, "ctrl");
	ShortcutManager.addShortcut(85,
		function():void { trace("Ctrl-Alt-U pressed."); },
		"ctrlAlt");
}

private function onAddedToStage() : void {
	stage.addEventListener(KeyboardEvent.KEY_DOWN, globalShortcutHandler);
}

private function globalShortcutHandler(event : KeyboardEvent) : void {
	ShortcutManager.shortcutHandler(event);
}

private function onCtrlY() : void {
	trace("Control-Y pressed.");
}

private function onAltY() : void {
	trace("Ctrl-Alt-Y pressed.");
}

I’ll post the code for the manager, and the sample application at the end of the post. Anyway, some improvements I’m planning…

I’d like to associate a context with each command, so that the same keyCodes could be used on different screens. Organizing the manager for such functionality doesn’t strike me as being that hard. I would have to make some changes to my logic to keep down the “conditional explosion”. The daunting part is where to get the context state… I’ll probably end up creating an interface for it, and then make my models implement it.

Of course I’d like to add more combination keys: SHIFT, SHIFT-ALT, CTRL-SHIFT-ALT, etc. The challenging part for that will be simplifying the conditional logic in shortcutHandler() to keep from having to check for a billion cases. I think I would start by pushing the (ctrlFunctionMap[event.keyCode] != null) check down a level, and using my first level of logic only to check the combination keys pressed.

Anyway, if this is useful for anyone I’m glad I could help. Here’s the source:

ShortcutManagerDemo.mxml
ShortcutManager.zip

Update (8/18/09): I added a few things to ShortcutManagerDemo.mxml, namely an interface that allows the user to add their own key commands at runtime. This has limited usefulness in it’s present form, as the functions themselves are still fixed at compile time. Here are some keycodes you can try it out on, anyhow.

a-65; b-66; c-67… 0-48; 1-49; 2-50…

Categories : Actionscript, Flex 3, Flex 4, Flex Components
Comments (0)
Jul
25

Detecting Changes in an Object

Posted by: jwd | Comments (0)

One of the projects I’m working on right now requires me to detect when changes to an object have occurred, so as to enable or disable some “OK”, “Apply” and “Cancel” buttons in the appropriate context.  For the longest time I’ve been thinking I was going to implement this with some sort of data binding, but as it’s distilled on my mind over the past few weeks I’ve decided that wasn’t the right approach.  Here are a few of the requirments/factors that played into that decision:

Background info: the gist of this app is that there’s a datagrid whose items can be loaded into a dialog for editing.

  1. The changes don’t affect the actual object in the model or database being edited until “Apply” or “OK” is pressed (i.e. they can always cancel out of the operation, regardless of any changes)
  2. If a user edits a field, I want that to register as a change.  If they change the field back to what it was, I don’t want that registered as another change, it needs to be as if it had never been changed (i.e. “OK” is not enabled).  This is why I ruled out a Binding approach.
  3. The objects being compared are pretty complex objects.  I didn’t want to have to go and manually compare each property, the properties of composed objects, and the properties of objects nested in collections.  The time to implement this, and the amount of time it would take to compare the object with its sentinel after each edit seemed to rule this option out.

I became pretty familiar with ByteArrays last year during a computer security class (I implemented my AES, RSA, etc. labs using ActionScript) and so I got the idea of serializing these objects to a ByteArray, and then comparing them.  After I came up with the idea, I googled it and realize I’m hardly the first to come up with it, but I’m still going to share it.

First, I built a method to convert an object to a ByteArray, convertToBytes:

public static function convertToBytes(m : Object) : ByteArray {
   var ba : ByteArray = new ByteArray();
   ba.writeObject(m);

   return ba;
}

The ByteArray class has lots of useful writeXXX methods depending on what you’re doing. In this case we use writeObject() which serializes an object to AMF, and writes it to the ByteArray.

Second, I built a method that compares two ByteArrays. There are several ways that this can be accomplished, I chose straight iteration. If someone knows a faster way, please let me know. The faster the better. Here it is:

public static function isByteArrayDifferent(base : ByteArray, revision : ByteArray) : Boolean {
   if (base.length != revision.length)
      return true;
   for (var i : int = 0; i &lt; base.length; i++) {
      if (base[i] != revision[i])
         return true;
   }
   return false;
}

Obviously, if they are different lengths they’re not the same. I like that the method short circuits if a difference is detected, which will be the most common case.

Finally, I wrote the method that accepts the two objects, and returns a Boolean indicating whether a change has been detected. It’s simple, as the preceding two methods do all of the work.

public static function hasObjectChanged(a1 : Object, a2 : Object) : Boolean {
   return isByteArrayDifferent( convertToBytes(a1), convertToBytes(a2) );
}

And it works pretty well. There are a few gotchas I will point out, fortunately none of them apply to my application specifically.

First, suppose your object has a collection with two objects, 1 and 2. The order matters when the objects are serialized. An object with 1,2 will serialize differently than the same object with 2,1. Depending on your application you may have to handle this case specially (if 2,1 is considered the same as 1,2).

Second, you can’t use this method to tell if two objects are the same instance in memory. There are other ways of doing that, this isn’t one of them.

ByteArrays are very useful and have loads of other applications (see the ObjectUtil.copy() method to see how to make copies of objects using ByteArrays. I’ll include a quick method I threw together to output the contents of a ByteArray (it’s very similar to isByteArrayDifferent(), in case anyone’s interested. I used it for debugging, but it can be easily modified to return the String instead of outputting it.

public static function outputBytes(label : String, block : ByteArray) : void {
   var s : String = &quot;&quot;;
   for (var i : int = 0; i &lt; block.length; i++) {
      s = s + uint(block[i]).toString(16) + &quot; &quot;;
   }
   trace (label + &quot;: &quot; + s);
   trace();
}
Categories : Actionscript, Design Patterns, Flex 3
Comments (0)
Jul
21

Changing Default Minimum Height of ScrollThumb

Posted by: jwd | Comments (0)

The default minimum height of the scroll thumb is about 10 pixels, which my client felt was a little too small (their data sets usually run into the several-hundreds, which is enough to push it to the minimum at most resolutions). So I set about trying to fix the problem.

There was no style or property I could set on the VScrollBar to control this, so I went to the ScrollThumb. There was no global style I could set on the class to control this, and if I extended the ScrollThumb and manually set it, there was no way for me to easily tell my VScrollBar to use this version of the ScrollThumb.

Long story short, I had to change the framework code. I changed line 48, where explicitMinHeight was set, to something more agreeable for my client. I was pretty disappointed about this, because it has some unfortunate side effects (which I could probably correct if I had the time). Specifically, if there’s a scroll bar that can’t fit the newly resized ScrollThumb, it doesn’t show one at all. This seems most common in short TextAreas.

Categories : Actionscript, Architecture, Flex 2, Flex 3, Monkey Patching, Scalability
Comments (0)
Next Page »

Search

Feedburner

Subscribe to

Get the latest updates delivered via email

Calendar

September 2010
M T W T F S S
« Jul    
 12345
6789101112
13141516171819
20212223242526
27282930  

Archives

  • July 2010 (1)
  • June 2010 (2)
  • May 2010 (1)
  • February 2010 (11)
  • January 2010 (3)
  • December 2009 (5)
  • November 2009 (1)
  • August 2009 (8)
  • July 2009 (8)
  • May 2009 (4)
  • April 2009 (1)
  • March 2009 (6)
  • January 2009 (1)
  • November 2008 (4)
  • October 2008 (5)
  • September 2008 (1)
  • August 2008 (5)
  • July 2008 (1)
  • June 2008 (2)
  • May 2008 (8)
  • April 2008 (5)
  • March 2008 (2)
  • February 2008 (3)
  • January 2008 (1)
  • December 2007 (6)
  • November 2007 (9)
  • October 2007 (1)
  • September 2007 (2)

Categories

Tag Cloud

adobe apache Architecture book review C++ centos client server architecture Custom Components database Design error message fedora flash catalyst flex Flex 3 Flex 4 fms iis 6 Interaction Design load balancing master-master master-slave mod_proxy_balancer Monkey Patching MySQL no protocol p2p peer to peer Perl PHP Red5 regex replication self registration selinux Shell Scripting shortcut manager skins socket policy file sockets states stored procedures stratus tools workflow

Coworkers

  • Casey Jackman
  • Sean Murphy

Family

  • Emily & CJ
  • Family Blog
  • Gary Dusbabek

Meta

  • Log in
  • Entries RSS
  • Comments RSS
  • WordPress.org

RSS FlexExamples

  • Setting a bitmap image fill on a Spark FormHeading control in Flex Hero
  • Setting the background alpha on a Spark FormHeading control in Flex Hero
  • Styling the error indicator on a Spark FomItem container in Flex Hero
  • Displaying the error indicator on a Spark FormItem container in Flex Hero
  • Styling the required indicator on a Spark FomItem container in Flex Hero

Spam Blocked

847 spam comments
blocked by
Akismet

Sponsored Links

JUICE Chat

BYU Adobe Users Group


Copyright © 2010 All Rights Reserved
Flexx Theme by iThemes
Powered by WordPress