0

How to avoid multiple desktops being created?

asked 2012-10-16 00:46:06 +0800

rickcr gravatar image rickcr
704 7

This took me a while to track down. I only noticed because of a global command being sent to a queue and I saw the event firing multiple times.

I have a menu item. I was only able to find one example showing how to use a menu with a MVVM approach and opening up the content in one of the divs of the layout. I'm doing it like so:

@Command
public void onShowContent(@BindingParam("param") String page) {
	Include contentInc = (Include) Path.getComponent("/mainPage/inner");
	contentInc.getChildren().clear();
	Executions.createComponents(page, contentInc, null);
}

"page" is passed when clicking the link. ..

<menuitem label="Builder" onClick="@command('onShowContent', param='/pages/home/builder.zul')" />

The issue though is if you click the above menu link multiple times (which will happen often), there is some kind of internal memory of pages (multiple desktops?) maybe being kept in scope? This is creating a problem since on builder.zul there is also a Tree that has a Controller and this controller listens for a global event on a queue. The problem is the listener will be fired multiple times from a SINGLE global post. The number of times I see it firing is in direct relation to the number of times there was a call to the creation of builder.zul.

For example the controller on the tree listens like such:

@Subscribe("specQueue_Submit")
public void processSpecSubmit(Event evt){ 
	if(evt instanceof GlobalCommandEvent){
                if("processSpecSubmit".equals(((GlobalCommandEvent)evt).getCommand())){
                      //logger 

The call to this queue only fires from one place with this command:

BindUtils.postGlobalCommand("specQueue_Submit", null, "processSpecSubmit", null);

The logger is firing multiple times for a single post to specQueue.

My hunch is that it's related to calling Executions.createComponents(page, contentInc, null); on each time the menu was clicked. How do I simply reuse the existing page instead of having a new one created each time after calling "clear" ? I must have to store the page somehow I'm guessing within the ViewModel and resurrect it some how if it's not null? Are there some examples of this? To me this is an extremely common work flow so I'd think there would be some examples. It's common to have a standard layout and you click menu items and open up your content. I need to be able to post the global command though without it having multiple listeners created each time.

delete flag offensive retag edit

6 Replies

Sort by ยป oldest newest

answered 2012-10-16 01:30:14 +0800

rickcr gravatar image rickcr
704 7

Hmmm I think i know what the issue could be. On the page that uses the Controller the controller has a reference to a session based value that is a reference that is used in multiple VMs. So my guess is the Controller can never be actually garbage collected and hence when a global command is fired, there could be multiple instances of the controller.

is there a way that for each User I could simply reuse the existing view (and corresponding view model) when I make this kind of call:

@Command
public void onShowContent(@BindingParam("param") String page) {
	Include contentInc = (Include) Path.getComponent("/mainPage/inner");
	contentInc.getChildren().clear();
	Executions.createComponents(page, contentInc, null);
}

I don't think I want to use desktop recycling do I http://books.zkoss.org/wiki/ZK_Developer's_Reference/Performance_Tips/Reuse_Desktops? I can't really tell if that's 'per user' or 'per application' it sounds at first like it's per user?

link publish delete flag offensive edit

answered 2012-10-16 02:02:11 +0800

rickcr gravatar image rickcr
704 7

I tried adding

<listener>
	<listener-class>org.zkoss.zkmax.ui.util.DesktopRecycle</listener-class>
</listener>

To my zk.xml but it didn't help. I'm logging the constructor of my ViewModel and it's called every time the above onShowContent command above fires, which in turn causes a Controller to be created each time (of which the Controller instances apparently are never garbage collected and all of them are listening to the subscribed queue.)

link publish delete flag offensive edit

answered 2012-10-16 04:04:50 +0800

rickcr gravatar image rickcr
704 7

I'm getting closer I think.. this at least lets me reuse a component but the problem now is for some reason any command on the page that get render are trying to be found in the MenuVM ! Not the ViewModel that should be attached to the zul. For example this below will correctly load the builder page but any commands now from the builder zul are trying to be found in this MenuVM...

//MenuVM (on the main layout page )

private Component builder;

@Command
public void onShowBuilder(@BindingParam("param") String page) {
	Include contentInc = (Include) Path.getComponent("/mainPage/inner");
	contentInc.getChildren().clear();
	if (builder == null) {
		builder = Executions.createComponents(page, contentInc, null);
	} else {
		contentInc.getChildren().add(builder);
	}
}

Problem: when I use the above approach commans on builder are trying to be found in the MenuVM above. When I go back to the old way.. (Creating it each time with Executions.createComponents(page, contentInc, null) the command fire correctly.)

Remember all of this is to figure out why I'm getting multiple firings of my subscribed listener in one of my components.

link publish delete flag offensive edit

answered 2012-10-29 10:56:20 +0800

dennis gravatar image dennis
3669 1 6
http://www.javaworld.com....

I think your main issue is the inside controller doesn't been released after clicking the menu and reloading it's parent.
This is because of you use @Subscribe on a controller to listen to a event-queue, however you don't unsubscribe it.
I can do un-subscribe by my own code when removing/reloading a area.
But, I don't know a better way to un-subscribe a @Subscribed controller automatically.

link publish delete flag offensive edit

answered 2012-10-29 13:21:51 +0800

rickcr gravatar image rickcr
704 7

Thanks Dennis. It would be nice though if the best approach to take in regard to dealingwith this "unsubscribe" issue when using @Subscribe was documented somewhere?
I brought it up here as well (while I was searching about this)
http://www.zkoss.org/forum/listComment/18752-Event-Queue-subscribed-more-than-once

it seems sort of dangerous that such a scenario could occur so easily using @Subscribe. (Interestingly the only reason I needed @Subscribe is because Composers can't listen for GlobalCommands ? I saw a feature request for this and this might help if we were simply allowed to have Composers use @GlobalCommand

If I had to start over and wasn't using the solution described above I'd end up just using a ViewModel in place of the Composer since I think it could do everything the Composer could do? AND it has the benefit of accept GlobalCommands and I wouldn't have to worry about all this subscribe/unsubscribe stuff.

link publish delete flag offensive edit

answered 2012-10-30 04:56:15 +0800

dennis gravatar image dennis
3669 1 6
http://www.javaworld.com....

updated 2012-10-30 04:56:32 +0800

It is more like a enhancement (or a bug?) when using @Subscribe in dynamic component construction scenario.
I posted a issue here, http://tracker.zkoss.org/browse/ZK-1438

link publish delete flag offensive edit
Your reply
Please start posting your answer anonymously - your answer will be saved within the current session and published after you log in or create a new account. Please try to give a substantial answer, for discussions, please use comments and please do remember to vote (after you log in)!

[hide preview]

Question tools

Follow

RSS

Stats

Asked: 2012-10-16 00:46:06 +0800

Seen: 178 times

Last updated: Oct 30 '12

Support Options
  • Email Support
  • Training
  • Consulting
  • Outsourcing
Learn More