From e355e73eff2081395cb00b5d650d55b9b1b18197 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Thu, 1 Aug 2013 01:02:00 +0800 Subject: [PATCH 01/13] IDEMPIERE-1226 Page Optimization: Specify image dimensions. --- .../webui/dashboard/DPFavourites.java | 2 +- .../webui/dashboard/DPRecentItems.java | 4 +- .../webui/desktop/DefaultDesktop.java | 1 + .../webui/panel/AbstractMenuPanel.java | 1 + .../webui/panel/TreeSearchPanel.java | 1 + .../theme/default/css/theme.css.dsp | 54 +++++++++++++++++++ .../theme/default/zul/calendar/calendar.css | 4 ++ 7 files changed, 64 insertions(+), 3 deletions(-) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPFavourites.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPFavourites.java index d827141f56..6b263c30a1 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPFavourites.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/dashboard/DPFavourites.java @@ -95,7 +95,7 @@ public class DPFavourites extends DashboardPanel implements EventListener // Elaine 2008/07/24 Image img = new Image(ThemeManager.getThemeResource("images/Delete24.png")); favToolbar.appendChild(img); - img.setStyle("text-align: right"); + img.setStyle("text-align: right; width:24px; height:24px;"); img.setTooltiptext(Util.cleanAmp(Msg.getMsg(Env.getCtx(), "Delete"))); img.setDroppable(DELETE_FAV_DROPPABLE); img.addEventListener(Events.ON_DROP, 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 5060ddfa06..2fa707fe79 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 @@ -101,14 +101,14 @@ public class DPRecentItems extends DashboardPanel implements EventListener, Tree cmbSearch = new AutoComplete(); cmbSearch.setAutodrop(true); cmbSearch.setId("treeSearchCombo"); + cmbSearch.setSclass("tree-search-combo"); cmbSearch.addEventListener(Events.ON_OK, this); cmbSearch.addEventListener(Events.ON_SELECT, new EventListener>() { diff --git a/org.adempiere.ui.zk/theme/default/css/theme.css.dsp b/org.adempiere.ui.zk/theme/default/css/theme.css.dsp index bda1e55aaa..62f8aaa861 100644 --- a/org.adempiere.ui.zk/theme/default/css/theme.css.dsp +++ b/org.adempiere.ui.zk/theme/default/css/theme.css.dsp @@ -137,12 +137,34 @@ html,body { .menu-href img { padding: 2px; padding-right: 4px; + width: 16px; + height: 16px; +} + +.menu-href-newbtn img { + width: 10px; + height: 10px; +} + +.window-container-toolbar-btn .z-toolbarbutton-cnt img { + width: 22px; + height: 22px; +} + +.tree-search-combo .z-comboitem-img img { + width: 16px; + height: 16px; } .fav-new-btn { margin-left: 4px; } +.fav-new-btn img { + height: 16px; + width: 16px; +} + .disableFilter img { opacity: 0.2; filter: progid : DXImageTransform . Microsoft . Alpha(opacity = 20); @@ -173,6 +195,11 @@ html,body { height: 22px; } +.breadcrumb-toolbar-button img { + width: 22px; + height: 22px; +} + .breadcrumb-record-info { font-size: 12px; } @@ -622,6 +649,10 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none { width: 100%; } +.favourites-box .z-toolbar-start { + float: right; +} + .recentitems-box { width: 90%; margin: auto; @@ -632,6 +663,10 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none { display: block; padding: 2px 0px; } + +.recentitems-box .z-toolbar-start { + float: right; +} .views-box { width: 90%; @@ -648,6 +683,11 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none { padding: 2px 0px; } +.views-box .link img { + height: 16px; + width: 16px; +} + .activities-box { width: 90%; margin: auto; @@ -771,6 +811,11 @@ div.wc-modal, div.wc-modal-none, div.wc-highlighted, div.wc-highlighted-none { display: inline-block; } +.adwindow-detailpane-toolbar .z-toolbarbutton img { + width: 16px; + height: 16px; +} + .adwindow-detailpane-toolbar .z-toolbar-body .docstatus-normal, .adwindow-detailpane-toolbar .z-toolbar-body .docstatus-error { padding-left: 4px; @@ -1543,6 +1588,10 @@ font-size: 0; padding: 1px 4px 1px 4px; } +.menu-search-toggle-on img, .menu-search-toggle-off img { + height: 16px; + width: 16px; +} <%-- workaround for http://jira.idempiere.com/browse/IDEMPIERE-692 --%> .z-combobox-pp { max-height: 200px; @@ -1595,6 +1644,11 @@ table.z-vbox > tbody > tr > td > table { width: 120px !important; } +.performance-indicator img { + height: 120px; + width: 120px; +} + .window-view-pi .performance-indicator { width: 180px !important; } diff --git a/org.adempiere.ui.zk/theme/default/zul/calendar/calendar.css b/org.adempiere.ui.zk/theme/default/zul/calendar/calendar.css index 94843dfb6f..97fa12f460 100644 --- a/org.adempiere.ui.zk/theme/default/zul/calendar/calendar.css +++ b/org.adempiere.ui.zk/theme/default/zul/calendar/calendar.css @@ -96,6 +96,10 @@ margin: -3px; padding-top: 5px; } +.calendar-toolbar .z-toolbarbutton img { + height: 16px; + width: 16px; +} .gecko2 .calendar-toolbar { margin: -3px; } From a4007bf19dab378d54ddf0ac073aebf1277c3ce8 Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Wed, 31 Jul 2013 12:12:10 -0500 Subject: [PATCH 02/13] IDEMPIERE-1227 add UU on clone method --- org.adempiere.base/src/org/compiere/model/GridTabVO.java | 1 + org.adempiere.base/src/org/compiere/model/GridWindowVO.java | 1 + 2 files changed, 2 insertions(+) diff --git a/org.adempiere.base/src/org/compiere/model/GridTabVO.java b/org.adempiere.base/src/org/compiere/model/GridTabVO.java index 2a2f4851da..682c144745 100644 --- a/org.adempiere.base/src/org/compiere/model/GridTabVO.java +++ b/org.adempiere.base/src/org/compiere/model/GridTabVO.java @@ -524,6 +524,7 @@ public class GridTabVO implements Evaluatee, Serializable Env.setContext(Ctx, windowNo, clone.TabNo, GridTab.CTX_AD_Tab_ID, String.valueOf(clone.AD_Tab_ID)); // clone.AD_Tab_ID = AD_Tab_ID; + clone.AD_Tab_UU = AD_Tab_UU; clone.Name = Name; Env.setContext(Ctx, windowNo, clone.TabNo, GridTab.CTX_Name, clone.Name); clone.Description = Description; diff --git a/org.adempiere.base/src/org/compiere/model/GridWindowVO.java b/org.adempiere.base/src/org/compiere/model/GridWindowVO.java index 23684ca28f..d3e86bad80 100644 --- a/org.adempiere.base/src/org/compiere/model/GridWindowVO.java +++ b/org.adempiere.base/src/org/compiere/model/GridWindowVO.java @@ -350,6 +350,7 @@ public class GridWindowVO implements Serializable { clone = new GridWindowVO(ctx, windowNo); clone.AD_Window_ID = AD_Window_ID; + clone.AD_Window_UU = AD_Window_UU; clone.Name = Name; clone.Description = Description; clone.Help = Help; From 92580221f7c78bb46c5dd3853077fa44bb076a9a Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Thu, 1 Aug 2013 17:18:14 +0800 Subject: [PATCH 03/13] IDEMPIERE-1230 Detail pane always created for level 1 tab even when there's no child tab. --- .../webui/adwindow/CompositeADTabbox.java | 65 ++++++++++++++----- 1 file changed, 48 insertions(+), 17 deletions(-) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java index 2bcab4e55a..47afa7c239 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java @@ -61,6 +61,8 @@ public class CompositeADTabbox extends AbstractADTabbox public static final String AD_TABBOX_ON_EDIT_DETAIL_ATTRIBUTE = "ADTabbox.onEditDetail"; private static final String ON_POST_TAB_SELECTION_CHANGED_EVENT = "onPostTabSelectionChanged"; + + private static final String ON_TAB_SELECTION_CHANGED_ECHO_EVENT = "onTabSelectionChangedEcho"; public static final String ON_SELECTION_CHANGED_EVENT = "onSelectionChanged"; @@ -215,6 +217,13 @@ public class CompositeADTabbox extends AbstractADTabbox onPostTabSelectionChanged((Boolean)event.getData()); } }); + + layout.addEventListener(ON_TAB_SELECTION_CHANGED_ECHO_EVENT, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + onTabSelectionChangedEcho((Boolean)event.getData()); + } + }); BreadCrumb breadCrumb = getBreadCrumb(); breadCrumb.addEventListener(Events.ON_CLICK, new EventListener() { @@ -437,16 +446,13 @@ public class CompositeADTabbox extends AbstractADTabbox headerTab.setDetailPaneMode(false); getBreadCrumb().getFirstChild().setVisible(false); - Events.echoEvent(new Event(ON_POST_TAB_SELECTION_CHANGED_EVENT, layout, oldIndex > newIndex)); + Events.postEvent(new Event(ON_POST_TAB_SELECTION_CHANGED_EVENT, layout, oldIndex > newIndex)); } private void onPostTabSelectionChanged(Boolean back) { - if (headerTab instanceof ADTabpanel) { - DetailPane detailPane = headerTab.getDetailPane(); - if (detailPane == null) { - detailPane = createDetailPane(); - } - + if (headerTab instanceof ADTabpanel && !headerTab.getGridTab().isSortTab()) { + + List list = new ArrayList(); int tabIndex = -1; int currentLevel = headerTab.getTabLevel(); for (int i = selectedIndex + 1; i< tabPanelList.size(); i++) { @@ -459,28 +465,53 @@ public class CompositeADTabbox extends AbstractADTabbox } if (tabPanel.getParent() != null) tabPanel.detach(); tabIndex++; - detailPane.setADTabpanel(tabIndex, tabPanel, tabLabel); + Object[] value = new Object[]{tabIndex, tabPanel, tabLabel, Boolean.TRUE}; + list.add(value); tabPanel.setDetailPaneMode(true); } else if (tabLevel > currentLevel ){ tabIndex++; - detailPane.setADTabpanel(tabIndex, tabPanel, tabLabel, false); + Object[] value = new Object[]{tabIndex, tabPanel, tabLabel, Boolean.FALSE}; + list.add(value); tabPanel.setDetailPaneMode(true); } else { break; } } - if (detailPane.getTabcount() > 0 && !headerTab.getGridTab().isSortTab()) { - detailPane.setVflex("true"); - detailPane.setSelectedIndex(0); - if (headerTab.getDetailPane() == null) { - headerTab.setDetailPane(detailPane); - } + if (!list.isEmpty()) { + DetailPane detailPane = headerTab.getDetailPane(); + if (detailPane == null) { + detailPane = createDetailPane(); + } + for (Object[] value : list) { + Boolean b = (Boolean) value[3]; + if (b.booleanValue()) + detailPane.setADTabpanel((Integer)value[0], (IADTabpanel)value[1], (ADTabLabel)value[2]); + else + detailPane.setADTabpanel((Integer)value[0], (IADTabpanel)value[1], (ADTabLabel)value[2], false); + } + if (detailPane.getTabcount() > 0) { + detailPane.setVflex("true"); + detailPane.setSelectedIndex(0); + if (headerTab.getDetailPane() == null) { + headerTab.setDetailPane(detailPane); + } + } + } + + Events.echoEvent(new Event(ON_TAB_SELECTION_CHANGED_ECHO_EVENT, layout, back)); + } + } + + private void onTabSelectionChangedEcho(Boolean back) { + if (headerTab instanceof ADTabpanel) { + DetailPane detailPane = headerTab.getDetailPane(); + + if (detailPane != null && detailPane.getTabcount() > 0) { activateDetailIfVisible(); } } - - updateBreadCrumb(); + updateBreadCrumb(); getBreadCrumb().getFirstChild().setVisible(true); updateTabState(); From bc81a931df61e977ca482436615c1e33f612d3eb Mon Sep 17 00:00:00 2001 From: Elaine Tan Date: Thu, 1 Aug 2013 17:34:50 +0800 Subject: [PATCH 04/13] IDEMPIERE-1231 Add non-persistent attributes support to PO --- .../src/org/compiere/model/PO.java | 22 +++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/PO.java b/org.adempiere.base/src/org/compiere/model/PO.java index b315c158f0..87f5a1d338 100644 --- a/org.adempiere.base/src/org/compiere/model/PO.java +++ b/org.adempiere.base/src/org/compiere/model/PO.java @@ -105,7 +105,7 @@ public abstract class PO /** * */ - private static final long serialVersionUID = 8742545079591136114L; + private static final long serialVersionUID = -341748204028700040L; public static final String LOCAL_TRX_PREFIX = "POSave"; @@ -251,7 +251,9 @@ public abstract class PO private int m_idOld = 0; /** Custom Columns */ private HashMap m_custom = null; - + /** Attributes */ + private HashMap m_attributes = null; + /** Zero Integer */ protected static final Integer I_ZERO = new Integer(0); /** Accounting Columns */ @@ -4515,4 +4517,20 @@ public abstract class PO ois.defaultReadObject(); log = CLogger.getCLogger(getClass()); } + + public void setAttribute(String columnName, Object value) { + if (m_attributes == null) + m_attributes = new HashMap(); + m_attributes.put(columnName, value); + } + + public Object getAttribute(String columnName) { + if (m_attributes != null) + return m_attributes.get(columnName); + return null; + } + + public HashMap getAttributes() { + return m_attributes; + } } // PO From af722e2fd57585e6389ada04c7c0e26170ed36eb Mon Sep 17 00:00:00 2001 From: Elaine Tan Date: Thu, 1 Aug 2013 17:40:13 +0800 Subject: [PATCH 05/13] IDEMPIERE-1231 Add non-persistent attributes support to PO --- .../src/org/compiere/model/MRegistration.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/MRegistration.java b/org.adempiere.base/src/org/compiere/model/MRegistration.java index b3dfe6e5d3..c71dea230c 100644 --- a/org.adempiere.base/src/org/compiere/model/MRegistration.java +++ b/org.adempiere.base/src/org/compiere/model/MRegistration.java @@ -40,7 +40,7 @@ public class MRegistration extends X_A_Registration /** * */ - private static final long serialVersionUID = -5178339895228217372L; + private static final long serialVersionUID = -2301393326847864746L; /** * Standard Constructor @@ -91,7 +91,7 @@ public class MRegistration extends X_A_Registration * Get All Attributes * @return Registration Attributes */ - public MRegistrationAttribute[] getAttributes() + public MRegistrationAttribute[] getRegistrationAttributes() { if (m_allAttributes == null) m_allAttributes = MRegistrationAttribute.getAll(getCtx()); @@ -201,7 +201,7 @@ public class MRegistration extends X_A_Registration saveEx(); int count = 0; // read values for all attributes - MRegistrationAttribute[] attributes = getAttributes(); + MRegistrationAttribute[] attributes = getRegistrationAttributes(); for (int i = 0; i < attributes.length; i++) { MRegistrationAttribute attribute = attributes[i]; From c70293cb5038c63711de32ce065f3ab899625987 Mon Sep 17 00:00:00 2001 From: Elaine Tan Date: Thu, 1 Aug 2013 18:50:27 +0800 Subject: [PATCH 06/13] IDEMPIERE-569 Ticket #1001758: Improve shipping configuration to support external services - sender's company name should retrieved from organization's name instead of description - sender's contact name should retrieved from sales rep's name instead of description --- .../src/org/compiere/model/MShippingTransaction.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/MShippingTransaction.java b/org.adempiere.base/src/org/compiere/model/MShippingTransaction.java index a3019a78e0..6be13af42b 100644 --- a/org.adempiere.base/src/org/compiere/model/MShippingTransaction.java +++ b/org.adempiere.base/src/org/compiere/model/MShippingTransaction.java @@ -319,7 +319,7 @@ public class MShippingTransaction extends X_M_ShippingTransaction /* StringBuilder sql = new StringBuilder(); sql.append("SELECT CompanyName, ContactName, PhoneNumber, EMail, C_Location_ID "); - sql.append("FROM X_ShippingSenderInfo_V "); + sql.append("FROM M_ShippingSenderInfo_V "); sql.append("WHERE M_ShippingTransaction_ID = ?"); PreparedStatement pstmt = null; @@ -355,8 +355,8 @@ public class MShippingTransaction extends X_M_ShippingTransaction MWarehouse warehouse = new MWarehouse(getCtx(), getM_Warehouse_ID(), get_TrxName()); partyInfo = new PartyInfo(); - partyInfo.setCompanyName(sender.getDescription()); - partyInfo.setContactName(senderContact.getDescription()); + partyInfo.setCompanyName(sender.getName()); + partyInfo.setContactName(senderContact.getName()); partyInfo.setPhoneNumber(sender.getInfo().getPhone()); partyInfo.setEmail(senderContact.getEMail()); partyInfo.setLocationId(warehouse.getC_Location_ID()); @@ -376,7 +376,7 @@ public class MShippingTransaction extends X_M_ShippingTransaction /* StringBuilder sql = new StringBuilder(); sql.append("SELECT CompanyName, ContactName, PhoneNumber, EMail, C_Location_ID "); - sql.append("FROM X_ShippingRecipientInfo_V "); + sql.append("FROM M_ShippingRecipientInfo_V "); sql.append("WHERE M_ShippingTransaction_ID = ?"); PreparedStatement pstmt = null; From e32e543324a680b799c7affb09113713bca23c43 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Thu, 1 Aug 2013 21:47:30 +0800 Subject: [PATCH 07/13] IDEMPIERE-1230 Detail pane always created for level 1 tab even when there's no child tab. Further optimization and fixed a event ordering bug in my previous revision for this ticket. --- .../webui/adwindow/CompositeADTabbox.java | 56 +++++++++++-------- 1 file changed, 32 insertions(+), 24 deletions(-) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java index 47afa7c239..dd2e8e156c 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java @@ -446,7 +446,7 @@ public class CompositeADTabbox extends AbstractADTabbox headerTab.setDetailPaneMode(false); getBreadCrumb().getFirstChild().setVisible(false); - Events.postEvent(new Event(ON_POST_TAB_SELECTION_CHANGED_EVENT, layout, oldIndex > newIndex)); + Events.sendEvent(new Event(ON_POST_TAB_SELECTION_CHANGED_EVENT, layout, oldIndex > newIndex)); } private void onPostTabSelectionChanged(Boolean back) { @@ -460,19 +460,13 @@ public class CompositeADTabbox extends AbstractADTabbox int tabLevel = tabPanel.getTabLevel(); ADTabListModel.ADTabLabel tabLabel = tabLabelList.get(i); if ((tabLevel - currentLevel) == 1 || (tabLevel == 0 && currentLevel == 0)) { - if (tabPanel.isActivated() && !tabPanel.isGridView()) { - tabPanel.switchRowPresentation(); - } - if (tabPanel.getParent() != null) tabPanel.detach(); tabIndex++; Object[] value = new Object[]{tabIndex, tabPanel, tabLabel, Boolean.TRUE}; list.add(value); - tabPanel.setDetailPaneMode(true); } else if (tabLevel > currentLevel ){ tabIndex++; Object[] value = new Object[]{tabIndex, tabPanel, tabLabel, Boolean.FALSE}; list.add(value); - tabPanel.setDetailPaneMode(true); } else { break; } @@ -481,22 +475,14 @@ public class CompositeADTabbox extends AbstractADTabbox if (!list.isEmpty()) { DetailPane detailPane = headerTab.getDetailPane(); if (detailPane == null) { - detailPane = createDetailPane(); - } - for (Object[] value : list) { - Boolean b = (Boolean) value[3]; - if (b.booleanValue()) - detailPane.setADTabpanel((Integer)value[0], (IADTabpanel)value[1], (ADTabLabel)value[2]); - else - detailPane.setADTabpanel((Integer)value[0], (IADTabpanel)value[1], (ADTabLabel)value[2], false); - } - if (detailPane.getTabcount() > 0) { - detailPane.setVflex("true"); - detailPane.setSelectedIndex(0); - if (headerTab.getDetailPane() == null) { - headerTab.setDetailPane(detailPane); - } + detailPane = createDetailPane(); } + detailPane.setAttribute("detailpane.tablist", list); + + detailPane.setVflex("true"); + if (headerTab.getDetailPane() == null) { + headerTab.setDetailPane(detailPane); + } } Events.echoEvent(new Event(ON_TAB_SELECTION_CHANGED_ECHO_EVENT, layout, back)); @@ -507,8 +493,30 @@ public class CompositeADTabbox extends AbstractADTabbox if (headerTab instanceof ADTabpanel) { DetailPane detailPane = headerTab.getDetailPane(); - if (detailPane != null && detailPane.getTabcount() > 0) { - activateDetailIfVisible(); + if (detailPane != null) { + @SuppressWarnings("unchecked") + List list = (List) detailPane.removeAttribute("detailpane.tablist"); + if (list != null && !list.isEmpty()) { + int currentLevel = headerTab.getTabLevel(); + for (Object[] value : list) { + int tabIndex = (Integer) value[0]; + IADTabpanel tabPanel = (IADTabpanel) value[1]; + ADTabLabel tabLabel = (ADTabLabel) value[2] ; + Boolean enable = (Boolean) value[3]; + + int tabLevel = tabPanel.getTabLevel(); + if ((tabLevel - currentLevel) == 1 || (tabLevel == 0 && currentLevel == 0)) { + if (tabPanel.isActivated() && !tabPanel.isGridView()) { + tabPanel.switchRowPresentation(); + } + if (tabPanel.getParent() != null) tabPanel.detach(); + } + tabPanel.setDetailPaneMode(true); + detailPane.setADTabpanel(tabIndex, tabPanel, tabLabel, enable.booleanValue()); + } + detailPane.setSelectedIndex(0); + activateDetailIfVisible(); + } } } updateBreadCrumb(); From 31426fbf60b4345fcbdbe4f0dbf292e589b33761 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Thu, 1 Aug 2013 21:58:23 +0800 Subject: [PATCH 08/13] IDEMPIERE-1231 Add non-persistent attributes support to PO. Peer review. --- .../src/org/compiere/model/MRegistration.java | 6 +++--- org.adempiere.base/src/org/compiere/model/PO.java | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/MRegistration.java b/org.adempiere.base/src/org/compiere/model/MRegistration.java index c71dea230c..b3dfe6e5d3 100644 --- a/org.adempiere.base/src/org/compiere/model/MRegistration.java +++ b/org.adempiere.base/src/org/compiere/model/MRegistration.java @@ -40,7 +40,7 @@ public class MRegistration extends X_A_Registration /** * */ - private static final long serialVersionUID = -2301393326847864746L; + private static final long serialVersionUID = -5178339895228217372L; /** * Standard Constructor @@ -91,7 +91,7 @@ public class MRegistration extends X_A_Registration * Get All Attributes * @return Registration Attributes */ - public MRegistrationAttribute[] getRegistrationAttributes() + public MRegistrationAttribute[] getAttributes() { if (m_allAttributes == null) m_allAttributes = MRegistrationAttribute.getAll(getCtx()); @@ -201,7 +201,7 @@ public class MRegistration extends X_A_Registration saveEx(); int count = 0; // read values for all attributes - MRegistrationAttribute[] attributes = getRegistrationAttributes(); + MRegistrationAttribute[] attributes = getAttributes(); for (int i = 0; i < attributes.length; i++) { MRegistrationAttribute attribute = attributes[i]; diff --git a/org.adempiere.base/src/org/compiere/model/PO.java b/org.adempiere.base/src/org/compiere/model/PO.java index 87f5a1d338..daa6810f66 100644 --- a/org.adempiere.base/src/org/compiere/model/PO.java +++ b/org.adempiere.base/src/org/compiere/model/PO.java @@ -4518,19 +4518,19 @@ public abstract class PO log = CLogger.getCLogger(getClass()); } - public void setAttribute(String columnName, Object value) { + public void set_Attribute(String columnName, Object value) { if (m_attributes == null) m_attributes = new HashMap(); m_attributes.put(columnName, value); } - public Object getAttribute(String columnName) { + public Object get_Attribute(String columnName) { if (m_attributes != null) return m_attributes.get(columnName); return null; } - public HashMap getAttributes() { + public HashMap get_Attributes() { return m_attributes; } } // PO From 2952930ab4715cc6b6f38fdc3faf74263678a865 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Thu, 1 Aug 2013 23:28:17 +0800 Subject: [PATCH 09/13] IDEMPIERE-1230 Detail pane always created for level 1 tab even when there's no child tab. Further optimization and fixed problem with sortTab. --- .../src/org/compiere/model/GridTab.java | 2 +- .../adempiere/webui/adwindow/ADTabpanel.java | 13 ++++-- .../webui/adwindow/CompositeADTabbox.java | 44 ++++++++++++++----- 3 files changed, 43 insertions(+), 16 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/GridTab.java b/org.adempiere.base/src/org/compiere/model/GridTab.java index b5f1b4aca6..5b4181ba97 100644 --- a/org.adempiere.base/src/org/compiere/model/GridTab.java +++ b/org.adempiere.base/src/org/compiere/model/GridTab.java @@ -2423,7 +2423,7 @@ public class GridTab implements DataStatusListener, Evaluatee, Serializable // Table Open? if (!m_mTable.isOpen()) { - log.severe ("Table not open"); + log.log (Level.SEVERE, "Table not open", new Exception("Table not open")); return -1; } // Row Count diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java index b4af00a3d2..a150807ec3 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/ADTabpanel.java @@ -92,6 +92,7 @@ import org.zkoss.zul.Cell; import org.zkoss.zul.Center; import org.zkoss.zul.DefaultTreeNode; import org.zkoss.zul.Div; +import org.zkoss.zul.RowRenderer; import org.zkoss.zul.Separator; import org.zkoss.zul.South; import org.zkoss.zul.Space; @@ -177,7 +178,7 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer private GridTabDataBinder dataBinder; - private boolean activated = false; + protected boolean activated = false; private Group currentGroup; @@ -1517,9 +1518,15 @@ DataStatusListener, IADTabpanel, IdSpace, IFieldEditorContainer } } else if (tabPanel != null && !tabPanel.getGridTab().isCurrent()) { tabPanel.activate(true); + } else if (tabPanel != null && tabPanel.isGridView()) { + //ensure row indicator is not lost + RowRenderer renderer = tabPanel.getGridView().getListbox().getRowRenderer(); + GridTabRowRenderer gtr = (GridTabRowRenderer)renderer; + org.zkoss.zul.Row row = gtr.getCurrentRow(); + if (row != null) + gtr.setCurrentRow(row); } - } - + } } /** diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java index dd2e8e156c..50af43f98f 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/adwindow/CompositeADTabbox.java @@ -188,8 +188,10 @@ public class CompositeADTabbox extends AbstractADTabbox if (formView && headerTab.isGridView()) { headerTab.switchRowPresentation(); } - - headerTab.getGridTab().setCurrentRow(row, true); + + if (!headerTab.getGridTab().isSortTab()) + headerTab.getGridTab().setCurrentRow(row, true); + if (headerTab.isGridView()) { if (headerTab.getGridTab().isNew() || headerTab.needSave(true, false)) { headerTab.getGridView().onEditCurrentRow(); @@ -450,8 +452,7 @@ public class CompositeADTabbox extends AbstractADTabbox } private void onPostTabSelectionChanged(Boolean back) { - if (headerTab instanceof ADTabpanel && !headerTab.getGridTab().isSortTab()) { - + if (headerTab instanceof ADTabpanel && !headerTab.getGridTab().isSortTab()) { List list = new ArrayList(); int tabIndex = -1; int currentLevel = headerTab.getTabLevel(); @@ -483,10 +484,9 @@ public class CompositeADTabbox extends AbstractADTabbox if (headerTab.getDetailPane() == null) { headerTab.setDetailPane(detailPane); } - } - - Events.echoEvent(new Event(ON_TAB_SELECTION_CHANGED_ECHO_EVENT, layout, back)); - } + } + } + Events.echoEvent(new Event(ON_TAB_SELECTION_CHANGED_ECHO_EVENT, layout, back)); } private void onTabSelectionChangedEcho(Boolean back) { @@ -514,8 +514,22 @@ public class CompositeADTabbox extends AbstractADTabbox tabPanel.setDetailPaneMode(true); detailPane.setADTabpanel(tabIndex, tabPanel, tabLabel, enable.booleanValue()); } - detailPane.setSelectedIndex(0); - activateDetailIfVisible(); + if (back == null || !back.booleanValue()) { + detailPane.setSelectedIndex(0); + activateDetailIfVisible(); + } else { + if (((ADTabpanel) headerTab).isDetailVisible()) { + IADTabpanel selectDetailPanel = detailPane.getSelectedADTabpanel(); + if (!selectDetailPanel.isVisible()) { + selectDetailPanel.setVisible(true); + } + if (!selectDetailPanel.isGridView()) { + selectDetailPanel.switchRowPresentation(); + } + if (selectDetailPanel instanceof ADTabpanel) + ((ADTabpanel)selectDetailPanel).activated = true; + } + } } } } @@ -780,8 +794,9 @@ public class CompositeADTabbox extends AbstractADTabbox } else { tabPanel.query(false, 0, 0); } - if (!tabPanel.isVisible()) + if (!tabPanel.isVisible()) { tabPanel.setVisible(true); + } if (!tabPanel.isGridView()) { tabPanel.switchRowPresentation(); } @@ -791,7 +806,12 @@ public class CompositeADTabbox extends AbstractADTabbox headerTab.getDetailPane().updateToolbar(false, true); } else { tabPanel.dynamicDisplay(0); - } + RowRenderer renderer = tabPanel.getGridView().getListbox().getRowRenderer(); + GridTabRowRenderer gtr = (GridTabRowRenderer)renderer; + Row row = gtr.getCurrentRow(); + if (row != null) + gtr.setCurrentRow(row); + } } private void showLastError() { From 6cceb116282ae7a31a61db5fc45fa45b6b479508 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Fri, 2 Aug 2013 16:09:45 +0800 Subject: [PATCH 10/13] IDEMPIERE-1235 Add API to support the sharing of non-transactional connection within a single execution thread. --- .../compiere/db/CallableStatementProxy.java | 8 +- .../compiere/db/PreparedStatementProxy.java | 6 +- .../src/org/compiere/db/StatementProxy.java | 13 +-- .../db/util/AutoCommitConnectionBroker.java | 81 +++++++++++++++++++ 4 files changed, 88 insertions(+), 20 deletions(-) create mode 100644 org.adempiere.base/src/org/idempiere/db/util/AutoCommitConnectionBroker.java diff --git a/org.adempiere.base/src/org/compiere/db/CallableStatementProxy.java b/org.adempiere.base/src/org/compiere/db/CallableStatementProxy.java index 4ed3b4ff43..91e8e21bad 100644 --- a/org.adempiere.base/src/org/compiere/db/CallableStatementProxy.java +++ b/org.adempiere.base/src/org/compiere/db/CallableStatementProxy.java @@ -13,13 +13,12 @@ package org.compiere.db; import java.sql.Connection; -import java.sql.ResultSet; import java.util.logging.Level; import org.adempiere.exceptions.DBException; import org.compiere.util.CStatementVO; -import org.compiere.util.DB; import org.compiere.util.Trx; +import org.idempiere.db.util.AutoCommitConnectionBroker; /** * Dynamic proxy for the CCallableStatement @@ -51,10 +50,7 @@ public class CallableStatementProxy extends PreparedStatementProxy { } else { - if (p_vo.getResultSetConcurrency() == ResultSet.CONCUR_UPDATABLE) - m_conn = DB.getConnectionRW (); - else - m_conn = DB.getConnectionRO(); + m_conn = AutoCommitConnectionBroker.getConnection(); conn = m_conn; } if (conn == null) diff --git a/org.adempiere.base/src/org/compiere/db/PreparedStatementProxy.java b/org.adempiere.base/src/org/compiere/db/PreparedStatementProxy.java index b744aa912c..457305122e 100644 --- a/org.adempiere.base/src/org/compiere/db/PreparedStatementProxy.java +++ b/org.adempiere.base/src/org/compiere/db/PreparedStatementProxy.java @@ -24,6 +24,7 @@ import org.compiere.util.CCachedRowSet; import org.compiere.util.CStatementVO; import org.compiere.util.DB; import org.compiere.util.Trx; +import org.idempiere.db.util.AutoCommitConnectionBroker; /** * Dynamic proxy for the CPreparedStatement interface @@ -60,10 +61,7 @@ public class PreparedStatementProxy extends StatementProxy { if (trx != null) { conn = trx.getConnection(); } else { - if (p_vo.getResultSetConcurrency() == ResultSet.CONCUR_UPDATABLE) - m_conn = DB.getConnectionRW(); - else - m_conn = DB.getConnectionRO(); + m_conn = AutoCommitConnectionBroker.getConnection(); conn = m_conn; } if (conn == null) diff --git a/org.adempiere.base/src/org/compiere/db/StatementProxy.java b/org.adempiere.base/src/org/compiere/db/StatementProxy.java index f3cc7af0e9..13cb249deb 100644 --- a/org.adempiere.base/src/org/compiere/db/StatementProxy.java +++ b/org.adempiere.base/src/org/compiere/db/StatementProxy.java @@ -31,6 +31,7 @@ import org.compiere.util.CStatementVO; import org.compiere.util.DB; import org.compiere.util.DisplayType; import org.compiere.util.Trx; +import org.idempiere.db.util.AutoCommitConnectionBroker; /** * @@ -152,10 +153,7 @@ public class StatementProxy implements InvocationHandler { } else { - if (p_vo.getResultSetConcurrency() == ResultSet.CONCUR_UPDATABLE) - m_conn = DB.getConnectionRW (); - else - m_conn = DB.getConnectionRO(); + m_conn = AutoCommitConnectionBroker.getConnection(); conn = m_conn; } if (conn == null) @@ -183,12 +181,7 @@ public class StatementProxy implements InvocationHandler { } finally { if (m_conn != null) { - try - { - m_conn.close(); - } - catch (Exception e) - {} + AutoCommitConnectionBroker.releaseConnection(m_conn); } m_conn = null; p_stmt = null; diff --git a/org.adempiere.base/src/org/idempiere/db/util/AutoCommitConnectionBroker.java b/org.adempiere.base/src/org/idempiere/db/util/AutoCommitConnectionBroker.java new file mode 100644 index 0000000000..a8243e9471 --- /dev/null +++ b/org.adempiere.base/src/org/idempiere/db/util/AutoCommitConnectionBroker.java @@ -0,0 +1,81 @@ +/****************************************************************************** + * 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.idempiere.db.util; + +import java.sql.Connection; +import java.sql.SQLException; + +import org.compiere.util.DB; + +/** + * This class managed the sharing of non-transactional connection per thread. + * @author hengsin + * + */ +public class AutoCommitConnectionBroker { + private static ThreadLocal threadLocalConnection = new ThreadLocal() { + protected ConnectionReference initialValue() + { + return null; + } + }; + + /** + * Retrieve non-transactional connection for current thread. + * If none have been allocated yet, a new one will be created from the connection pool. + * @return Connection + */ + public static Connection getConnection() { + ConnectionReference connReference = threadLocalConnection.get(); + if (connReference != null) { + connReference.referenceCount++; + return connReference.connection; + } else { + Connection connection = DB.createConnection(true, false, Connection.TRANSACTION_READ_COMMITTED); + connReference = new ConnectionReference(connection); + threadLocalConnection.set(connReference); + return connection; + } + } + + /** + * Release connection. The connection goes back to pool if reference count is zero. + * @param conn + */ + public static void releaseConnection(Connection conn) { + ConnectionReference connReference = threadLocalConnection.get(); + if (connReference != null && connReference.connection == conn) { + connReference.referenceCount--; + if (connReference.referenceCount <= 0) { + threadLocalConnection.set(null); + try { + connReference.connection.close(); + } catch (SQLException e) {} + } + } else { + try { + conn.close(); + } catch (SQLException e) {} + } + } + + private static class ConnectionReference { + protected Connection connection; + protected int referenceCount; + protected ConnectionReference(Connection conn) { + connection = conn; + referenceCount = 1; + } + } +} From ff167fd130e0ba17ef88cf5d5dde3e5009c716d1 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Fri, 2 Aug 2013 22:56:46 +0800 Subject: [PATCH 11/13] IDEMPIERE-1044 Load testing. Fixed MCountry.getDefault(ctx) not thread safe. --- .../src/org/compiere/model/MCountry.java | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/org.adempiere.base/src/org/compiere/model/MCountry.java b/org.adempiere.base/src/org/compiere/model/MCountry.java index 2531b2a4ec..e8572b8774 100644 --- a/org.adempiere.base/src/org/compiere/model/MCountry.java +++ b/org.adempiere.base/src/org/compiere/model/MCountry.java @@ -58,8 +58,7 @@ public final class MCountry extends X_C_Country */ public static MCountry get (Properties ctx, int C_Country_ID) { - if (s_countries == null || s_countries.size() == 0) - loadAllCountries(ctx); + loadAllCountriesIfNeeded(ctx); String key = String.valueOf(C_Country_ID); MCountry c = (MCountry)s_countries.get(key); if (c != null) @@ -80,8 +79,7 @@ public final class MCountry extends X_C_Country */ public static MCountry getDefault (Properties ctx) { - if (s_countries == null || s_countries.size() == 0) - loadAllCountries(ctx); + loadAllCountriesIfNeeded(ctx); return s_default; } // get @@ -92,14 +90,19 @@ public final class MCountry extends X_C_Country */ public static MCountry[] getCountries(Properties ctx) { - if (s_countries == null || s_countries.size() == 0) - loadAllCountries(ctx); + loadAllCountriesIfNeeded(ctx); MCountry[] retValue = new MCountry[s_countries.size()]; s_countries.values().toArray(retValue); Arrays.sort(retValue, new MCountry(ctx, 0, null)); return retValue; } // getCountries + private static synchronized void loadAllCountriesIfNeeded(Properties ctx) { + if (s_countries == null || s_countries.isEmpty()) { + loadAllCountries(ctx); + } + } + /** * Load Countries. * Set Default Language to Client Language From 99acbcf99d551b6c2e08033a2feaaef04efdf83e Mon Sep 17 00:00:00 2001 From: Carlos Ruiz Date: Fri, 2 Aug 2013 16:56:33 -0500 Subject: [PATCH 12/13] IDEMPIERE-1162 / fix iDempiereDaily build broken --- migration/i1.0c-release/oracle/201307301502_IDEMPIERE-1162.sql | 3 +++ 1 file changed, 3 insertions(+) diff --git a/migration/i1.0c-release/oracle/201307301502_IDEMPIERE-1162.sql b/migration/i1.0c-release/oracle/201307301502_IDEMPIERE-1162.sql index dbf3ad9488..ee2aa76831 100644 --- a/migration/i1.0c-release/oracle/201307301502_IDEMPIERE-1162.sql +++ b/migration/i1.0c-release/oracle/201307301502_IDEMPIERE-1162.sql @@ -1,3 +1,6 @@ +SET SQLBLANKLINES ON +SET DEFINE OFF + -- Jul 30, 2013 3:02:24 PM COT -- IDEMPIERE-1162 UPDATE R_MailText SET MailText='Dear @Name@, From ccb801effd91ac41f8c91f0753c4927a449644e6 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Sat, 3 Aug 2013 08:36:21 +0800 Subject: [PATCH 13/13] IDEMPIERE-1244 Use client side timer instead of server thread for the auto refresh of dashboard gadget. --- .../webui/dashboard/DashboardRunnable.java | 3 +- .../webui/desktop/DashboardController.java | 34 +++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) 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 47524de587..4f9e70da61 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 @@ -84,7 +84,8 @@ public class DashboardRunnable implements Runnable, Serializable public void run() { - if (dashboardPanels != null && desktop != null && desktop.get() != null) + if (dashboardPanels != null && desktop != null && desktop.get() != null + && desktop.get().isAlive() && desktop.get().isServerPushEnabled()) { Locales.setThreadLocal(locale); try { 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 feb947e2c2..818358763b 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 @@ -23,8 +23,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.concurrent.ScheduledFuture; -import java.util.concurrent.TimeUnit; import java.util.logging.Level; import org.adempiere.webui.apps.AEnv; @@ -37,7 +35,6 @@ import org.adempiere.webui.report.HTMLExtension; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.window.ZkReportViewerProvider; -import org.compiere.Adempiere; import org.compiere.model.I_AD_Menu; import org.compiere.model.MDashboardContent; import org.compiere.model.MDashboardPreference; @@ -71,6 +68,7 @@ import org.zkoss.zul.Include; import org.zkoss.zul.Panel; import org.zkoss.zul.Panelchildren; import org.zkoss.zul.Separator; +import org.zkoss.zul.Timer; import org.zkoss.zul.Toolbar; import org.zkoss.zul.Toolbarbutton; import org.zkoss.zul.Vlayout; @@ -90,7 +88,7 @@ public class DashboardController implements EventListener { private Anchorlayout dashboardLayout; private Anchorchildren maximizedHolder; private DashboardRunnable dashboardRunnable; - private ScheduledFuture dashboardFuture; + private Timer dashboardTimer; private final static int DEFAULT_DASHBOARD_WIDTH = 95; @@ -386,7 +384,18 @@ public class DashboardController implements EventListener { // default Update every one minutes int interval = MSysConfig.getIntValue(MSysConfig.ZK_DASHBOARD_REFRESH_INTERVAL, 60000); - dashboardFuture = Adempiere.getThreadPoolExecutor().scheduleWithFixedDelay(dashboardRunnable, interval, interval, TimeUnit.MILLISECONDS); + dashboardTimer = new Timer(); + dashboardTimer.setDelay(interval); + dashboardTimer.setRepeats(true); + dashboardTimer.addEventListener(Events.ON_TIMER, new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + if (dashboardRunnable != null && !dashboardRunnable.isEmpty()) { + dashboardRunnable.run(); + } + } + }); + dashboardTimer.setPage(parent.getPage()); } } @@ -596,15 +605,11 @@ public class DashboardController implements EventListener { * @param desktop */ public void onSetPage(Page page, Desktop desktop) { - if (dashboardFuture != null && !dashboardFuture.isDone()) { - dashboardFuture.cancel(true); - Adempiere.getThreadPoolExecutor().remove((Runnable) dashboardFuture); + if (dashboardTimer != null) { DashboardRunnable tmp = dashboardRunnable; 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); + dashboardTimer.setPage(page); } } @@ -612,10 +617,9 @@ public class DashboardController implements EventListener { * clean up for logout */ public void onLogOut() { - if (dashboardFuture != null && !dashboardFuture.isDone()) { - dashboardFuture.cancel(true); - Adempiere.getThreadPoolExecutor().remove((Runnable) dashboardFuture); - dashboardFuture = null; + if (dashboardTimer != null) { + dashboardTimer.detach(); + dashboardTimer = null; } if (dashboardRunnable != null) { dashboardRunnable = null;