From 9d312ff60536bedc79ba318a968f2dbacdb8440f Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Thu, 17 Jan 2013 01:14:16 +0800 Subject: [PATCH] IDEMPIERE-348 Zk Performance: remove use of polling background thread for update of dashboard. Use server side event to update calendar and recent items dashboard widget. Activities dashboard widget is still using polling but update of home tab label for activities count is now event driven. --- .../src/org/compiere/model/MRecentItem.java | 28 +++ .../src/org/compiere/util/Trx.java | 44 ++++ .../org/compiere/util/TrxEventListener.java | 24 ++ .../webui/dashboard/CalendarWindow.java | 9 +- .../webui/dashboard/DPActivities.java | 45 ++-- .../adempiere/webui/dashboard/DPCalendar.java | 211 ++++++++++++++++-- .../webui/dashboard/DPRecentItems.java | 79 ++++++- .../webui/dashboard/DashboardPanel.java | 7 + .../webui/dashboard/DashboardRunnable.java | 25 +-- .../webui/desktop/DashboardController.java | 13 +- .../webui/desktop/DefaultDesktop.java | 51 +++-- .../org/adempiere/webui/desktop/IDesktop.java | 11 +- 12 files changed, 454 insertions(+), 93 deletions(-) create mode 100644 org.adempiere.base/src/org/compiere/util/TrxEventListener.java diff --git a/org.adempiere.base/src/org/compiere/model/MRecentItem.java b/org.adempiere.base/src/org/compiere/model/MRecentItem.java index 7d505f5853..588645a2da 100644 --- a/org.adempiere.base/src/org/compiere/model/MRecentItem.java +++ b/org.adempiere.base/src/org/compiere/model/MRecentItem.java @@ -16,15 +16,22 @@ package org.compiere.model; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; +import java.util.HashMap; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Properties; +import org.adempiere.base.Service; +import org.adempiere.base.event.EventManager; import org.adempiere.exceptions.AdempiereException; import org.compiere.util.CCache; import org.compiere.util.CLogger; import org.compiere.util.DB; import org.compiere.util.Env; +import org.idempiere.distributed.IMessageService; +import org.idempiere.distributed.ITopic; +import org.osgi.service.event.Event; /** * Recent Item model @@ -33,6 +40,8 @@ import org.compiere.util.Env; */ public class MRecentItem extends X_AD_RecentItem { + public static final String ON_RECENT_ITEM_CHANGED_TOPIC = "onRecentItemChanged"; + /** * */ @@ -166,6 +175,24 @@ public class MRecentItem extends X_AD_RecentItem ri.setAD_Window_ID(AD_Window_ID); ri.setAD_Tab_ID(AD_Tab_ID); ri.saveEx(); + publishChangedEvent(AD_User_ID); + } + + private static void publishChangedEvent(int AD_User_ID) { + IMessageService service = Service.locator().locate(IMessageService.class).getService(); + if (service != null) { + ITopic topic = service.getTopic(ON_RECENT_ITEM_CHANGED_TOPIC); + topic.publish(AD_User_ID); + } else { + postOnChangedEvent(AD_User_ID); + } + } + + public static void postOnChangedEvent(int AD_User_ID) { + Map properties = new HashMap(); + properties.put("AD_User_ID", AD_User_ID); + Event event = new Event(ON_RECENT_ITEM_CHANGED_TOPIC, properties); + EventManager.getInstance().postEvent(event); } /* @@ -178,6 +205,7 @@ public class MRecentItem extends X_AD_RecentItem if (ri != null) { DB.executeUpdateEx("UPDATE AD_RecentItem SET Updated=SYSDATE WHERE AD_RecentItem_ID=?", new Object[] {ri.getAD_RecentItem_ID()}, null); deleteExtraRecentItems(ctx, AD_User_ID); + publishChangedEvent(AD_User_ID); } } diff --git a/org.adempiere.base/src/org/compiere/util/Trx.java b/org.adempiere.base/src/org/compiere/util/Trx.java index 93a92dfe7a..3716080167 100644 --- a/org.adempiere.base/src/org/compiere/util/Trx.java +++ b/org.adempiere.base/src/org/compiere/util/Trx.java @@ -19,9 +19,11 @@ package org.compiere.util; import java.sql.Connection; import java.sql.SQLException; import java.sql.Savepoint; +import java.util.ArrayList; import java.util.Collection; import java.util.Date; import java.util.HashMap; +import java.util.List; import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -84,6 +86,8 @@ public class Trx private static Map s_cache = null; // create change listener private static Trx.TrxMonitor s_monitor = new Trx.TrxMonitor(); + + private List listeners = new ArrayList(); public static void startTrxMonitor() { @@ -278,6 +282,7 @@ public class Trx m_connection.rollback(); log.log(isLocalTrx(m_trxName) ? Level.FINE : Level.INFO, "**** " + m_trxName); m_active = false; + fireAfterRollbackEvent(true); return true; } } @@ -287,13 +292,22 @@ public class Trx if (throwException) { m_active = false; + fireAfterRollbackEvent(false); throw e; } } m_active = false; + fireAfterRollbackEvent(false); return false; } // rollback + private void fireAfterRollbackEvent(boolean success) { + TrxEventListener[] copies = listeners.toArray(new TrxEventListener[0]); + for(TrxEventListener l : copies) { + l.afterRollback(this, success); + } + } + /** * Rollback * @return true if success, false if failed or transaction already rollback @@ -347,6 +361,7 @@ public class Trx m_connection.commit(); log.info ("**** " + m_trxName); m_active = false; + fireAfterCommitEvent(true); return true; } } @@ -356,13 +371,22 @@ public class Trx if (throwException) { m_active = false; + fireAfterCommitEvent(false); throw e; } } m_active = false; + fireAfterCommitEvent(false); return false; } // commit + private void fireAfterCommitEvent(boolean success) { + TrxEventListener[] copies = listeners.toArray(new TrxEventListener[0]); + for(TrxEventListener l : copies) { + l.afterCommit(this, success); + } + } + /** * Commit * @return true if success @@ -417,10 +441,18 @@ public class Trx } m_connection = null; m_active = false; + fireAfterCloseEvent(); log.config(m_trxName); return true; } // close + private void fireAfterCloseEvent() { + TrxEventListener[] copies = listeners.toArray(new TrxEventListener[0]); + for(TrxEventListener l : copies) { + l.afterClose(this); + } + } + /** * * @param name @@ -589,6 +621,18 @@ public class Trx m_timeout = timeout; } + /** + * + * @param listener + */ + public void addTrxEventListener(TrxEventListener listener) { + listeners.add(listener); + } + + public boolean removeTrxEventListener(TrxEventListener listener) { + return listeners.remove(listener); + } + static class TrxMonitor implements Runnable { diff --git a/org.adempiere.base/src/org/compiere/util/TrxEventListener.java b/org.adempiere.base/src/org/compiere/util/TrxEventListener.java new file mode 100644 index 0000000000..155cb8d294 --- /dev/null +++ b/org.adempiere.base/src/org/compiere/util/TrxEventListener.java @@ -0,0 +1,24 @@ +/****************************************************************************** + * Copyright (C) 2013 Heng Sin Low * + * Copyright (C) 2013 Trek Global * + * 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.compiere.util; + +/** + * @author hengsin + * + */ +public interface TrxEventListener { + public void afterCommit(Trx trx, boolean success); + public void afterRollback(Trx trx, boolean success); + public void afterClose(Trx trx); +} diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/CalendarWindow.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/CalendarWindow.java index cb6e911d8e..7ab630a94f 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/CalendarWindow.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/CalendarWindow.java @@ -30,6 +30,7 @@ import java.util.TimeZone; import org.adempiere.webui.component.Window; import org.adempiere.webui.session.SessionManager; import org.compiere.model.X_R_RequestType; +import org.compiere.util.Env; import org.jfree.chart.ChartFactory; import org.jfree.chart.JFreeChart; import org.jfree.chart.encoders.EncoderUtil; @@ -116,7 +117,7 @@ public class CalendarWindow extends Window implements EventListener { lbxRequestTypes.addEventListener(Events.ON_SELECT, this); lbxRequestTypes.appendItem("(Show All)", "0"); - ArrayList types = DPCalendar.getRequestTypes(); + ArrayList types = DPCalendar.getRequestTypes(Env.getCtx()); for(X_R_RequestType type : types) lbxRequestTypes.appendItem(type.getName(), type.getR_RequestType_ID() + ""); lbxRequestTypes.setSelectedIndex(0); @@ -215,7 +216,7 @@ public class CalendarWindow extends Window implements EventListener { int R_RequestType_ID = Integer.parseInt(li.getValue().toString()); scm.clear(); - ArrayList events = DPCalendar.getEvents(R_RequestType_ID); + ArrayList events = DPCalendar.getEvents(R_RequestType_ID, Env.getCtx()); for (ADCalendarEvent event : events) scm.add(event); calendars.setModel(scm); @@ -341,7 +342,7 @@ public class CalendarWindow extends Window implements EventListener { lbxRequestTypes.removeItemAt(i); lbxRequestTypes.appendItem("(Show All)", "0"); - ArrayList types = DPCalendar.getRequestTypes(); + ArrayList types = DPCalendar.getRequestTypes(Env.getCtx()); for(X_R_RequestType requestType : types) { Listitem item = lbxRequestTypes.appendItem(requestType.getName(), requestType.getR_RequestType_ID() + ""); @@ -352,7 +353,7 @@ public class CalendarWindow extends Window implements EventListener { lbxRequestTypes.setSelectedIndex(0); scm.clear(); - ArrayList events = DPCalendar.getEvents(R_RequestType_ID); + ArrayList events = DPCalendar.getEvents(R_RequestType_ID, Env.getCtx()); for (ADCalendarEvent event : events) scm.add(event); calendars.setModel(scm); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPActivities.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPActivities.java index 557317655f..caef448624 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPActivities.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPActivities.java @@ -15,9 +15,12 @@ package org.adempiere.webui.dashboard; import java.sql.PreparedStatement; import java.sql.ResultSet; +import java.util.HashMap; +import java.util.Map; import java.util.logging.Level; import org.adempiere.webui.component.Button; +import org.adempiere.webui.desktop.IDesktop; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.util.ServerPushTemplate; import org.compiere.model.MRole; @@ -29,6 +32,8 @@ import org.compiere.util.Util; import org.zkoss.zk.ui.Component; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.EventQueue; +import org.zkoss.zk.ui.event.EventQueues; import org.zkoss.zk.ui.event.Events; import org.zkoss.zul.Box; import org.zkoss.zul.Vbox; @@ -221,27 +226,41 @@ public class DPActivities extends DashboardPanel implements EventListener @Override public void refresh(ServerPushTemplate template) { - noOfNotice = getNoticeCount(); - noOfRequest = getRequestCount(); - noOfWorkflow = getWorkflowCount(); - noOfUnprocessed = getUnprocessedCount(); - - template.executeAsync(this); + int notice = getNoticeCount(); + int request = getRequestCount(); + int workflow = getWorkflowCount(); + int unprocessed = getUnprocessedCount(); + if (noOfNotice != notice || noOfRequest != request + || noOfWorkflow != workflow || noOfUnprocessed != unprocessed ) + { + noOfNotice = notice; + noOfRequest = request; + noOfWorkflow = workflow; + noOfUnprocessed = unprocessed; + template.executeAsync(this); + } } @Override public void updateUI() { - //don't update if not visible - Component c = this.getParent(); - while (c != null) { - if (!c.isVisible()) - return; - c = c.getParent(); - } btnNotice.setLabel(labelN + " : " + noOfNotice); btnRequest.setLabel(labelR + " : " + noOfRequest); btnWorkflow.setLabel(labelW + " : " + noOfWorkflow); if (isShowUnprocessed()) btnUnprocessed.setLabel(labelU + " : " + noOfUnprocessed); + + EventQueue queue = EventQueues.lookup(IDesktop.ACTIVITIES_EVENT_QUEUE, true); + Map map = new HashMap(); + map.put("notice", noOfNotice); + map.put("request", noOfRequest); + map.put("workflow", noOfWorkflow); + map.put("unprocessed", noOfUnprocessed); + Event event = new Event(IDesktop.ON_ACTIVITIES_CHANGED_EVENT, null, map); + queue.publish(event); + } + + @Override + public boolean isPooling() { + return true; } public void onEvent(Event event) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPCalendar.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPCalendar.java index 85e7459a52..7bebed792c 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPCalendar.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPCalendar.java @@ -20,17 +20,34 @@ import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Calendar; import java.util.Date; +import java.util.HashMap; +import java.util.Map; +import java.util.Properties; +import org.adempiere.base.Service; +import org.adempiere.base.event.AbstractEventHandler; +import org.adempiere.base.event.EventManager; +import org.adempiere.base.event.IEventTopics; import org.adempiere.webui.session.SessionManager; +import org.adempiere.webui.util.ServerPushTemplate; +import org.compiere.model.I_R_Request; +import org.compiere.model.PO; import org.compiere.model.X_R_RequestType; import org.compiere.util.DB; import org.compiere.util.Env; +import org.compiere.util.Trx; +import org.compiere.util.TrxEventListener; +import org.idempiere.distributed.IMessageService; +import org.idempiere.distributed.ITopic; +import org.idempiere.distributed.ITopicSubscriber; +import org.osgi.service.event.EventHandler; import org.zkoss.calendar.Calendars; import org.zkoss.calendar.api.CalendarEvent; import org.zkoss.calendar.event.CalendarsEvent; import org.zkoss.calendar.impl.SimpleCalendarModel; import org.zkoss.util.Locales; import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; @@ -45,7 +62,7 @@ import org.zkoss.zul.Toolbarbutton; * @author Elaine * @date November 20, 2008 */ -public class DPCalendar extends DashboardPanel implements EventListener { +public class DPCalendar extends DashboardPanel implements EventListener, EventHandler { /** @@ -54,25 +71,30 @@ public class DPCalendar extends DashboardPanel implements EventListener { private static final long serialVersionUID = -224914882522997787L; private Calendars calendars; private SimpleCalendarModel scm; - private Toolbarbutton btnCal, btnRefresh; + private Toolbarbutton btnCal, btnRefresh; + private Button btnCurrentDate; private Label lblDate; private Component divArrowLeft, divArrowRight; + private static final String ON_REQUEST_CHANGED_TOPIC = "onRequestChanged"; + private EventWindow eventWin; - + private Properties ctx; + private Desktop desktop; + + private static RequestEventHandler eventHandler; + private static TopicSubscriber subscriber; + public DPCalendar() { super(); - scm = new SimpleCalendarModel(); - ArrayList events = getEvents(0); - for (ADCalendarEvent event : events) - scm.add(event); - + ctx = new Properties(); + ctx.putAll(Env.getCtx()); + Component component = Executions.createComponents("calendar_mini.zul", this, null); calendars = (Calendars) component.getFellow("cal"); - calendars.setModel(scm); btnCal = (Toolbarbutton) component.getFellow("btnCal"); btnCal.addEventListener(Events.ON_CLICK, this); @@ -96,6 +118,25 @@ public class DPCalendar extends DashboardPanel implements EventListener { calendars.addEventListener("onEventCreate", this); calendars.addEventListener("onEventEdit", this); + + EventManager.getInstance().register(ON_REQUEST_CHANGED_TOPIC, this); + createStaticListeners(); + } + + private synchronized void createStaticListeners() { + if (eventHandler == null) { + eventHandler = new RequestEventHandler(); + eventHandler.bindEventManager(EventManager.getInstance()); + } + + if (subscriber == null) { + subscriber = new TopicSubscriber(); + IMessageService service = Service.locator().locate(IMessageService.class).getService(); + if (service != null) { + ITopic> topic = service.getTopic("onRequestChanged"); + topic.subscribe(subscriber); + } + } } public void onEvent(Event e) throws Exception { @@ -143,7 +184,7 @@ public class DPCalendar extends DashboardPanel implements EventListener { } } - public static ArrayList getEvents(int RequestTypeID) { + public static ArrayList getEvents(int RequestTypeID, Properties ctx) { ArrayList events = new ArrayList(); String sql = "SELECT DISTINCT r.R_Request_ID, r.DateNextAction, " + "r.DateStartPlan, r.DateCompletePlan, r.StartTime, r.EndTime, " @@ -162,10 +203,10 @@ public class DPCalendar extends DashboardPanel implements EventListener { try { ps = DB.prepareStatement(sql, null); - ps.setInt(1, Env.getAD_User_ID(Env.getCtx())); - ps.setInt(2, Env.getAD_User_ID(Env.getCtx())); - ps.setInt(3, Env.getAD_User_ID(Env.getCtx())); - ps.setInt(4, Env.getAD_Client_ID(Env.getCtx())); + ps.setInt(1, Env.getAD_User_ID(ctx)); + ps.setInt(2, Env.getAD_User_ID(ctx)); + ps.setInt(3, Env.getAD_User_ID(ctx)); + ps.setInt(4, Env.getAD_Client_ID(ctx)); if(RequestTypeID > 0) ps.setInt(5, RequestTypeID); @@ -313,7 +354,7 @@ public class DPCalendar extends DashboardPanel implements EventListener { return events; } - public static ArrayList getRequestTypes() { + public static ArrayList getRequestTypes(Properties ctx) { ArrayList types = new ArrayList(); String sql = "SELECT * " + "FROM R_RequestType " @@ -325,12 +366,12 @@ public class DPCalendar extends DashboardPanel implements EventListener { try { ps = DB.prepareStatement(sql, null); - ps.setInt(1, Env.getAD_Client_ID(Env.getCtx())); + ps.setInt(1, Env.getAD_Client_ID(ctx)); rs = ps.executeQuery(); while (rs.next()) { - types.add(new X_R_RequestType(Env.getCtx(), rs, null)); + types.add(new X_R_RequestType(ctx, rs, null)); } } catch (Exception e) { e.printStackTrace(); @@ -345,14 +386,30 @@ public class DPCalendar extends DashboardPanel implements EventListener { btnRefreshClicked(); } - private void btnRefreshClicked() { - scm.clear(); - ArrayList events = getEvents(0); - for (ADCalendarEvent event : events) - scm.add(event); + @Override + public void refresh(ServerPushTemplate template) { + refreshModel(); + template.executeAsync(this); + desktop = getDesktop(); + } + + @Override + public void updateUI() { calendars.setModel(scm); calendars.invalidate(); } + + private void btnRefreshClicked() { + refreshModel(); + updateUI(); + } + + private void refreshModel() { + scm = new SimpleCalendarModel(); + ArrayList events = getEvents(0, ctx); + for (ADCalendarEvent event : events) + scm.add(event); + } private void updateDateLabel() { Date b = calendars.getBeginDate(); @@ -374,4 +431,114 @@ public class DPCalendar extends DashboardPanel implements EventListener { calendars.previousPage(); updateDateLabel(); } + + @Override + public void handleEvent(org.osgi.service.event.Event event) { + if (event.getTopic().equals(ON_REQUEST_CHANGED_TOPIC)) { + String clientId = (String) event.getProperty(I_R_Request.COLUMNNAME_AD_Client_ID); + String salesRepId = (String) event.getProperty(I_R_Request.COLUMNNAME_SalesRep_ID); + String userId = (String) event.getProperty(I_R_Request.COLUMNNAME_AD_User_ID); + String createdBy = (String) event.getProperty(I_R_Request.COLUMNNAME_CreatedBy); + + String AD_Client_ID = Integer.toString(Env.getAD_Client_ID(ctx)); + String AD_User_ID = Integer.toString(Env.getAD_User_ID(ctx)); + if (clientId.equals(AD_Client_ID) && !"0".equals(AD_User_ID)) { + if (salesRepId.equals(AD_User_ID) || userId.equals(AD_User_ID) || createdBy.equals(AD_User_ID)) { + try { + if (desktop != null && desktop.isAlive()) { + ServerPushTemplate template = new ServerPushTemplate(desktop); + refresh(template); + } else { + EventManager.getInstance().unregister(this); + } + } catch (Exception e) { + EventManager.getInstance().unregister(this); + } + } + } + } + } + + static class TopicSubscriber implements ITopicSubscriber> { + + @Override + public void onMessage(Map message) { + org.osgi.service.event.Event requestChangedEvent = new org.osgi.service.event.Event(ON_REQUEST_CHANGED_TOPIC, message); + EventManager.getInstance().postEvent(requestChangedEvent); + } + + } + + static class RequestEventHandler extends AbstractEventHandler { + @Override + protected void doHandleEvent(org.osgi.service.event.Event event) { + PO po = getPO(event); + I_R_Request request = (I_R_Request)po; + Map message = new HashMap(); + message.put(I_R_Request.COLUMNNAME_SalesRep_ID, Integer.toString(request.getSalesRep_ID())); + message.put(I_R_Request.COLUMNNAME_AD_User_ID, Integer.toString(request.getAD_User_ID())); + message.put(I_R_Request.COLUMNNAME_CreatedBy, Integer.toString(request.getCreatedBy())); + message.put(I_R_Request.COLUMNNAME_AD_Client_ID, Integer.toString(request.getAD_Client_ID())); + RequestRunnable runnable = new RequestRunnable(message); + Trx trx = po.get_TrxName() != null ? Trx.get(po.get_TrxName(), false) : null; + if (trx != null && trx.isActive()) { + trx.addTrxEventListener(new TrxListener(runnable)); + } else { + runnable.run(); + } + } + + @Override + protected void initialize() { + registerTableEvent(IEventTopics.PO_AFTER_NEW, I_R_Request.Table_Name); + registerTableEvent(IEventTopics.PO_AFTER_CHANGE, I_R_Request.Table_Name); + registerTableEvent(IEventTopics.PO_AFTER_DELETE, I_R_Request.Table_Name); + } + } + + static class RequestRunnable implements Runnable { + + private Map message; + + protected RequestRunnable(Map message) { + this.message = message; + } + + @Override + public void run() { + IMessageService service = Service.locator().locate(IMessageService.class).getService(); + if (service != null) { + ITopic> topic = service.getTopic(ON_REQUEST_CHANGED_TOPIC); + topic.publish(message); + } else { + org.osgi.service.event.Event requestChangedEvent = new org.osgi.service.event.Event(ON_REQUEST_CHANGED_TOPIC, message); + EventManager.getInstance().postEvent(requestChangedEvent); + } + } + } + + static class TrxListener implements TrxEventListener { + + private Runnable runnable; + + protected TrxListener(Runnable runnable) { + this.runnable = runnable; + } + + @Override + public void afterRollback(Trx trx, boolean success) { + } + + @Override + public void afterCommit(Trx trx, boolean success) { + if (success) { + runnable.run(); + } + } + + @Override + public void afterClose(Trx trx) { + trx.removeTrxEventListener(this); + } + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPRecentItems.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPRecentItems.java index 4de02ac58e..59f15c5e1e 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPRecentItems.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPRecentItems.java @@ -14,7 +14,10 @@ package org.adempiere.webui.dashboard; import java.util.List; +import java.util.Properties; +import org.adempiere.base.Service; +import org.adempiere.base.event.EventManager; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.util.ServerPushTemplate; import org.compiere.model.MQuery; @@ -24,7 +27,12 @@ import org.compiere.model.MTable; import org.compiere.util.Env; import org.compiere.util.Msg; import org.compiere.util.Util; +import org.idempiere.distributed.IMessageService; +import org.idempiere.distributed.ITopic; +import org.idempiere.distributed.ITopicSubscriber; +import org.osgi.service.event.EventHandler; import org.zkoss.zk.ui.Component; +import org.zkoss.zk.ui.Desktop; import org.zkoss.zk.ui.event.DropEvent; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; @@ -42,7 +50,7 @@ import org.zkoss.zul.Vbox; * @author Carlos Ruiz / GlobalQSS * @date January 27, 2012 */ -public class DPRecentItems extends DashboardPanel implements EventListener { +public class DPRecentItems extends DashboardPanel implements EventListener, EventHandler { private static final String AD_RECENT_ITEM_ID_ATTR = "AD_RecentItem_ID"; @@ -53,12 +61,24 @@ public class DPRecentItems extends DashboardPanel implements EventListener topic = service.getTopic(MRecentItem.ON_RECENT_ITEM_CHANGED_TOPIC); + topic.subscribe(topicSubscriber); + } + } } private void createRecentItemsPanel() @@ -101,7 +133,7 @@ public class DPRecentItems extends DashboardPanel implements EventListener 0) { - MRecentItem ri = MRecentItem.get(Env.getCtx(), AD_RecentItem_ID); - String TableName = MTable.getTableName(Env.getCtx(), ri.getAD_Table_ID()); + MRecentItem ri = MRecentItem.get(ctx, AD_RecentItem_ID); + String TableName = MTable.getTableName(ctx, ri.getAD_Table_ID()); MQuery query = MQuery.getEqualQuery(TableName + "_ID", ri.getRecord_ID()); SessionManager.getAppDesktop().openWindow(ri.getAD_Window_ID(), query, null); @@ -170,12 +202,11 @@ public class DPRecentItems extends DashboardPanel implements EventListener ris = MRecentItem.getFromUser(Env.getCtx(), AD_User_ID); + List ris = MRecentItem.getFromUser(ctx, AD_User_ID); int riShown = 0; for (MRecentItem ri : ris) { String label = ri.getLabel(); @@ -224,7 +255,33 @@ public class DPRecentItems extends DashboardPanel implements EventListener { + @Override + public void onMessage(Integer message) { + MRecentItem.postOnChangedEvent(message); + } + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DashboardPanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DashboardPanel.java index b6c9b48d4c..a6e702eef4 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DashboardPanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DashboardPanel.java @@ -38,4 +38,11 @@ public abstract class DashboardPanel extends Window implements IDashboardPanel { public void updateUI() { } + + /** + * @return true if this dashboard widget uses polling to update its content + */ + public boolean isPooling() { + return false; + } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DashboardRunnable.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DashboardRunnable.java index 59a464f94e..648c6b8914 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DashboardRunnable.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DashboardRunnable.java @@ -18,10 +18,8 @@ import java.util.ArrayList; import java.util.List; import java.util.Locale; import java.util.Properties; -import java.util.logging.Level; import org.adempiere.util.ServerContext; -import org.adempiere.webui.desktop.IDesktop; import org.adempiere.webui.session.SessionContextListener; import org.adempiere.webui.util.ServerPushTemplate; import org.compiere.util.CLogger; @@ -42,9 +40,9 @@ public class DashboardRunnable implements Runnable, Serializable private Desktop desktop; private List dashboardPanels; - private IDesktop appDesktop; private Locale locale; + @SuppressWarnings("unused") private static final CLogger logger = CLogger.getCLogger(DashboardRunnable.class); /** @@ -52,17 +50,15 @@ public class DashboardRunnable implements Runnable, Serializable * @param desktop zk desktop interface * @param appDesktop adempiere desktop interface */ - public DashboardRunnable(Desktop desktop, IDesktop appDesktop) { + public DashboardRunnable(Desktop desktop) { this.desktop = desktop; - this.appDesktop = appDesktop; dashboardPanels = new ArrayList(); locale = Locales.getCurrent(); } - public DashboardRunnable(DashboardRunnable tmp, Desktop desktop, - IDesktop appDesktop) { - this(desktop, appDesktop); + public DashboardRunnable(DashboardRunnable tmp, Desktop desktop) { + this(desktop); this.dashboardPanels = tmp.dashboardPanels; } @@ -70,16 +66,17 @@ public class DashboardRunnable implements Runnable, Serializable { Locales.setThreadLocal(locale); try { - refreshDashboard(); + refreshDashboard(true); } catch (Exception e) { - logger.log(Level.INFO, e.getLocalizedMessage(), (e.getCause() != null ? e.getCause() : e)); +// logger.log(Level.INFO, e.getLocalizedMessage(), (e.getCause() != null ? e.getCause() : e)); + throw new RuntimeException(e); } } /** * Refresh dashboard content */ - public void refreshDashboard() + public void refreshDashboard(boolean pooling) { ServerPushTemplate template = new ServerPushTemplate(desktop); @@ -102,12 +99,14 @@ public class DashboardRunnable implements Runnable, Serializable { ServerContext.setCurrentInstance(ctx); } + for(int i = 0; i < dashboardPanels.size(); i++) { + if (pooling && !dashboardPanels.get(i).isPooling()) + continue; + dashboardPanels.get(i).refresh(template); } - - appDesktop.onServerPush(template); } finally { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DashboardController.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DashboardController.java index 70e273d4f1..b721a572f8 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DashboardController.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DashboardController.java @@ -97,7 +97,7 @@ public class DashboardController implements EventListener { private Anchorchildren maximizedHolder; private DashboardRunnable dashboardRunnable; private ScheduledFuture dashboardFuture; - + public DashboardController() { dashboardLayout = new Anchorlayout(); dashboardLayout.setSclass("dashboard-layout"); @@ -120,7 +120,7 @@ public class DashboardController implements EventListener { if (!dashboardLayout.getDesktop().isServerPushEnabled()) dashboardLayout.getDesktop().enableServerPush(true); - dashboardRunnable = new DashboardRunnable(parent.getDesktop(), desktopImpl); + dashboardRunnable = new DashboardRunnable(parent.getDesktop()); // Dashboard content Vlayout dashboardColumnLayout = null; @@ -416,7 +416,7 @@ public class DashboardController implements EventListener { if (!dashboardRunnable.isEmpty()) { - dashboardRunnable.refreshDashboard(); + dashboardRunnable.refreshDashboard(false); // default Update every one minutes int interval = MSysConfig.getIntValue(MSysConfig.ZK_DASHBOARD_REFRESH_INTERVAL, 60000); @@ -617,7 +617,7 @@ public class DashboardController implements EventListener { } if (!dashboardRunnable.isEmpty()) - dashboardRunnable.refreshDashboard(); + dashboardRunnable.refreshDashboard(false); } } } @@ -626,14 +626,13 @@ public class DashboardController implements EventListener { * * @param page * @param desktop - * @param appDesktop */ - public void onSetPage(Page page, Desktop desktop, IDesktop appDesktop) { + public void onSetPage(Page page, Desktop desktop) { if (dashboardFuture != null && !dashboardFuture.isDone()) { dashboardFuture.cancel(true); DashboardRunnable tmp = dashboardRunnable; - dashboardRunnable = new DashboardRunnable(tmp, desktop, appDesktop); + dashboardRunnable = new DashboardRunnable(tmp, desktop); // default Update every one minutes int interval = MSysConfig.getIntValue(MSysConfig.ZK_DASHBOARD_REFRESH_INTERVAL, 60000); dashboardFuture = Adempiere.getThreadPoolExecutor().scheduleWithFixedDelay(dashboardRunnable, interval, interval, TimeUnit.MILLISECONDS); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java index c0a4ff773e..7f6ffd95ed 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/DefaultDesktop.java @@ -18,6 +18,7 @@ package org.adempiere.webui.desktop; import java.io.Serializable; +import java.util.Map; import java.util.Properties; import org.adempiere.base.event.EventManager; @@ -29,7 +30,6 @@ import org.adempiere.webui.apps.AEnv; import org.adempiere.webui.apps.BusyDialog; import org.adempiere.webui.component.Tabpanel; import org.adempiere.webui.component.ToolBarButton; -import org.adempiere.webui.dashboard.DPActivities; import org.adempiere.webui.event.MenuListener; import org.adempiere.webui.event.ZKBroadCastManager; import org.adempiere.webui.panel.BroadcastMessageWindow; @@ -57,6 +57,8 @@ import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zk.ui.event.EventQueue; +import org.zkoss.zk.ui.event.EventQueues; import org.zkoss.zk.ui.event.Events; import org.zkoss.zk.ui.event.OpenEvent; import org.zkoss.zk.ui.util.Clients; @@ -75,7 +77,7 @@ import org.zkoss.zul.West; * @version $Revision: 0.10 $ * @author Deepak Pansheriya/Vivek - Adding support for message broadcasting */ -public class DefaultDesktop extends TabbedDesktop implements MenuListener, Serializable, EventListener, IServerPushCallback,EventHandler,DesktopCleanup +public class DefaultDesktop extends TabbedDesktop implements MenuListener, Serializable, EventListener, EventHandler,DesktopCleanup { /** * generated serial version ID @@ -119,6 +121,9 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria //subscribing to broadcast event bindEventManager(); ZKBroadCastManager.getBroadCastMgr(); + + EventQueue queue = EventQueues.lookup(ACTIVITIES_EVENT_QUEUE, true); + queue.subscribe(this); } protected Component doCreatePart(Component parent) @@ -242,18 +247,36 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria } } } + else if (eventName.equals(ON_ACTIVITIES_CHANGED_EVENT)) + { + @SuppressWarnings("unchecked") + Map map = (Map) event.getData(); + Integer notice = (Integer) map.get("notice"); + Integer request = (Integer) map.get("request"); + Integer workflow = (Integer) map.get("workflow"); + Integer unprocessed = (Integer) map.get("unprocessed"); + boolean change = false; + if (notice != null && notice.intValue() != noOfNotice) + { + noOfNotice = notice.intValue(); change = true; + } + if (request != null && request.intValue() != noOfRequest) + { + noOfRequest = request.intValue(); change = true; + } + if (workflow != null && workflow.intValue() != noOfWorkflow) + { + noOfWorkflow = workflow.intValue(); change = true; + } + if (unprocessed != null && unprocessed.intValue() != noOfUnprocessed) + { + noOfUnprocessed = unprocessed.intValue(); change = true; + } + if (change) + updateUI(); + } } - public void onServerPush(ServerPushTemplate template) - { - noOfNotice = DPActivities.getNoticeCount(); - noOfRequest = DPActivities.getRequestCount(); - noOfWorkflow = DPActivities.getWorkflowCount(); - noOfUnprocessed = DPActivities.getUnprocessedCount(); - - template.executeAsync(this); - } - /** * * @param page @@ -264,11 +287,11 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria this.page = page; if (dashboardController != null) { - dashboardController.onSetPage(page, layout.getDesktop(), this); + dashboardController.onSetPage(page, layout.getDesktop()); } if (sideController != null) { - sideController.onSetPage(page, layout.getDesktop(), this); + sideController.onSetPage(page, layout.getDesktop()); } } } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java index 15390960f9..535b7e434c 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/desktop/IDesktop.java @@ -20,7 +20,6 @@ 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.compiere.model.MQuery; import org.compiere.util.WebDoc; import org.zkoss.zk.ui.Component; @@ -34,6 +33,8 @@ import org.zkoss.zk.ui.Page; public interface IDesktop extends UIPart { public static final String WINDOWNO_ATTRIBUTE = "desktop.windowno"; + public static final String ACTIVITIES_EVENT_QUEUE = "ActivitiesEventQueue"; + public static final String ON_ACTIVITIES_CHANGED_EVENT = "onActivitiesChanged"; /** * @@ -193,12 +194,4 @@ public interface IDesktop extends UIPart { * User logout from desktop, do clean up */ public void logout(); - - /** - * 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(ServerPushTemplate template); }