Futurescale, Inc. PureMVC Home

The PureMVC Framework Code at the Speed of Thought


Over 10 years of community discussion and knowledge are maintained here as a read-only archive.

New discussions should be taken up in issues on the appropriate projects at https://github.com/PureMVC

Pages: 1 [2] 3
Print
Author Topic: Fabrication 0.4 released  (Read 52805 times)
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #15 on: October 21, 2008, 05:24:19 »

Ruben,

I have fixed the FlexModule to allow loading of modules directly using the ModuleManager. The fix is available from the google code page[1] version 0.4.1. I ended up not requiring the custom event because the ModuleEvent.READY does the job nicely.

In your code's ready handler you will need to do something like,

:
protected function onPluginCreated (event:PluginEvent) : void {
   event.plugin.defaultRouteAddress = applicationAddress;
   event.plugin.router = applicationRouter;
}

The accept router call won't be needed. The new fabricator takes care of that. I duplicated the simple_routing example to simple_routing_with_module_manager[2]. You can refer to this example for further details. The key changes are in ModulesContainerMediator#moduleReadyListener. The rest are minor changes to remove the cast to ModuleLoader.

peace,
darshan

[1] : http://code.google.com/p/fabrication/downloads/list
[2] : http://fabrication.googlecode.com/svn/examples/simple_routing_with_module_manager
Logged
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« Reply #16 on: October 21, 2008, 10:48:36 »

Quick question about the TO: field on routeNotification. I'm getting some weird behavior. I have a module called DataService, which has an instance name of DataService0 according to moduleAddres.getInstance(). However when I ask the shell to send a route to DataService/DataService0 the module never gets it. However if I use DataService/* _OR_ DataService/DataService0/INPUT I recieve the message. Any ideas? I've tried tracing it all the way to the junction call and can't see anying that would cause this.

Should there be entries in the junction.inputPipes array for more than Shell/Shell0/INPUT and DataService/DataService0/INPUT?
Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #17 on: October 21, 2008, 11:27:50 »

Well spotted. Some of the internal piping is showing there. I think I need to add a check in the RouteNotificationCommand. Something like,

:
if (to != "*" && !(new RegExp("\/INPUT").test(to)) {
   to += "/INPUT";
}

I will verify everything works and check this in tomorrow. Please use the explicit /INPUT for the moment.

peace,
darshan
Logged
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« Reply #18 on: October 21, 2008, 11:57:18 »

Well spotted. Some of the internal piping is showing there. I think I need to add a check in the RouteNotificationCommand. Something like,

:
if (to != "*" && !(new RegExp("\/INPUT").test(to)) {
   to += "/INPUT";
}

I will verify everything works and check this in tomorrow. Please use the explicit /INPUT for the moment.

peace,
darshan

No problem, glad to help. For now I'm just using MyMOdule.moduleAddress.getInputName() to send to a specific instance.

Again, I have to say great job! It's taking a bit to learn, but it's a very powerful utility and I thank you for sharing it. It's going to save me a lot of time once I get it fully implemented into my project.
Logged
freakinruben
Newbie
*
Posts: 4



View Profile Email
« Reply #19 on: October 21, 2008, 02:10:54 »

Wow that's quick Darshan! Thanks. You've really done a great job with 0.4  ;D.

I've been busy refactoring some of my own code and finally everything is working again with 0.41 so that's quite a relief.

One question I have so far is about the new FabricationProxy.
I have several modules who's proxy extend the same base proxy, named "DataProxy". When they extend "DataProxy", they don't change the ProxyName because I wanted them to share a couple of Commands which fill the "DataProxy".

So in my situation you have "Module1Proxy" and "Module2Proxy", which both extend "DataProxy" and therefore are both registered as "DataProxy" (within their own core). When one of them get's filled they send a notification "DATA_SET", but because the registered name ("DataProxy") is not the same as the classname ("Module1Proxy"), the fabricationproxy class changes the notificationname to "DataProxy/DATA_SET". So the notification never reaches my mediator...

So my question is, why does the notificationname get changed when the registered proxyname is not same as the classname?


Here are some other small comments, but nothing really big so far.
A small addition to the FlexModule class is to add some event metadata
:
[Event(type="org.puremvc.as3.multicore.utilities.fabrication.events.FabricatorEvent", name="fabricationCreated")]
[Event(type="org.puremvc.as3.multicore.utilities.fabrication.events.FabricatorEvent", name="fabricationRemoved")]

Although I've seen a "removeEventListener" for every "addEventlistener", another addition I've made myself is to add "false, 0, true" to every addEventListener call to make sure those references won't cause any memoryproblems... (yes I know, not really necessary)

Tnx,
Ruben
Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #20 on: October 21, 2008, 10:24:04 »

Hey Ruben,

I've been busy refactoring some of my own code and finally everything is working again with 0.41 so that's quite a relief.
Its a relief for me too that you got it working. I was afraid I had gone a little overboard with some of the features. ;-)

One question I have so far is about the new FabricationProxy.
I have several modules who's proxy extend the same base proxy, named "DataProxy". When they extend "DataProxy", they don't change the ProxyName because I wanted them to share a couple of Commands which fill the "DataProxy".

...
So my question is, why does the notificationname get changed when the registered proxyname is not same as the classname?

Good point. This is by design, and works well with the Reflexive Notification interests. I am using some of my Proxies in a similar manner,i.e.:- Multiple instances of the same proxy with a different proxy name. However since the Proxy is the same the notification name is common between them. This is a conflict with the PureMVC's notification system, where each notification name must be unique. Hence the 2 part notification name expansion.

There are 2 ways to handle such notifications. The first and easiest is using respondToProxyName. If you name your handler like respondToMyProxy, Whenever the proxy named MyProxy, dispatches a notification this handler will be called automatically. The fabrication mediator picks this notification interest up using reflection. So in your case the code would be,

:
public function respondToModule1Proxy(note:INotification):void {

}

// and

public function respondToModule2Proxy(note:INotification):void {

}

The convention is to have your proxy named something like MyCustom<Proxy>. Any respondTo method with a Proxy in its name is interpreted as a notification interest in that proxy.

The second way is a little involved. In this approach you need to qualify the notification explicitly using qualifyNotificationInterests in a mediator. So in your case if you need to do something like,

:
override public function qualifyNotificationInterests():Object {
   return {dataSet : "Module1Proxy"};
}

public function respondToDataSet(note:INotification):void {

}

This does the expansion of the DATA_SET notification from Module1Proxy to Module1Proxy/dataSet. So you can use respondToDataSet. Hope this clarifies things a bit. I haven't figured yet out how to qualify the same note name with different Proxies without method overloading. So if you might have to stick to the first approach.

If this is something you would like to avoid you can use,

:
override public function getNotificationName(note:String):String {
   return note;
};

Should I add a flag like expansion:Boolean to turn this behaviour on or off?

Here are some other small comments, but nothing really big so far.
A small addition to the FlexModule class is to add some event metadata
Sure, I will do the same.

Although I've seen a "removeEventListener" for every "addEventlistener", another addition I've made myself is to add "false, 0, true" to every addEventListener call to make sure those references won't cause any memoryproblems... (yes I know, not really necessary)
Can you tell me where you would like to add this? In general most of the classes in Fabrication implement the IDisposable interface to clean up before they are deleted. My preference is towards explicit removeEventListener and overall cleanup. If an object is no longer used I tend to delete it and any references to it in the dispose method.

peace,
darshan
Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #21 on: October 22, 2008, 12:55:02 »

Jason,

I have fixed the routeNotification issue you encountered earlier. You can now use module.moduleAddress directly in the to, or ModuleName/InstanceName without the /INPUT.

Ruben,

I added the expansion property to FabricationProxy, that i mentioned earlier.

The version is updated to 0.4.2[1].

peace,
darshan

[1] : http://code.google.com/p/fabrication/downloads/list
Logged
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« Reply #22 on: October 22, 2008, 06:06:56 »

Jason,

I have fixed the routeNotification issue you encountered earlier. You can now use module.moduleAddress directly in the to, or ModuleName/InstanceName without the /INPUT.

Awesome, thanks.

BTW, I know Ruben mentioned listener clean-up a bit... do you happen to have any examples on how to ensure a module loaded using Fabrication is properly removed. Like what methods need to be called prior to deleting. I had a quick scan of the examples and didn't see anything.
Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #23 on: October 22, 2008, 06:24:56 »

I haven't got an example of this yet, but its mostly one call. You need to call module.dispose() to cleanup a module. That call does disposal of all the internals like the facade, controller, view, commands, etc. I am also dispatching a SHUTDOWN notification prior to the module's disposable. If you wish to perform any custom cleanup in the module before disposable, you can register a command with this notification.

As a general practice most of the classes implement the IDisposable interface. So when you extend any class you should override the dispose method, implement any custom cleanup if needed and then call super.dispose().

peace,
darshan
« Last Edit: October 22, 2008, 06:28:47 by Darshan Sawardekar » Logged
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« Reply #24 on: October 24, 2008, 07:14:31 »

I hope I'm not driving you nuts with all the questions... but I'm not sure if this is intentional or something I'm missing. Right now I have a Shell App and two modules, Logger and DataService. Everything is working as it should except that messages sent from DataService can only be listened for by Shell and not by Logger. Is there a method I'm missing somewhere to lay pipes Module-to-module? I was under the impression that because they all use the same Router that it wasn't necessary but it seems it I don't give a TO: in the routeMessage then messages from DataService are always routed to Shell/Shell0/INPUT where I was hoping it was sending globally.


UPDATE: Looks like I may have found another bug. In RouteNotificationCommand's execute if the to: is set to "*" the if/else statements end up converting "*" to "*/INPUT" which then gets ignored by everything. When I change these if/else statements to skip doing anything to "*" everything seems to work and my modules all get notified.

It's the line below that converts "*" to "*/INPUT":
:
else if (to is String && !allInstanceRegExp.test(to as String)  && !ModuleAddress.inputSuffixRegExp.test(to as String)) {
to = (to as String) + ModuleAddress.INPUT_SUFFIX;

I just added && to != "*" to the end of that and everything works
:
} else if (to is String && !allInstanceRegExp.test(to as String)  && !ModuleAddress.inputSuffixRegExp.test(to as String) && to != "*") {
« Last Edit: October 24, 2008, 07:53:39 by jasonmac » Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #25 on: October 24, 2008, 09:24:08 »

Hey Jason,

This is great feedback. And your diagnosis and fix is correct. I will check this into the repo tomorrow.

peace,
darshan
Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #26 on: October 29, 2008, 07:27:43 »

Hey Jason,

I have committed this fix to the svn and updated to 0.4.3. Sorry it took so long.

peace,
darshan
Logged
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« Reply #27 on: November 04, 2008, 07:21:32 »

A little more to add. Ive being working on cleaning up my "modules" for removal and noticed that calling the FabricationFacades.dispose() method on a module does a nice job chaining calls for removal of references. One thing I found interesting was that it checks whether the view implements an IDisposable interface, but Fabrication uses the base PMVC view. I found this to be a great idea since I have been trying to find a way to dispose of a number of dynamic mediators but can't get a reference to the views mediatorMap since it's a protected property of the View. So I went ahead and added a FabricationView to implement the dispose method on so I could be sure to dispose of all currently referenced mediators. I'll leave it up to you if you want to add it or not, but here it is. I'm debating doing the same for the Model since I really want to clear EVERYTHING before unloading a module, including any stray StartupManager references. This seems to give a nice clean way to run the dispose() and onRemove() methods for all for proxies, mediators and commands. Plus I still have the option of using a shutdown command based on the notification to do any other clean-up.

Here's the code for the view.


FabricationView
:
/**
 * Copyright (C) 2008 Darshan Sawardekar.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

 package org.puremvc.as3.multicore.utilities.fabrication.core {
import org.puremvc.as3.multicore.core.View;
import org.puremvc.as3.multicore.interfaces.IMediator;
import org.puremvc.as3.multicore.utilities.fabrication.interfaces.IDisposable;

/**
* FabricationView is the custom view used by fabrication.
* It allows for the disposal of all currently registered mediators
* at once.
* @author Jason MacDonald (Jason.MacDonald@vfmii.com)
*/
public class FabricationView extends View implements IDisposable {

/**
* Creates and returns the multiton instance of the view for
* the specified multiton key.
*
* @param key The multiton key whose view is to be retrieved.
*/
static public function getInstance(key:String):FabricationView {
trace(instanceMap[key]);
if (instanceMap[key] == null) {
instanceMap[key] = new FabricationView(key);
}

return instanceMap[key] as FabricationView;
}

public function FabricationView(key:String) {
super(key);
}

/**
* @see org.puremvc.as3.multicore.utilities.fabrication.interfaces.IDisposable#dispose()
*/
public function dispose():void {

for each(var mediator:IMediator in mediatorMap) {

if (mediator is IDisposable) {
IDisposable(mediator).dispose();
}
// check if mediator still exists or it was removed in the dispose call
if (hasMediator(mediator.getMediatorName())) {
// removing the mediator also removes it's observerMap references, good to call
removeMediator(mediator.getMediatorName());
}
}

instanceMap[multitonKey] = null;
}

}
}


Addition to FabricationController to override parent initialize call of view
:
    override protected function initializeController():void {
           view = FabricationView.getInstance( multitonKey );
     }

On a side note, thanks to fabrication I've successfully created a mutli-tiered Flash application (modules within modules loaded at run time) that can be then loaded inside a Flex application for debugging and hook into all the routing and notifications sent within the Flash app for logging. Simply awesome work, thanks Darshan! Not sure I could have accomplished the same thing without your hard work.
« Last Edit: November 04, 2008, 07:32:44 by jasonmac » Logged
Jason MacDonald
Sr. Member
****
Posts: 243


View Profile Email
« Reply #28 on: November 04, 2008, 01:13:54 »

Well after diving even further into unloading of modules I've discovered that regardless of cleanup, modules loaded into a Flex Fabrication shell will never be GC'd. I haven't figured out why but using a basic Flex application loading a Fabrication swf file (just a basic Flash file with the document class as FlashApplication and a 1MB image on the stage) won't ever get GC'd after removing. If you put the loaded swf in it's own ApplicationDomain or exclude Fabrication from the Flex shell, the SWF file will get GC'd when unloaded.

All I can figure is there is something somewhere in Fabrication holding a reference between the loaded SWF and Flex. Keep in mind this is with no custom code anywhere other than the Flex loading and unloading functions. So there's no extra stuff being loaded in the SWF file (I don't even give a startup command so I could halt further execution).

I'm using Google's Chrome browser to track the memory usage. Repeated loading/unloading drives the memory up, almost doubling every time load/unload is run. But either setting the loaderContext to it's own ApplicationDomain, or setting the Flex shell to extend something other than Fabrication, the loaded swf is unloaded and memory is released. Hopefully we can track this down as this puts a huge damper on my usage of Fabrication:(

The files can be found here

Change the MemoryTest.mxml file to extend Fabrication instead of MX:Application to see the memory increase. Sorry it's a pretty simple test but unless I'm missing something it proves the inability for GC to remove the swf completely if both the loader and loadee use Fabrication. I'm thinking it might be something in the routing somewhere. Using dispose() on the swf before unloading doesn't seem to help.
Logged
Darshan Sawardekar
Sr. Member
****
Posts: 85


View Profile Email
« Reply #29 on: November 05, 2008, 06:58:09 »

Hey Jason,

I had left out the concrete Model and View for later. Your example seems to be a good time to implement this. I am currently doing the units test for Fabrication. And I did discover some of the clean up issues also. A few important classes like DynamicJunction had not implemented IDisposable among others. Things are slightly broken at the moment because I am doing the unit tests after the implementation. I have to be careful about not breaking anything in stuff that is not yet tested.

Fundamentally I do not see any reason that things would not get gc'd correctly. There is extensive implementation of IDisposable throughout the framework. I will get back in a day or so with a fix.

peace,
darshan


Logged
Pages: 1 [2] 3
Print