AMFPHP is back!

Today is a wonderful day!

I have teamed up with Ariel Sommeria-klein to revive the legendary AMFPHP project that spawned a whole new dimension of Rich Internet Applications. Together we have brought AMFPHP 1.9 out of beta and made it compatible with PHP 5.3. We have also completely rewritten the AMFPHP service browser and we have several improvements planned for AMFPHP 2.0.

If you would like to help contribute to this great open-source application, please contact us.Vote in HexoSearch

Truly Cached Flex Modules

I was working on a project recently and I discovered (or possibly misunderstood – see disclaimer) that Flex does not cache modules correctly, or – at the very least – it does not do it effectively according to my tests.

My test was conducted using Flex SDK 3.4.0.9271, Firefox 3.5.7 and Flex Builder Professional 3.0.214193

According to this page (under the Preloading modules heading), Adobe asserts the following:

When a module is loaded by the Flex application for the first time, the module’s SWF file is transferred across the network and stored in the browser’s cache. If the Flex application unloads that module, but then later reloads it, there should be less wait time because Flash Player loads the module from the cache rather than across the network.

Module SWF files, like all SWF files, reside in the browser’s cache unless and until a user clears them. As a result, modules can be loaded by the main application across several sessions, reducing load time; but this depends on how frequently the browser’s cache is flushed.

I found the above claims to be demonstrably false, or at least just plain inefficient…

Let me qualify this:
According to my tests (conducted with my Firefox browser cache turned both on and off), i found that the application’s memory usage keeps growing exponentially when switching between two loaded modules. There was a variance (as you’d imagine) when the browser cache was left on but nonetheless, the memory usage keeps growing steadily.

In the code sample below, I have extended the functionality of the mx.modules.ModuleLoader class, and added a Dictionary (with a little logic) to manage the (ostensibly) effective caching of previously loaded modules. I have not tested this class extensively, but all the tests that I conducted seemed to produce a significant memory and speed improvement.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
package
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.utils.ByteArray;
	import flash.utils.Dictionary;
 
	import mx.events.FlexEvent;
	import mx.events.ModuleEvent;
	import mx.modules.IModuleInfo;
	import mx.modules.ModuleLoader;
	import mx.modules.ModuleManager;
 
	/**
	 * This class manages the loading, unloading and caching of Flex Modules
	 * This is a modified version of the mx.modules.ModuleLoader class
	 *
	 * @author Danny Kopping - danny@ria-coder.com
	 */
	public class CachedModuleLoader extends ModuleLoader
	{
		private var map:Dictionary = new Dictionary();
 
		private var _url:String = null;
		private var module:IModuleInfo;
		private var loadRequested:Boolean = false;
 
		public function CachedModuleLoader()
		{
			super();
		}
 
		override public function set url(value:String):void
		{
			if (value == _url)
				return;
 
			if (module)
			{
				module.removeEventListener(ModuleEvent.PROGRESS, moduleProgressHandler);
				module.removeEventListener(ModuleEvent.SETUP, moduleSetupHandler);
				module.removeEventListener(ModuleEvent.READY, moduleReadyHandler);
				module.removeEventListener(ModuleEvent.ERROR, moduleErrorHandler);
				module.removeEventListener(ModuleEvent.UNLOAD, moduleUnloadHandler);
 
				//module.release();
				module = null;
 
				if (child)
				{
					removeChild(child);
					//child = null;
				}
			}
 
			_url = value;
 
			dispatchEvent(new FlexEvent(FlexEvent.URL_CHANGED));
			removeAllChildren();
 
			if (_url != null && loadRequested)
			{
				if(!map[_url])
					loadModule();
				else
				{
 
					child = map[_url];
					addChild(child);
				}
			}
		}
 
		override public function get url():String
		{
			return _url;
		}
 
		override public function createComponentsFromDescriptors(recurse:Boolean = true):void
		{
			super.createComponentsFromDescriptors(recurse);
 
			loadRequested = true;
			loadModule();
		}
 
		override public function loadModule(url:String = null, bytes:ByteArray = null):void
		{
			if (url != null)
				_url = url;
 
			if (_url == null)
			{
				//trace("loadModule() - null url");
				return;
			}
 
			if (map[_url])
			{
				//trace("loadModule() - already created the child");
				return;
			}
 
			if (module)
			{
				//trace("loadModule() - load already initiated");
				return;
			}
 
			dispatchEvent(new FlexEvent(FlexEvent.LOADING));
 
			module = ModuleManager.getModule(_url);
 
			module.addEventListener(ModuleEvent.PROGRESS, moduleProgressHandler);
			module.addEventListener(ModuleEvent.SETUP, moduleSetupHandler);
			module.addEventListener(ModuleEvent.READY, moduleReadyHandler);
			module.addEventListener(ModuleEvent.ERROR, moduleErrorHandler);
			module.addEventListener(ModuleEvent.UNLOAD, moduleUnloadHandler);
 
			module.load(applicationDomain, null, bytes);
		}
 
		override public function unloadModule():void
		{
			if (child && contains(child))
			{
				removeChild(child);
				child = null;
			}
 
			if (module)
			{
				module.removeEventListener(ModuleEvent.PROGRESS, moduleProgressHandler);
				module.removeEventListener(ModuleEvent.SETUP, moduleSetupHandler);
				module.removeEventListener(ModuleEvent.READY, moduleReadyHandler);
				module.removeEventListener(ModuleEvent.ERROR, moduleErrorHandler);
 
				module.unload();
				module.removeEventListener(ModuleEvent.UNLOAD, moduleUnloadHandler);
				module = null;
			}
 
			if(map[_url])
			{
				delete map[_url];
			}
		}
 
		private function moduleProgressHandler(event:ModuleEvent):void
		{
			dispatchEvent(event);
		}
 
		private function moduleSetupHandler(event:ModuleEvent):void
		{
			// Not ready for creation yet, but can call factory.info().
 
			dispatchEvent(event);
		}
 
		private function moduleReadyHandler(event:ModuleEvent):void
		{
			child = module.factory.create() as DisplayObject;
			dispatchEvent(event);
 
			if (child)
			{
				var p:DisplayObjectContainer = parent;
				// p.removeChild(this);
				addChild(child);
 
				map[url] = child;//ModuleManager.getModule(_url);
				//trace(map + ":" + url + ":" + map[url]);
			}
		}
 
		private function moduleErrorHandler(event:ModuleEvent):void
		{
			unloadModule();
			dispatchEvent(event);
		}
 
		private function moduleUnloadHandler(event:ModuleEvent):void
		{
			dispatchEvent(event);
		}
	}
}

The usage of this class is exactly the same as the regular mx.modules.ModuleLoader class. I hope this helps!

Download this file: Download

**DISCLAIMER**
I know a bit about Flex modules from hours of obsessing over them, but i do not know everything. From my tests of the efficacy of the above code & explanation, i found that it reduces memory usage and increases the general usability of my Flex project; i could be very wrong on this topic, and if i am – please tell me. Maybe i’m just an idiot, but it seems to work…
Vote in HexoSearch

VirtualBox Guest Additions woes

Sigh… I wish that computers would just work all the time!

Today i booted my machine, launched my snazzy Linux virtual machine, proceeded to start my XAMPP stack and mount my shared folder so that my Linux VM can communicate with my host OS (sigh) Windows… This works 99.9% of the time, but to my horror:

/sbin/mount.vboxsf: mounting failed with the error: No such device

FUCK! What now? So i google and google, but nothing works…

I’m now bashing my head in frustration against my desk, spilling my coffee and rubbing my blood-red forehead, but that didn’t help either… I try mount VBoxGuestAdditions via VirtualBox, nothing. Nothing, nothing, nothing! NOTHING!

By now i’m getting so irritated that i’m drinking coffee after coffee and smoking vociferously… and then…. -cue lightbulb- if you can’t take the horse to the water (mount VBoxGuestAdditions in Linux), pick the fucker up and dump him in there (download the fucking thing from Linux and run it from Linux). So, i dig around looking for the URL, eventually find it, mount the ISO file (help instructions on mounting an ISO file in Linux here), run the installation script, reboot and IT WORKS!! …and the children lived happily ever after.Vote in HexoSearch

Flash Runtime implemented in JavaScript

Now this completely threw me… I can’t believe how one guy can be so skilled!

Flash Runtime in JavaScript

This is completely ridiculous… Tobias Schneider has written a JavaScript-based Flash runtime! Well, this gives Internet Explorer another thing to completely fuck up! Very impressed indeed…Vote in HexoSearch

Using a Custom Cursor in Flex with CSS

If you want to use a custom busyCursor animation in your Flex applications, you don’t need to do anything fancy… All you have to do is define one, solitary property in your CSS file:

CursorManager
{
	/* You can use PNGs, SWFs or JPEGs */
	busyCursor: Embed(source="path/to/your/resource");
}

To invoke the busy cursor, use the following line:

CursorManager.setBusyCursor();

…or you could set the showBusyCursor to true in your HTTPService or RemoteObject instances.Vote in HexoSearch

Converting GIF to SWF using Flash CS4

A typical ballache… You want to use one of the cool loaders from http://ajaxload.info/ in your Flash/Flex apps, but it only generates a GIF file… So you’ll scour the net for a GIF2SWF application and you’ll undoubtedly come across a few, but you don’t have to look any further than Flash CS4 – surprisingly!

All you have to do is open a GIF file in Flash CS4 and it’ll automatically pull it apart and convert it to frames, which can then be converted to a SWF file :)

Hope it helps you at some point!Vote in HexoSearch

AMFPHP Genie v0.2

AMFPHP Genie (0.2) is a simple tool to help you get shit done using Flex and AMFPHP.

Check out http://dannykopping.co.za/amfphp-genie/ for more information!Vote in HexoSearch

How's This For a Christmas Reading List?

I decided to splurge a bit of loot on some new books (which i hope i’ll have time to ever read) with the money from a few invoices clearing over the past couple of weeks.

Here’s what I ordered… There are some great books here which i hope you’ll consider looking at:

Vote in HexoSearch

QuickTip: asSQL Connection Problem with non-localhost hostname

asSQL is just great. It’s an ActionScript 3.0 library for connecting AIR applications to MySQL databases and it works brilliantly – when it works! Recently i gave it a shot again after an utter failure the last time i tried it… The library seems to be a port of the Java implementation of connecting to MySQL (Connections, Statements, Fields, etc). It takes a little getting used to but it’s an excellent library and i highly recommend it.

Background

My development environment is Windows-based (against my wishes… not enough loot or motivation for a Mac, lack of Adobe software on Linux) but i make up for this by running a virtual Linux installation in my Windows environment and networking the two together. It actually works really well, and the process for setting this up can be found here. To cut a very long and boring story short, essentially i cannot use localhost as your server when using this architecture since i have my LAMPP stack running on my Linux environment, so my “local server” can only be accessed using the IP address of the virtual system.

This complicates things a bit when trying to use asSQL to connect to my MySQL installation, because MySQL gets all paranoid and won’t accept connections from a foreign IP address (the IP of my Windows machine on which my AIR app is running)… To get around this, all you have to do is create a new user in MySQL (i used phpMyAdmin), set the user’s hostname to the IP address of your remote system (i’ve explained how to do this below) and you’re ready to rock and roll!

Solution

The first thing you need to do is get the IP address of the remote system…

Windows: Open the command line, type ipconfig
Linux & Mac: Open the terminal, type ifconfig

In this example, my IP address is 192.168.56.1

I’ll be using phpMyAdmin to fix the problem.

  1. Open phpMyAdmin and click on the Privileges tab.
  2. Click Add a new User
  3. Enter any username you like, paste the IP address obtained in the Host field and put in a password
  4. Under the Database for user panel, leave it at None
  5. Under the Global privileges, you can Check All

That should sort it out… Remember, this is HIGHLY insecure and should only be used on development environments and NOT production environments.Vote in HexoSearch

Let it Bloom

Hhmm… what’s this? ;)

http://www.letitbloom.com/Vote in HexoSearch