[ 2486831 ] Dashboard auto refresh performance issue

This commit is contained in:
Heng Sin Low 2009-01-05 02:01:48 +00:00
parent ac7b3839e6
commit 12c4c3ea4e
10 changed files with 233 additions and 65 deletions

View File

@ -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();

View File

@ -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() {
}
}

View File

@ -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<DashboardPanel> 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);
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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();
}

View File

@ -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;
}
}