From d4c36857618feade47cba16e2f37da2885c9bfc8 Mon Sep 17 00:00:00 2001 From: hengsin Date: Thu, 26 Jan 2023 18:20:03 +0800 Subject: [PATCH] IDEMPIERE-5520 Navigation between Tabs leave Detached DOM objects (Leak) (#1648) * IDEMPIERE-5520 Navigation between Tabs leave Detached DOM objects (Leak) - Fix NPE * IDEMPIERE-5520 Navigation between Tabs leave Detached DOM objects (Leak) - refine NPE fix - fix a potential leak in billboard * IDEMPIERE-5520 Navigation between Tabs leave Detached DOM objects (Leak) - reduce detached html elemenet leak for change role --- .../org/adempiere/webui/AdempiereWebUI.java | 16 ++++--- .../src/org/adempiere/webui/apps/AEnv.java | 3 +- .../org/adempiere/webui/component/Tab.java | 9 +++- .../webui/desktop/DefaultDesktop.java | 38 ++++++++++++++-- .../org/adempiere/webui/desktop/IDesktop.java | 43 +++++++++++++++++++ .../adempiere/webui/part/WindowContainer.java | 5 ++- .../webui/util/ServerPushTemplate.java | 3 ++ .../chart/ChartRendererServiceImpl.java | 8 ++-- 8 files changed, 107 insertions(+), 18 deletions(-) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java index c332cb836d..9937b58b1c 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/AdempiereWebUI.java @@ -628,17 +628,19 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb if (Env.getCtx().get(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER) != null) properties.put(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER, Env.getCtx().get(ServerContextURLHandler.SERVER_CONTEXT_URL_HANDLER)); + //desktop cleanup + IDesktop appDesktop = getAppDeskop(); + if (appDesktop != null) + appDesktop.logout(T -> {if (T) asyncChangeRole(httpRequest.getSession(), locale, properties);}); + } + + private void asyncChangeRole(HttpSession httpSession, Locale locale, Properties properties) { //stop key listener if (keyListener != null) { keyListener.detach(); keyListener = null; } - //desktop cleanup - IDesktop appDesktop = getAppDeskop(); - if (appDesktop != null) - appDesktop.logout(); - //remove all children component getChildren().clear(); @@ -648,7 +650,7 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb this.setPage(page); //clear session attributes - Enumeration attributes = httpRequest.getSession().getAttributeNames(); + Enumeration attributes = httpSession.getAttributeNames(); while(attributes.hasMoreElements()) { String attribute = attributes.nextElement(); @@ -656,7 +658,7 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb if (attribute.contains("zkoss.")) continue; - httpRequest.getSession().removeAttribute(attribute); + httpSession.removeAttribute(attribute); } //logout ad_session diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AEnv.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AEnv.java index d243c65555..9ca76e94c5 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AEnv.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/apps/AEnv.java @@ -80,6 +80,7 @@ import org.zkoss.zk.ui.Execution; import org.zkoss.zk.ui.Executions; import org.zkoss.zk.ui.event.Event; import org.zkoss.zk.ui.event.EventListener; +import org.zkoss.zul.Button; import org.zkoss.zul.impl.InputElement; import com.lowagie.text.DocumentException; @@ -881,7 +882,7 @@ public final class AEnv * @param c */ public static void detachInputElement(Component c) { - if (c instanceof InputElement) { + if (c instanceof InputElement || c instanceof Button) { c.detach(); } if (c.getChildren().size() > 0) { diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Tab.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Tab.java index ecbe201460..7a55352402 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Tab.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/component/Tab.java @@ -184,15 +184,17 @@ public class Tab extends org.zkoss.zul.Tab } //Workaround for detached HTML input element leak + boolean attached = getDesktop() != null; if (panel != null) { - Executions.schedule(getDesktop(), e -> panel.detach(), new Event("onCloseLinkedPanel")); + if (attached) + Executions.schedule(getDesktop(), e -> panel.detach(), new Event("onCloseLinkedPanel")); } detach(); if (panel != null) { //Workaround for detached HTML input element leak - if (panel.getChildren().size() > 0) { + if (attached && panel.getChildren().size() > 0) { Component[] childs = panel.getChildren().toArray(new Component[0]); for(Component c : childs) { AEnv.detachInputElement(c); @@ -204,6 +206,9 @@ public class Tab extends org.zkoss.zul.Tab if (include instanceof Include) { include.detach(); } + + if (!attached) + panel.detach(); } } 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 96498b0b63..9d9aa150de 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 @@ -30,6 +30,7 @@ import org.adempiere.base.event.EventManager; import org.adempiere.base.event.IEventManager; import org.adempiere.base.event.IEventTopics; import org.adempiere.model.MBroadcastMessage; +import org.adempiere.util.Callback; import org.adempiere.webui.ClientInfo; import org.adempiere.webui.LayoutUtils; import org.adempiere.webui.adwindow.ADWindow; @@ -806,7 +807,30 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria return layout; } + @Override public void logout() { + logout(null); + } + + @Override + public void logout(Callback callback) { + if (layout != null && layout.getDesktop() != null + && Executions.getCurrent() != null && Executions.getCurrent().getNativeRequest() != null) { + //close all tabs + List tabs = windowContainer.getComponent().getTabs().getChildren(); + int end = tabs.size() - 1; + for (int i = end; i >= 0; i--) { + ((Tab)tabs.get( i )).close(); + } + AEnv.detachInputElement(layout); + //schedule async logout + Executions.schedule(layout.getDesktop(), e -> asyncLogout(callback), new Event("onAsyncLogout")); + } else { + asyncLogout(callback); + } + } + + private void asyncLogout(Callback callback) { unbindEventManager(); if (dashboardController != null) { dashboardController.onLogOut(); @@ -817,10 +841,17 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria sideController.onLogOut(); sideController = null; } - if (layout != null) { - layout.detach(); + + if (callback != null) { + if (layout != null && layout.getDesktop() != null + && Executions.getCurrent() != null && Executions.getCurrent().getNativeRequest() != null) { + Executions.schedule(layout.getDesktop(), e -> callback.onCallback(Boolean.TRUE), new Event("onAsyncLogoutCallback")); + } else { + callback.onCallback(Boolean.TRUE); + } } - layout = null; + + layout = null; pnlHead = null; max = null; m_desktop = null; @@ -1012,6 +1043,7 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria updateHelpContext(X_AD_CtxHelp.CTXTYPE_Task, taskId); } + @Override public boolean isPendingWindow() { List windows = getWindows(); if (windows != null) { 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 97f3fbf163..0e7841e2e3 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 @@ -201,17 +201,60 @@ public interface IDesktop extends UIPart { */ public void logout(); + /** + * + * @param callback callback after logout + */ + public default void logout(Callback callback) { + logout(); + if (callback != null) { + callback.onCallback(Boolean.TRUE); + } + } + + /** + * update help content in help/info panel + * @param ctxTypes + * @param recordId + */ public void updateHelpContext(String ctxType, int recordId); + /** + * update tooltip content in help/info panel + * @param gridField + */ public void updateHelpTooltip(GridField gridField); + /** + * update tooltip content in help/info panel + * @param hdr + * @param desc + * @param help + * @param otherContent + */ public void updateHelpTooltip(String hdr, String desc, String help, String otherContent); + /** + * update quick info (status line) in help/info panel + * @param gridTab + */ public void updateHelpQuickInfo(GridTab gridTab); + /** + * + * @return true if there are changes not save yet + */ public boolean isPendingWindow(); + /** + * update tab title by windowNo + * @param title + * @param windowNo + */ public void setTabTitle(String title, int windowNo); + /** + * render home tab + */ public void renderHomeTab(); } diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/part/WindowContainer.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/part/WindowContainer.java index fa355fd35d..8568bfd532 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/part/WindowContainer.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/part/WindowContainer.java @@ -424,7 +424,7 @@ public class WindowContainer extends AbstractUIPart implements EventListener= tabbox.getTabs().getChildren().size()) + return; + org.zkoss.zul.Tabs tabs = tabbox.getTabs(); Tab tab = (Tab) tabs.getChildren().get(tabNo); setTabTitle(title, tab); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/ServerPushTemplate.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/ServerPushTemplate.java index b165db6aef..fe0a0e3179 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/ServerPushTemplate.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/ServerPushTemplate.java @@ -45,6 +45,9 @@ public class ServerPushTemplate { * @param callback */ public void executeAsync(final IServerPushCallback callback) { + if (!desktop.isAlive()) + return; + try { EventListener task = new EventListener() { @Override diff --git a/org.idempiere.zk.billboard.chart/src/org/idempiere/zk/billboard/chart/ChartRendererServiceImpl.java b/org.idempiere.zk.billboard.chart/src/org/idempiere/zk/billboard/chart/ChartRendererServiceImpl.java index c7a65768d0..d04225f7c3 100644 --- a/org.idempiere.zk.billboard.chart/src/org/idempiere/zk/billboard/chart/ChartRendererServiceImpl.java +++ b/org.idempiere.zk.billboard.chart/src/org/idempiere/zk/billboard/chart/ChartRendererServiceImpl.java @@ -188,9 +188,9 @@ public class ChartRendererServiceImpl implements IChartRendererService { if (Executions.getCurrent() != null) { - String script = "var parent = jq('#" + parent.getUuid() + "');"; - script += "var billboard = parent.children().first(); "; - script += "var div = parent.children().eq(1); "; + String script = "(function() {let parent = jq('#" + parent.getUuid() + "');"; + script += "let billboard = parent.children().first(); "; + script += "let div = parent.children().eq(1); "; script += "if (billboard.children().length == 0) {"; script += "div.show(); "; script += "billboard.hide(); "; @@ -198,7 +198,7 @@ public class ChartRendererServiceImpl implements IChartRendererService { script += "else {"; script += "div.hide(); "; script += "billboard.show(); "; - script += "}"; + script += "}})();"; Clients.response(new AuScript(script)); }