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