From 12c4c3ea4e4a68ec093a287343befb9cd6af8f57 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Mon, 5 Jan 2009 02:01:48 +0000 Subject: [PATCH] [ 2486831 ] Dashboard auto refresh performance issue --- .../webui/dashboard/DPActivities.java | 27 +++++-- .../webui/dashboard/DashboardPanel.java | 12 ++-- .../webui/dashboard/DashboardRunnable.java | 54 ++++++++------ .../webui/dashboard/IDashboardPanel.java | 11 ++- .../webui/desktop/DefaultDesktop.java | 28 +++++--- .../org/adempiere/webui/desktop/IDesktop.java | 8 ++- .../webui/desktop/NavBar2Desktop.java | 28 +++++--- .../webui/desktop/NavBarDesktop.java | 30 +++++--- .../webui/util/IServerPushCallback.java | 29 ++++++++ .../webui/util/ServerPushTemplate.java | 71 +++++++++++++++++++ 10 files changed, 233 insertions(+), 65 deletions(-) create mode 100644 zkwebui/WEB-INF/src/org/adempiere/webui/util/IServerPushCallback.java create mode 100644 zkwebui/WEB-INF/src/org/adempiere/webui/util/ServerPushTemplate.java diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DPActivities.java b/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DPActivities.java index 414357a0ff..29241c04b0 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DPActivities.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DPActivities.java @@ -19,6 +19,7 @@ import java.util.logging.Level; import org.adempiere.webui.component.Button; import org.adempiere.webui.session.SessionManager; +import org.adempiere.webui.util.ServerPushTemplate; import org.compiere.model.MRole; import org.compiere.util.CLogger; import org.compiere.util.DB; @@ -42,6 +43,12 @@ public class DPActivities extends DashboardPanel implements EventListener { private static final CLogger logger = CLogger.getCLogger(DPActivities.class); private Button btnNotice, btnRequest, btnWorkflow; + + private int noOfNotice; + + private int noOfRequest; + + private int noOfWorkflow; public DPActivities() { @@ -162,18 +169,24 @@ public class DPActivities extends DashboardPanel implements EventListener { return count; } - public void refresh() + @Override + public void refresh(ServerPushTemplate template) { - int noOfNotice = getNoticeCount(); - int noOfRequest = getRequestCount(); - int noOfWorkflow = getWorkflowCount(); + noOfNotice = getNoticeCount(); + noOfRequest = getRequestCount(); + noOfWorkflow = getWorkflowCount(); - btnNotice.setLabel("Notice : " + noOfNotice); + template.execute(this); + } + + @Override + public void updateUI() { + btnNotice.setLabel("Notice : " + noOfNotice); btnRequest.setLabel("Request : " + noOfRequest); btnWorkflow.setLabel("Workflow Activities : " + noOfWorkflow); } - - public void onEvent(Event event) + + public void onEvent(Event event) { Component comp = event.getTarget(); String eventName = event.getName(); diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DashboardPanel.java b/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DashboardPanel.java index ad484f5eaa..fb49dbd5b3 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DashboardPanel.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DashboardPanel.java @@ -14,6 +14,7 @@ package org.adempiere.webui.dashboard; import org.adempiere.webui.component.Window; +import org.adempiere.webui.util.ServerPushTemplate; /** * Custom dashboard item base class @@ -27,12 +28,9 @@ public abstract class DashboardPanel extends Window implements IDashboardPanel { super(); } - /** - * Desktop.UpdateInfoRunnable will call this method at a certain interval. - * Subclass should overwrite this method to support server push. - */ - public void refresh() { - + public void refresh(ServerPushTemplate template) { + } + + public void updateUI() { } - } diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DashboardRunnable.java b/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DashboardRunnable.java index 19fd317454..e5fd043641 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DashboardRunnable.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/DashboardRunnable.java @@ -15,21 +15,34 @@ package org.adempiere.webui.dashboard; import java.util.ArrayList; import java.util.List; -import java.util.logging.Level; import org.adempiere.webui.desktop.IDesktop; +import org.adempiere.webui.util.ServerPushTemplate; +import org.compiere.model.MSysConfig; import org.compiere.util.CLogger; import org.zkoss.zk.ui.Desktop; -import org.zkoss.zk.ui.Executions; +/** + * + * @author hengsin + * + */ public class DashboardRunnable implements Runnable { private Desktop desktop; private boolean stop = false; private List dashboardPanels; private IDesktop appDesktop; - private static final CLogger logger = CLogger.getCLogger(DashboardRunnable.class); + @SuppressWarnings("unused") + private static final CLogger logger = CLogger.getCLogger(DashboardRunnable.class); + private final static String ZK_DASHBOARD_REFRESH_INTERVAL = "ZK_DASHBOARD_REFRESH_INTERVAL"; + + /** + * + * @param desktop zk desktop interface + * @param appDesktop adempiere desktop interface + */ public DashboardRunnable(Desktop desktop, IDesktop appDesktop) { this.desktop = desktop; this.appDesktop = appDesktop; @@ -39,44 +52,43 @@ public class DashboardRunnable implements Runnable { public void run() { + // default Update every one minutes + int interval = MSysConfig.getIntValue(ZK_DASHBOARD_REFRESH_INTERVAL, 60000); while(!stop) { try { - Thread.sleep(60000); // Update every one minutes + Thread.sleep(interval); } catch (InterruptedException e1) { if (stop) break; } - try { - // get full control of desktop - Executions.activate(desktop); - try { - refreshDashboard(); - } catch (Error ex) { - logger.log(Level.WARNING, "UpdateInfo Thread error="+ex.getLocalizedMessage(), ex); - break; - } finally { - // release full control of desktop - Executions.deactivate(desktop); - } - } catch (Throwable e) { - logger.log(Level.WARNING, "UpdateInfo Thread error="+e.getLocalizedMessage(), e); + if (desktop.isAlive()) { + refreshDashboard(); + } else { break; - } + } } } + /** + * Refresh dashboard content + */ public void refreshDashboard() { + ServerPushTemplate template = new ServerPushTemplate(desktop); for(int i = 0; i < dashboardPanels.size(); i++) - dashboardPanels.get(i).refresh(); + dashboardPanels.get(i).refresh(template); - appDesktop.onServerPush(); + appDesktop.onServerPush(template); } public void stop() { stop = true; } + /** + * Add DashboardPanel to the auto refresh list + * @param dashboardPanel + */ public void add(DashboardPanel dashboardPanel) { dashboardPanels.add(dashboardPanel); } diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/IDashboardPanel.java b/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/IDashboardPanel.java index 547fc943e6..b5b672d8a2 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/IDashboardPanel.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/dashboard/IDashboardPanel.java @@ -13,17 +13,22 @@ *****************************************************************************/ package org.adempiere.webui.dashboard; +import org.adempiere.webui.util.IServerPushCallback; +import org.adempiere.webui.util.ServerPushTemplate; + /** * Interface for dashboard panel * @author Elaine * @author hengsin * */ -public interface IDashboardPanel { +public interface IDashboardPanel extends IServerPushCallback { /** - * Refresh content of panel + * Refresh content of panel. For performance reason, keep the activate of desktop as short + * as possible. + * @param template */ - public void refresh(); + public void refresh(ServerPushTemplate template); } diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java b/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java index 170d4a3208..e459ac62b3 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java @@ -34,6 +34,8 @@ import org.adempiere.webui.dashboard.DashboardRunnable; import org.adempiere.webui.event.MenuListener; import org.adempiere.webui.panel.HeaderPanel; import org.adempiere.webui.panel.SidePanel; +import org.adempiere.webui.util.IServerPushCallback; +import org.adempiere.webui.util.ServerPushTemplate; import org.compiere.model.MMenu; import org.compiere.model.X_AD_Menu; import org.compiere.model.X_PA_DashboardContent; @@ -65,7 +67,7 @@ import org.zkoss.zul.Panelchildren; * @date Mar 2, 2007 * @version $Revision: 0.10 $ */ -public class DefaultDesktop extends TabbedDesktop implements MenuListener, Serializable, EventListener +public class DefaultDesktop extends TabbedDesktop implements MenuListener, Serializable, EventListener, IServerPushCallback { private static final long serialVersionUID = 9056511175189603883L; @@ -79,6 +81,12 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria private Thread dashboardThread; private DashboardRunnable dashboardRunnable; + + private int noOfNotice; + + private int noOfRequest; + + private int noOfWorkflow; public DefaultDesktop() { @@ -348,15 +356,13 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria } } - public void onServerPush() + public void onServerPush(ServerPushTemplate template) { - int noOfNotice = DPActivities.getNoticeCount(); - int noOfRequest = DPActivities.getRequestCount(); - int noOfWorkflow = DPActivities.getWorkflowCount(); - int total = noOfNotice + noOfRequest + noOfWorkflow; + noOfNotice = DPActivities.getNoticeCount(); + noOfRequest = DPActivities.getRequestCount(); + noOfWorkflow = DPActivities.getWorkflowCount(); - windowContainer.setTabTitle(0, "Home (" + total + ")", - "Notice : " + noOfNotice + ", Request : " + noOfRequest + ", Workflow Activities : " + noOfWorkflow); + template.execute(this); } /** @@ -384,4 +390,10 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria dashboardThread.interrupt(); } } + + public void updateUI() { + int total = noOfNotice + noOfRequest + noOfWorkflow; + windowContainer.setTabTitle(0, "Home (" + total + ")", + "Notice : " + noOfNotice + ", Request : " + noOfRequest + ", Workflow Activities : " + noOfWorkflow); + } } diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java b/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java index d574eb5dfa..002e1e2d20 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java @@ -18,6 +18,7 @@ import org.adempiere.webui.apps.ProcessDialog; import org.adempiere.webui.component.Window; import org.adempiere.webui.panel.ADForm; import org.adempiere.webui.part.UIPart; +import org.adempiere.webui.util.ServerPushTemplate; import org.adempiere.webui.window.ADWindow; import org.compiere.model.MQuery; import org.compiere.util.WebDoc; @@ -171,7 +172,10 @@ public interface IDesktop extends UIPart { public void logout(); /** - * Invoke by the server push thread. + * Invoke by the server push thread. If the desktop argument is not null, must activate desktop + * before making update to UI. For performance reason, keep the activate of desktop as short + * as possible. + * @param template */ - public void onServerPush(); + public void onServerPush(ServerPushTemplate template); } diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/NavBar2Desktop.java b/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/NavBar2Desktop.java index 569755a11b..1e2a5a8d8f 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/NavBar2Desktop.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/NavBar2Desktop.java @@ -31,6 +31,8 @@ import org.adempiere.webui.dashboard.DashboardRunnable; import org.adempiere.webui.event.MenuListener; import org.adempiere.webui.panel.HeaderPanel; import org.adempiere.webui.panel.SidePanel; +import org.adempiere.webui.util.IServerPushCallback; +import org.adempiere.webui.util.ServerPushTemplate; import org.compiere.model.MMenu; import org.compiere.model.X_AD_Menu; import org.compiere.model.X_PA_DashboardContent; @@ -58,7 +60,7 @@ import org.zkoss.zul.Panelchildren; /** * @author hengsin */ -public class NavBar2Desktop extends TabbedDesktop implements MenuListener, Serializable, EventListener +public class NavBar2Desktop extends TabbedDesktop implements MenuListener, Serializable, EventListener, IServerPushCallback { private static final String FAVOURITES_PATH = "/zul/favourites.zul"; @@ -79,6 +81,12 @@ public class NavBar2Desktop extends TabbedDesktop implements MenuListener, Seria private Accordion shortcutPanel; + private int noOfNotice; + + private int noOfRequest; + + private int noOfWorkflow; + public NavBar2Desktop() { super(); @@ -388,15 +396,13 @@ public class NavBar2Desktop extends TabbedDesktop implements MenuListener, Seria } } - public void onServerPush() + public void onServerPush(ServerPushTemplate template) { - int noOfNotice = DPActivities.getNoticeCount(); - int noOfRequest = DPActivities.getRequestCount(); - int noOfWorkflow = DPActivities.getWorkflowCount(); - int total = noOfNotice + noOfRequest + noOfWorkflow; + noOfNotice = DPActivities.getNoticeCount(); + noOfRequest = DPActivities.getRequestCount(); + noOfWorkflow = DPActivities.getWorkflowCount(); - shortcutPanel.setLabel(1, "Activities (" + total + ")"); - shortcutPanel.setTooltiptext(1, "Notice : " + noOfNotice + ", Request : " + noOfRequest + ", Workflow Activities : " + noOfWorkflow); + template.execute(this); } /** @@ -424,4 +430,10 @@ public class NavBar2Desktop extends TabbedDesktop implements MenuListener, Seria dashboardThread.interrupt(); } } + + public void updateUI() { + int total = noOfNotice + noOfRequest + noOfWorkflow; + shortcutPanel.setLabel(1, "Activities (" + total + ")"); + shortcutPanel.setTooltiptext(1, "Notice : " + noOfNotice + ", Request : " + noOfRequest + ", Workflow Activities : " + noOfWorkflow); + } } diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/NavBarDesktop.java b/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/NavBarDesktop.java index b3f6db5664..5296ca3e08 100644 --- a/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/NavBarDesktop.java +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/desktop/NavBarDesktop.java @@ -32,6 +32,8 @@ import org.adempiere.webui.dashboard.DashboardRunnable; import org.adempiere.webui.event.MenuListener; import org.adempiere.webui.panel.HeaderPanel; import org.adempiere.webui.panel.SidePanel; +import org.adempiere.webui.util.IServerPushCallback; +import org.adempiere.webui.util.ServerPushTemplate; import org.compiere.model.MMenu; import org.compiere.model.X_AD_Menu; import org.compiere.model.X_PA_DashboardContent; @@ -62,7 +64,7 @@ import org.zkoss.zul.Treerow; /** * @author hengsin */ -public class NavBarDesktop extends TabbedDesktop implements MenuListener, Serializable, EventListener +public class NavBarDesktop extends TabbedDesktop implements MenuListener, Serializable, EventListener, IServerPushCallback { private static final String FAVOURITES_PATH = "/zul/favourites.zul"; @@ -88,6 +90,12 @@ public class NavBarDesktop extends TabbedDesktop implements MenuListener, Serial private West leftRegion; private DPFavourites favPanel; + + private int noOfNotice; + + private int noOfRequest; + + private int noOfWorkflow; public NavBarDesktop() { @@ -404,15 +412,13 @@ public class NavBarDesktop extends TabbedDesktop implements MenuListener, Serial } } - public void onServerPush() + public void onServerPush(ServerPushTemplate template) { - int noOfNotice = DPActivities.getNoticeCount(); - int noOfRequest = DPActivities.getRequestCount(); - int noOfWorkflow = DPActivities.getWorkflowCount(); - int total = noOfNotice + noOfRequest + noOfWorkflow; - - navigationPanel.setLabel(2, "Activities (" + total + ")"); - navigationPanel.setTooltiptext(2, "Notice : " + noOfNotice + ", Request : " + noOfRequest + ", Workflow Activities : " + noOfWorkflow); + noOfNotice = DPActivities.getNoticeCount(); + noOfRequest = DPActivities.getRequestCount(); + noOfWorkflow = DPActivities.getWorkflowCount(); + + template.execute(this); } /** @@ -440,4 +446,10 @@ public class NavBarDesktop extends TabbedDesktop implements MenuListener, Serial dashboardThread.interrupt(); } } + + public void updateUI() { + int total = noOfNotice + noOfRequest + noOfWorkflow; + navigationPanel.setLabel(2, "Activities (" + total + ")"); + navigationPanel.setTooltiptext(2, "Notice : " + noOfNotice + ", Request : " + noOfRequest + ", Workflow Activities : " + noOfWorkflow); + } } diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/util/IServerPushCallback.java b/zkwebui/WEB-INF/src/org/adempiere/webui/util/IServerPushCallback.java new file mode 100644 index 0000000000..9b490fd179 --- /dev/null +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/util/IServerPushCallback.java @@ -0,0 +1,29 @@ +/****************************************************************************** + * Copyright (C) 2008 Low Heng Sin * + * Copyright (C) 2008 Idalica Corporation * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ +package org.adempiere.webui.util; + +/** + * Callback interface to perform UI update inside the UI thread or server push thread. + * @author hengsin + * + */ +public interface IServerPushCallback { + + /** + * Callback method to perform UI related update. For performance reason, implementation of this method + * must not perform potentially slow operation. + */ + public void updateUI(); + +} diff --git a/zkwebui/WEB-INF/src/org/adempiere/webui/util/ServerPushTemplate.java b/zkwebui/WEB-INF/src/org/adempiere/webui/util/ServerPushTemplate.java new file mode 100644 index 0000000000..7845176225 --- /dev/null +++ b/zkwebui/WEB-INF/src/org/adempiere/webui/util/ServerPushTemplate.java @@ -0,0 +1,71 @@ +/****************************************************************************** + * Copyright (C) 2008 Low Heng Sin * + * Copyright (C) 2008 Idalica Corporation * + * This program is free software; you can redistribute it and/or modify it * + * under the terms version 2 of the GNU General Public License as published * + * by the Free Software Foundation. This program is distributed in the hope * + * that it will be useful, but WITHOUT ANY WARRANTY; without even the implied * + * warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * + * See the GNU General Public License for more details. * + * You should have received a copy of the GNU General Public License along * + * with this program; if not, write to the Free Software Foundation, Inc., * + * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. * + *****************************************************************************/ +package org.adempiere.webui.util; + +import java.util.logging.Level; + +import org.compiere.util.CLogger; +import org.zkoss.zk.ui.Desktop; +import org.zkoss.zk.ui.Executions; + +/** + * Zk UI update must be done in either UI thread or using server push. This class help to implement + * that base on spring's jdbctemplate pattern. + * @author hengsin + * + */ +public class ServerPushTemplate { + + private Desktop desktop; + + private final static CLogger logger = CLogger.getCLogger(ServerPushTemplate.class); + + /** + * + * @param desktop + */ + public ServerPushTemplate(Desktop desktop) { + this.desktop = desktop; + } + + /** + * Execute callback in UI thread + * @param callback + */ + public void execute(IServerPushCallback callback) { + boolean inUIThread = Executions.getCurrent() != null; + + try { + if (!inUIThread) { + //1 second timeout + Executions.activate(desktop, 1000); + } + callback.updateUI(); + } catch (Exception e) { + logger.log(Level.WARNING, "Server push error="+e.getLocalizedMessage(), e); + } finally { + if (!inUIThread) { + Executions.deactivate(desktop); + } + } + } + + /** + * + * @return desktop + */ + public Desktop getDesktop() { + return desktop; + } +}