Loading a Remote Module into a Local App

08.14.2009

At work, we deal a lot with loading modules residing on remote servers into our applications running locally.  By locally I mean from a local path on our hard drives (the default Flex Builder run/debug settings), not on a local web server instance (localhost).  Depending on what type of module we are loading, we would normally use one of Flex’s built-in functions to load modules:

// Loading a language resource module:
resourceManager.loadResourceModule('http://aaronhardy.com/en_US_ResourceModule.swf');
 
// Loading a style module (e.g., a compiled font swf):
StyleManager.loadStyleDeclarations("http://aaronhardy.com/fonts/Astroid.swf");
 
// Loading miscellaneous modules:
var moduleInfo:IModuleInfo = ModuleManager.getModule('http://aaronhardy.com/MyModule.swf');
moduleInfo.load();

But these methods of importing modules result in errors like the following:

Unable to load resource module from http://aaronhardy.com/en_US_ResourceModule.swf
Error: Unable to load style(SWF is not a loadable module): http://aaronhardy.com/fonts/Astroid.swf.

One way to solve these issues is by using a crossdomain policy file on the server and then using a local apache instance to run the application. In my talks with the folks at Adobe, this seems to be their standard workflow which may be why we haven’t heard more about these problems. However, my workflow is to NOT have a local web hosting server running and I don’t want to have one running just to get around these errors.

As of Flex SDK 3.2, Adobe introduced a new parameter to IModuleInfo.load() for raw module bytes. As a result, we can now retrieve the remote module’s raw bytes using a URLLoader and then load them directly as a module using the IModuleInfo.load() method. The module is now part of the same security domain as the main application and no errors are thrown.

I’ve bundled up this logic into a class called ModuleMarshaller which is found below.

package com.aaronhardy
{
	import flash.events.ErrorEvent;
	import flash.events.Event;
	import flash.events.EventDispatcher;
	import flash.events.IOErrorEvent;
	import flash.events.SecurityErrorEvent;
	import flash.net.URLLoader;
	import flash.net.URLLoaderDataFormat;
	import flash.net.URLRequest;
	import flash.utils.ByteArray;
 
	import mx.events.ModuleEvent;
	import mx.modules.IModuleInfo;
	import mx.modules.ModuleManager;
 
	/**
	 * Dispatched once module loading is complete.
	 */
	[Event(name="ready", type="mx.events.ModuleEvent")]
 
	/**
	 * Dispatched if an error is encountered while loading the module.
	 */
	[Event(name="error", type="mx.events.ModuleEvent")]
 
	/**
	 * Loads a remote module.  Specifically, this is a workaround to allow an application being run
	 * locally (not using a web server) to load a module residing on a remote server.
	 * Usually, when attemping to do so using resourceManager.loadResourceModule(),
	 * StyleManager.loadStyleDeclarations(), or IModuleInfo.load() without passing in bytes
	 * results in errors such as the following:
	 *
	 * Error: Unable to load resource module from http://aaronhardy.com/locales/en_US.swf
	 * Error: Unable to load style(SWF is not a loadable module): http://aaronhardy.com/fonts/Astroid.swf.
	 *
	 * Be aware that any module imported with this class may have access to anything within
	 * your application's security sandbox.
	 */
	public class ModuleMarshaller extends EventDispatcher
	{
		protected var url:String;
 
		/**
		 * Appears to need to be stored in the class scope, otherwise garbage collection
		 * wipes out some of the event handling within IModuleInfo.
		 */
		protected var module:IModuleInfo;
 
		/**
		 * Constructor.
		 * @param url The external module to load into the application.
		 */
		public function ModuleMarshaller(url:String)
		{
			this.url = url;
		}
 
		/**
		 * Starts the loading process by loading in the module as bytes.
		 */
		public function loadModule():URLLoader
		{
			var urlRequest:URLRequest = new URLRequest(url);
			var urlLoader:URLLoader = new URLLoader(urlRequest);
			urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
			urlLoader.addEventListener(Event.COMPLETE, bytesLoadedHandler);
			urlLoader.addEventListener(IOErrorEvent.IO_ERROR, errorHandler);
			urlLoader.addEventListener(SecurityErrorEvent.SECURITY_ERROR, errorHandler);
			return urlLoader;
		}
 
		/**
		 * Once the module bytes are loaded, convert them back into a module within the
		 * application's security domain.
		 */
		protected function bytesLoadedHandler(event:Event):void
		{
			var styleModuleBytes:ByteArray = ByteArray(URLLoader(event.target).data);
			module = ModuleManager.getModule(url);
			module.addEventListener(ModuleEvent.READY, modReadyHandler);
			module.addEventListener(ModuleEvent.ERROR, errorHandler);
			module.load(null, null, styleModuleBytes);
		}
 
		/**
		 * Once the module information is loaded, use the factory to create an instance of the
		 * module.
		 */
		protected function modReadyHandler(event:ModuleEvent):void
		{
			ModuleManager.getModule(url).factory.create();
			dispatchEvent(event.clone());
		}
 
		/**
		 * Reports errors that may have occurred.
		 */
		protected function errorHandler(event:Event):void
		{
			if (event is ModuleEvent)
			{
				dispatchEvent(event.clone());
			}
			else if (event is ErrorEvent)
			{
				dispatchEvent(new ModuleEvent(ModuleEvent.ERROR, false, false, 0, 0,
					ErrorEvent(event).text));
			}
		}
 
	}
}

If you’re loading a style module, you may also want this class, which ensures the style module takes effect in your application immediately after the module is loaded.

package com.aaronhardy
{
	import mx.events.ModuleEvent;
 
	/**
	 * Provides the module marshalling functionality of ModuleMarshaller with the ability to
	 * update styles immediately after the module is loaded.
	 */
	public class StyleModuleMarshaller extends ModuleMarshaller
	{
		/**
		 * If true, forces an immediate update of the application's styles after the module is
		 * loaded.
		 */
		protected var updateStylesImmediately:Boolean = true;
 
		/**
		 * Constructor.
		 * @param url The external module to load into the application.
		 * @param update If true, forces an immediate update of the application's styles after
		 *        the module is loaded.
		 */
		public function StyleModuleMarshaller(url:String, updateStylesImmediately:Boolean=true)
		{
			super(url);
			this.updateStylesImmediately = updateStylesImmediately;
		}
 
		/**
		 * @inheritDoc
		 */
		override protected function modReadyHandler(event:ModuleEvent):void
		{
			ModuleManager.getModule(url).factory.create();
 
			if (updateStylesImmediately)
			{
				mx.core.Singleton.getInstance("mx.styles::IStyleManager2").styleDeclarationsChanged();
			}
 
			dispatchEvent(event.clone());
		}
	}
}

The Dirty Details

So, why does the ModuleMarshaller function without errors while Flex’s built-in classes do not? You might not care, but in case you do, here’s what I know–and don’t know. First, all the built-in functions end up using ModuleInfo.load() found in ModuleManager.as. This function uses a Loader to load in the contents of the module. When the loaded SWF file is accessible and ready for use (i.e., the Loader’s LoaderInfo object dispatches an Event.INIT event), ModuleInfo.initHandler() is called. In this function we have the following segment of code:

factoryInfo = new FactoryInfo();
 
try
{
    factoryInfo.factory = loader.content as IFlexModuleFactory;
}
catch(error:Error)
{
}
 
if (!factoryInfo.factory)
{
    var moduleEvent:ModuleEvent = new ModuleEvent(
        ModuleEvent.ERROR, event.bubbles, event.cancelable);
    moduleEvent.bytesLoaded = 0;
    moduleEvent.bytesTotal = 0;
    moduleEvent.errorText = "SWF is not a loadable module";
    dispatchEvent(moduleEvent);
    return;
}

Notice how it takes the content from the loader and attempt to cast it as an IFlexModuleFactory object. In this case, loader.content is NOT an IFlexModuleFactory instance, resulting in factoryInfo.factory being set to null. Later in the function, it checks to see if factoryInfo.factory is null. If it is, an error is thrown. This is the origin of the errors you see in your application.

So why does ModuleMarshaller work? When ModuleMarshaller makes this call:

module.load(null, null, styleModuleBytes);

It ultimately ends up in the same ModuleInfo.initHandler() function as we saw above. In both cases the Flex Builder debugger shows that loader.content is of type GeneratedResourceModule1975496797516746634_mx_core_FlexModuleFactory. However, when we load the module using the raw bytes loaded with a URLLoader, it IS able to cast the object as an IFlexModuleFactory object, resulting in a non-null factoryInfo.factory object and avoiding any error.

Why such similarity yet one is able to be cast while the other is not? I don’t know. That’s as far as I can go in my debugging quests with what’s open source. I assume it has to do with the Flash Player security but I haven’t read anything that would specifically explain why the content cannot be cast as an IFlexModuleFactory object when using built-in Flex module loading methods. If you can fill in the gap or have questions, please feel free to post a comment. Bon appetit!

Tags: , , , , , , , , , , , , , , ,


Comments

09.04.2009 / Greg said:

You save our ass dude.

09.08.2009 / Jamie said:

Hi Arron, I tried using your marshaller, but got the following error in the bytesLoaderHandler in the bytesLoadedHandler( ). Any idea what this is about?

VerifyError: Error #1053: illegal override of FlexModuleFactory in mx.core.FlexModuleFactory

09.08.2009 / Aaron Hardy said:

Jamie, can you tell me what version of the SDK you’re using? It for sure needs to be 3.2 or later.

09.08.2009 / Jamie said:

Oh such quick reply!

I m using skd 3.4.

BTW, since we are at this, what am I supposed to do oce I get the urlLoader from loadModule()? I was expecting it to return nth in that method, since the Module is already loaded and stuff.

09.08.2009 / Jamie said:

Seems like the error is gone now (I didnt reli do anything..just clean, change the remote, clean…etc). But I am still puzzled as to what I am supposed to do with the URLLoader returned. Like I mentioned before, I thought module.load(..) in bytesLoadedHandler helps u load the remote module. So why do we still need the URLLoader from loadModule? What about ModuleManager.getModule…() in modReadyHandler? Whats the purpose of creating a Module objeect?

09.09.2009 / lunven said:

谢谢版主,根据你的思路和代码,我在SDK3.2里面成功实现了。

09.09.2009 / Aaron Hardy said:

@Jamie

You shouldn’t need to do anything with the URLLoader. The only reason it is returned is in case you want to show users loading progress or something.

There are two steps here:
(1) Retrieve the bytes from the server. This is done in loadModule().
(2) Take the bytes and make a module out of it. This is done in bytesLoadedHandler().

Regarding “ModuleManager.getModule(url).factory.create();”–you may not need it in your particular case. I believe the reason I put it there is because when attempting to load a module that’s a style module I needed to create an instance of the module before I could have it take effect in my application. In other words, I think it’s important to have it in StyleModuleMarshaller’s modReadyHandler() but probably not in ModuleMarshaller. Thanks for the heads up. I’ll make sure that’s the case when I get some time. If you test out ModuleMarshaller without that line and verify that everything works as expected, let me know and that will same me a bit of time. Thanks again.

09.09.2009 / Jamie said:

Its very strange. I basically just call loadModule() when the creatioinComplete event is fired, create the ModuleMarshaller instance with the location of the swf, and nothing happens after I run it. No exceptions no nth.

Did I do sth wrong? How come everybody else seems to be able to use it?

09.10.2009 / Aaron Hardy said:

@Jamie

Create an instance of the ModuleMarshaller and pass in the url of your module. Then call loadModule() on the instance. Set a debugging breakpoint on bytesLoadedHandler() and modReadyHandler(). Run your application. Does the debugger get to the bytesLoadedHandler() function? Does it get to the modReadyHandler() function?

If both answers are yes, maybe there’s a misunderstanding of what the class actually does. The class is only to get the module into the application. It does NOT add the module to the display list. In other words, if your module has some sort of user interface and all you have done is what I’ve outlined above, the module’s UI is not going to show up in the app. In order to do that, you have to get the module into the app using ModuleMarshaller, then retrieve an instance of the module using ModuleManager.getModule(url).factory.create(); and then add the module instance as a child of whatever component you want it displayed inside. Hopefully that helps.

09.10.2009 / Jamie said:

Yeah, I also did a vbox.addChild(myModule) and its working ok now. Thanks Aaron! Now I can get pass the first step of my “proof of concept”.

Do you know if there’s a better way to swap modules in-and-out? The reason I am asking this is I am trying to do a my proof of concept of updating my AIR application’s modules with updates from my remote server. Our company has very strict security policy so users were unable to freely download and install anything (hence can’t use the Updater class to re-download the whole AIR file when we do releases or fixes). We are using the Cairngorm framework currently and if this is successful we will prolly have to re-organize our code structure.

I was also wondering, if i unload then reload the new swf from the remote server (to get the latest updates of a particular modules), modules and all those Model, ModelLocator from my Cairngorm can still talk to each other right? Of course, users will have to re-login the app in order to trigger some kind of retrieval from server, but, modules can still talk to other modules w/o problems?

09.11.2009 / Aaron Hardy said:

@Jamie,

Glad that worked for you.

I haven’t really had to do what you’re having to do with modules, but it sounds reasonable. You may already be doing this, but I would try to download and store the modules locally so you don’t have to download them each time the app runs. Then every time the app starts up just query the server to see if there are module updates.

I know Cairngorm has some issues when using it from within modules but I haven’t messed with it for probably a year and a half. I was able to get it to work back then but I’m not sure what changes they’ve made since or what other developers have come up with for workarounds. I would imagine other people have done what you’re trying to do so push on. Your modules should be able to talk to each other okay. To decrease your chances of running into a snag, make sure your application and modules are built with the same SDK version. If you’re not going to be able to build everything with the same SDK, you’ll want to be aware of the Marshall Plan: http://opensource.adobe.com/wiki/display/flexsdk/Marshall+Plan

Good luck!

09.14.2009 / Jamie said:

Thanks Aaron for your reply. I am pretty new with AIR and modules so questions start popping up as I code along.

I read somewhere that after I downlaod the modules it is going to save a copy inside the app-storage folder somewhere??? Right now I am really just creating an instance of the Module I retrieved from the server so I will have to re-download everytime I launch the app.

09.21.2009 / Bill Walker said:

I have a Flex web application that I’ve been building with Flex Builder and the default Flex SDK 3.2. I just downloaded the Flex 3.4 SDK to try it (although I can’t find specific release notes on what may have been changed). When I rebuild with the new SDK, I get the “VerifyError: Error #1053: illegal override of FlexModuleFactory” exception mentioned above. It occurs when I try to load a runtime stylesheet:

var styleEvents:IEventDispatcher = StyleManager.loadStyleDeclarations(stylePath);

// register event listeners to monitor progress
styleEvents.addEventListener( StyleEvent.PROGRESS, updateStyleProgress );
styleEvents.addEventListener( StyleEvent.COMPLETE, updateStyleProgress );
styleEvents.addEventListener( StyleEvent.ERROR, updateStyleProgress );

and happens in the event handler. Any idea what changed between 3.2 and 3.4 that would cause this?

09.21.2009 / Aaron Hardy said:

@Bill
Did you try cleaning your project? That fixed it for Jamie.

12.01.2009 / Ben said:

Whenever I add these classes to my project, it first tells me that StyleModuleMarshaller.as can’t find “ModuleManager”, so I have to import it, then it gets stuck on line 38 where you use Singleton, throwing the error “Access of undefined property mx”

I’m using SDK 3.4, but I have also tried 3.2 to no avail. Any ideas?

12.01.2009 / Aaron Hardy said:

@Ben
I believe you should only have to import the ModuleManager class if the two classes aren’t in the same package. In regard to the error being thrown, try adding “import mx.core.Singleton” at the top of the class. If you’re still having issues, search “Access of undefined property mx” in google and you’ll get a lot of results that may pertain to your situation. Good luck and post back if you get it working or run into additional problems.

12.02.2009 / Ben said:

Hey Jamie, thanks for your response. It turns out that if I add “import mx.core.Singleton;” to the top of StyleManagerMarshaller class, it removes the odd “mx not found” error I previously mentioned, allowing me to compile everything.

Now I’m faced with another issue. When I try to load a style module, the bytesLoadedHandler function gets called successuflly, but the modReadyHandler never gets called. It seems, in fact, that the module object is not dispatching ModuleEvents of any kind, or if it is, I can’t listen for them successfully.

But, I’m pretty sure that the module is successfully being loaded in the end, because in my output box is says [SWF] /myDirectory/mySwf.swf – 999 bytes after decompression. I’m assuming this means it’s being loaded, even though the filename of the SWF it displays happens to be the file name of the loading SWF, but the bytes are significantly smaller.

12.02.2009 / Ben said:

Sorry not Jamie, I meant Aaron :(

12.02.2009 / Aaron Hardy said:

@Ben
The weird mx not found error can sometimes occur if your application has access to two different classes called Singleton. I think there are a few other scenarios where it can come up but that’s the one I’m acquainted with. The error itself is kind of cryptic though.

As for modReadyHandler never getting called–I’ve never ran into that. Can you put a breakpoint on errorHandler() and make sure it isn’t being called?

12.02.2009 / Ben said:

@Aaron

Yes, I put a breakpoint in the errorHandler but that’s never getting called either.

I’m noticing that when I monitor the “ready” variable in the module class, it never gets set to true. Not sure why this would be.

12.02.2009 / Ben said:

Update: It appears that this is a crossdomain issue. I can use these classes on a style module that exists locally and it works perfectly (styles get updated automatically, etc). But when I use it locally to access a style module on a different domain, or even upload it and run it from one domain and load from yet another domain, it fails to work. The module is never “ready”.

12.03.2009 / Ben said:

I’ve narrowed the issue down to the loader instance of the ModuleManager class….when it calls the loadBytes method, none of the events for the loader ever get fired. Honestly at this point I’m stumped. It’s definitely not a problem inside your classes, so thanks for all of your help :)

12.03.2009 / Aaron Hardy said:

@Ben

Thanks for the update. As you can probably tell from the latter portion of my post, I’m stumped myself sometimes when it comes to module loading within the framework. I’ll keep my eye out for the symptoms you described and post back if I find anything significant.

12.17.2009 / Mittul said:

Hey Aaron,
I am new to flex Air.Is the module being saved anywhere locally?
How is it importing to the same sandbox?
Thanks

12.18.2009 / Aaron Hardy said:

@Mittul
The module doesn’t get saved locally in the traditional sense. It just gets loaded into memory for the user’s session. I’m not really certain where to start answering your second question. It’s a fairly broad and in-depth topic, but if you have a more specific question I’ll do what I can to answer. Thanks for stopping by.

12.23.2009 / Mittul said:

Thanks for a prompt reply.If its loaded in the session,can the module access the local file system??

02.09.2010 / Vinny Savory said:

Hi Aaron, great post!
I’m wondering how easy it is to get my flex app (which sits on a remote server), to load a style swf from a different remote server? Is this where I need to have a cross-domain.xml?
Thanks!

09.14.2010 / ram sambamurthy said:

i used your code to try loading a language resource module for which i would usually use the resourceManager.loadResourceModule() function. i received the following error at the dispatchEvent(event.clone()) of function modReadyHandler()

TypeError: Error #1034: Type Coercion failed: cannot convert mx.events::ModuleEvent@1c61aa9 to mx.events.ResourceEvent.

code:
private var private var moduleMarshaller: ModuleMarshaller
moduleMarshaller = new ModuleMarshaller(“app-storage:/lang_de_DE.swf”)
moduleMarshaller.addEventListener(“ready”, UpdateLanguageDescriptors_onLoadComplete)
moduleMarshaller.addEventListener(“error”, UpdateLanguageDescriptors_onLoadError)
moduleMarshaller.loadModule()

i’m really lost with how to apply your code to loading a language resource module where i use the .properties file to create strings in other languages, and mxmlc compiled into a lang_xx_XX.swf module. after loading the resource module with resourceManager.loadResourceModule() i would do a resourceManager.getString() to obtain the other language’s string values.

any help will be greatly appreciated. thanks

09.16.2010 / ram sambamurthy said:

OK, so I figured it out, and am presenting it here for the benefit of others who want to load a language resource module compiled into an .swf using Aaron’s ModuleMarshaller

import flash.events.IEventDispatcher;
import mx.events.ModuleEvent;
import mx.events.ResourceEvent;
import mx.resources.IResourceManager;
import mx.resources.ResourceManager;
import com.aaronhardy.ModuleMarshaller

private var moduleMarshaller: ModuleMarshaller
private var resourceManager : IResourceManager = ResourceManager.getInstance()
private var eventDispatcher: IEventDispatcher

private function SomeFunction(): void {}
// create an ModuleMarshaller instance and open a resource module
moduleMarshaller = new ModuleMarshaller(“app-storage:/lang_ms_MY.swf”)

// add eventlisteners to respond to ModuleMarhsaller’s “ready” and “error events”
moduleMarshaller.addEventListener(“ready”, moduleMarshaller_onReady)
moduleMarshaller.addEventListener(“error”, moduleMarshaller_onError)
}

// after ModuleMarshaller loads the module successfully,
// we can now call the ResourceManager to load the language resource
private function moduleMarshaller_onReady(evt: ModuleEvent): void {

// now that ModuleMarshaller has done its job, we can kill event listeners
moduleMarshaller.removeEventListener(“ready”, moduleMarshaller_onReady)
moduleMarshaller.removeEventListener(“error”, moduleMarshaller_onError)
moduleMarshaller = null

// We finally achieve our mission of loading the language resource module.
// The ResourceManager uses the same URL as a handle for ModuleManager
// to identify the previously marshalled module
eventDispatcher = resourceManager.loadResourceModule(“app-storage:/lang_ms_MY.swf”, false) // false or true, depends on your functionality
eventDispatcher.addEventListener(ResourceEvent.COMPLETE, UpdateLanguageDescriptors_onLoadComplete)
eventDispatcher.addEventListener(ResourceEvent.ERROR, UpdateLanguageDescriptors_onLoadError)
}

// respond to any errors thrown by the marshaller
private function moduleMarshaller_onError(evt: ModuleEvent): void {

// kill event listeners
moduleMarshaller.removeEventListener(“ready”, moduleMarshaller_onReady)
moduleMarshaller.removeEventListener(“error”, moduleMarshaller_onError)
moduleMarshaller = null

// do whatever else you wanna do
}

// now you can get going with your code
private function UpdateLanguageDescriptors_onLoadComplete(evt: ResourceEvent): void {

eventDispatcher.removeEventListener(ResourceEvent.COMPLETE, UpdateLanguageDescriptors_onLoadComplete)
eventDispatcher.removeEventListener(ResourceEvent.ERROR, UpdateLanguageDescriptors_onLoadError)

// your code
}

// ouch!
private function UpdateLanguageDescriptors_onLoadError(evt: ResourceEvent): void {

eventDispatcher.removeEventListener(ResourceEvent.COMPLETE, UpdateLanguageDescriptors_onLoadComplete)
eventDispatcher.removeEventListener(ResourceEvent.ERROR, UpdateLanguageDescriptors_onLoadError)

// your code
}

09.16.2010 / Aaron Hardy said:

Ram, that’s awesome! I’m glad you got it working and posted the answer. Sorry I couldn’t help much.

10.11.2010 / Isaac said:

Hi iAaron,

The StyleModuleMarshaller never worked in AIR, have tried several options, fonts are not applied.
Any clue?

10.11.2010 / Aaron Hardy said:

Isaac,

Not w/o some details. Are you getting an error? Have you debugged and made sure the bytes are being loaded? What SDK are you using? How are you creating your font SWFs? Can you get the fonts to load without using StyleModuleMarshaller and serving up the fonts locally instead of from a remote server? That’d be a good start…

01.07.2011 / Vitor Monteiro said:

The best solution I’ve seen so far.
Aaron 1 – Adobe 0!

03.24.2011 / Gaius said:

Seriously, thank-you.
I cannot believe Adobe didn’t anticipate such a basic need as using their stylesheets for AIR…

03.29.2011 / Harshal said:

Thanks a Lot Aaron, works like a charm…
but there are some cases when it loads the module, it does not fire the READY event internally, i.e it will not go in modReadyHandler function. I just run it again and it works, that’s the sole point of confusion…

Also Any particular reason, why url is taken in constructor ? can’t I take it in the loadModule function itself, and set it to class variable ?

Apart from that, works perfect for me, thanks a lot.

04.02.2011 / Aaron Hardy said:

@Vitor, @Gaius Thanks for your thanks. Glad I could help.

@Harshal I can’t say I ever remember seeing the behavior you’re describing. Maybe someone else on here has? As for the url in the constructor, it’s not really necessary to do it that way and there shouldn’t be a problem passing it through loadModule().

05.04.2011 / Kishore said:

Hi ,

Please can u post the example that how to cal remote CSS , i have included the above two classes in to my application(ModuleMarshell & StyleModuleMarshell) . but not able to cal the CSS swf remotly. if you send any example how to implement it will be very very very help full to me.Please help me in this

Thanks
J Kishore

05.18.2011 / Will said:

Thank you so much for this info.

If I want to load an external style swf, do I just create an instance of StyleModuleMarshaller and call loadModule()? Or do I still need to call IStyleManager2.loadStyleDeclarations() afterwards, for example, after module ready?

06.21.2011 / Davis said:

Dang! I just got hit with this myself, and debugged it into the same area you indicate in ModuleManager.as using the latest and greatest SDK 4.5.

FYI: I just posted this question to flexcoders mailing list, but I’m not holding out for a solid answer…

http://tech.groups.yahoo.com/group/flexcoders/message/162771

It sux that this hasn’t been fixed. Note that in my case, I’m trying to load a SWF from the local file system into an Air app. Hence: it would seem bizarre if security constraints would be the root cause of the issue here.

06.21.2011 / Davis said:

Follow-up — I was able to solve this particular issue, by loading the module in actionscript passing the ByteArray. I did not end up using your ModuleMarshaller, but I assume we work around the same problem by passing in the byte array. For example:

// load the swf into a byte array
var bytes:ByteArray = new ByteArray();
var urlLoader:URLLoader = new URLLoader();
urlLoader.dataFormat = URLLoaderDataFormat.BINARY;
urlLoader.addEventListener(Event.COMPLETE,
function(evt:Event):void {
bytes = ByteArray(evt.target.data);
moduleLoader.loadModule(url, bytes);
}
);

// LOAD IT
urlLoader.load(new URLRequest(url));

But this *STILL* doesn’t solve all the issues. The ModuleEvent.READY event does not always fire. It seems like something gets garbage collected, and the event is lost most of the time. If I step debug it into the framework code, I can make it work, but if I run w/o breakpoints the event never fires.

There have been lots of bugs filed against Adobe for this:

http://bugs.adobe.com/jira/browse/SDK-14669
http://bugs.adobe.com/jira/browse/SDK-24343

Those are just two — I think there are others. I’m aghast that this issue still hasn’t been fixed in 4.5 SDK. Here’s the workaround:

Instead of adding an event listener to the ModuleLoader for ModuleEvent.READY, add it to the IModuleInfo object in the ModuleEvent.SETUP…example:

var moduleLoader:ModuleLoader = new ModuleLoader();

// DON’T DO THE FIRST ONE
//moduleLoader.addEventListener(ModuleEvent.READY, onReady);
moduleLoader.addEventListener(ModuleEvent.ERROR, onError);
moduleLoader.addEventListener(ModuleEvent.PROGRESS, onProgress);
moduleLoader.addEventListener(ModuleEvent.SETUP, onSetup);
moduleLoader.addEventListener(ModuleEvent.UNLOAD, onUnload);
moduleLoader.addEventListener(mx.events.FlexEvent.LOADING, onLoading);
moduleLoader.addEventListener(mx.events.FlexEvent.URL_CHANGED, onUrlChanged);

Now, look at the onSetup handler:

private var moduleInfo:IModuleInfo; // save a ref so it won’t get GC’d

private function onSetup(evt:ModuleEvent):void {
LOG.info(“Module was initialized”);
moduleInfo = evt.module;
moduleInfo.addEventListener(ModuleEvent.READY, onReady);
moduleInfo.addEventListener(ModuleEvent.ERROR, onError);
}

Now, ModuleEvent.READY seems to always fire consistently, and then you can add the content as a child to some other container:

private function onReady(evt:ModuleEvent):void {
LOG.info(“Module is ready”);
var info:IModuleInfo = evt.module;
var obj:Object = info.factory.create();
border.addElement(obj as IVisualElement);
}

I hope this helps somebody! I just wasted a lot of hours on it.

11.11.2011 / Dmitry said:

“However, my workflow is to NOT have a local web hosting server running and I don’t want to have one running just to get around these errors.”

Don’t you need one (Apache or Tomcat) to load your modules or resource bundles anyway?

11.11.2011 / Aaron Hardy said:

No, I wanted to load them from a remote server. How to do so is the topic of this post.

11.11.2011 / Dmitry said:

So, then why not to go with crossdomain.xml solution? Much easier then code around module.load(), right? I’m just not getting rationale why webserver + crossdomain.xml is not a viable solution for your case.

11.11.2011 / Dmitry said:

“Likewise it doesn’t call a crossdomain automatically either.” – Good catch. Thanks, it’s now clear.
I’ll do the same, while AIR is still breathing…

01.06.2012 / Chuck said:

I wasn’t able to get StyleModuleMarshaller to actually apply the loaded styles to the app. I found I needed to add a call to loadStyleDeclarations in the modReadyHandler override in order to actually get them applied.

// Even though the url is passed in, it’s not going to re-fetch/load the module,
// so there’s no unnecessary performance penalty.
mx.core.Singleton.getInstance(“mx.styles::IStyleManager2”).loadStyleDeclarations2(url, updateStylesImmediately, null, null);

As I note in the comment, I debugged and verified that the module isn’t actually being refetched, since the ModuleManager singleton maintains a map of all requested URLs and their loaded modules.

Just floating this out there in case anyone else is trying to use StyleModuleMarshaller without success.

01.12.2012 / Maurice said:

I am trying to use StyleModuleMarshallers to load an external swf with a font embedded (css converted into swf) in Flex 4.5.

In the modReadyHandler i can see it gets the font (event.currentTarget.factory._info.fonts).

Could you tell me how i can then do a Font.registerFont(font)?

Any help would be appreciated. Also the strange thing, in debug locally it works but when i put the app online it generates an error: Error #2044: IOErrorEvent:. text=Error #2124: Loaded file is unknown type.

Thanx!

01.12.2012 / Aaron Hardy said:

Hey Maurice. I remember working with runtime fonts a couple years back and I ran into a bunch of problems. Not so much loading the font, but getting the font swf right and registering it correctly. You can see a chronology of my battle here:

http://forums.adobe.com/thread/465439

I know I used several articles to help me figure it out. One of them is no longer available. I think this is a good one: http://bit.ly/4FyegT

I hope you find what you need.


Leave a Comment

Your email address is required but will not be published.




Comment