diff --git a/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/AtmosphereServerPush.java b/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/AtmosphereServerPush.java index 28e7828c70..60b1b33420 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/AtmosphereServerPush.java +++ b/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/AtmosphereServerPush.java @@ -301,6 +301,14 @@ public class AtmosphereServerPush implements ServerPush { startClientPush(desktop.get()); } + /** + * + * @return true if it is holding an atmosphere resource + */ + public boolean hasAtmosphereResource() { + return this.resource.get() != null; + } + private class Schedule { private EventListener task; private T event; 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 dc79a0b3e3..d203e00526 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 @@ -44,6 +44,7 @@ import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ITheme; import org.adempiere.webui.theme.ThemeManager; import org.adempiere.webui.util.BrowserToken; +import org.adempiere.webui.util.DesktopWatchDog; import org.adempiere.webui.util.UserPreference; import org.compiere.Adempiere; import org.compiere.model.MRole; @@ -140,12 +141,20 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb public void onCreate() { - this.getPage().setTitle(ThemeManager.getBrowserTitle()); + String ping = Executions.getCurrent().getHeader("X-PING"); + if (!Util.isEmpty(ping, true)) + { + cleanupForPing(); + return; + } + + this.getPage().setTitle(ThemeManager.getBrowserTitle()); Executions.getCurrent().getDesktop().enableServerPush(true); + DesktopWatchDog.addDesktop(Executions.getCurrent().getDesktop()); SessionManager.setSessionApplication(this); - Session session = Executions.getCurrent().getDesktop().getSession(); + final Session session = Executions.getCurrent().getDesktop().getSession(); Properties ctx = Env.getCtx(); langSession = Env.getContext(ctx, Env.LANGUAGE); @@ -171,9 +180,28 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb Executions.getCurrent().getDesktop().addListener(new TokenCommand()); Executions.getCurrent().getDesktop().addListener(new ZoomCommand()); - eventThreadEnabled = Executions.getCurrent().getDesktop().getWebApp().getConfiguration().isEventThreadEnabled(); + eventThreadEnabled = Executions.getCurrent().getDesktop().getWebApp().getConfiguration().isEventThreadEnabled(); } + private void cleanupForPing() { + final Desktop desktop = Executions.getCurrent().getDesktop(); + final WebApp wapp = desktop.getWebApp(); + final DesktopCache desktopCache = ((WebAppCtrl) wapp).getDesktopCache(desktop.getSession()); + final Session session = desktop.getSession(); + + //clear context, invalidate session + Env.getCtx().clear(); + destroySession(session); + desktop.setAttribute(DESKTOP_SESSION_INVALIDATED_ATTR, Boolean.TRUE); + Adempiere.getThreadPoolExecutor().schedule(() -> { + try { + desktopCache.removeDesktop(desktop); + } catch (Throwable t) { + t.printStackTrace(); + } + }, 1, TimeUnit.SECONDS); + } + public void onOk() { } @@ -386,14 +414,6 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb final WebApp wapp = desktop.getWebApp(); final DesktopCache desktopCache = ((WebAppCtrl) wapp).getDesktopCache(desktop.getSession()); - Adempiere.getThreadPoolExecutor().schedule(() -> { - try { - desktopCache.removeDesktop(desktop); - } catch (Throwable t) { - t.printStackTrace(); - } - }, 5, TimeUnit.SECONDS); - final Session session = logout0(); //clear context, invalidate session @@ -402,7 +422,14 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb desktop.setAttribute(DESKTOP_SESSION_INVALIDATED_ATTR, Boolean.TRUE); //redirect to login page - Executions.sendRedirect("index.zul"); + Executions.sendRedirect("index.zul"); + + try { + desktopCache.removeDesktop(desktop); + DesktopWatchDog.removeDesktop(desktop); + } catch (Throwable t) { + t.printStackTrace(); + } } private void destroySession(final Session session) { @@ -414,10 +441,14 @@ public class AdempiereWebUI extends Window implements EventListener, IWeb session.invalidate(); } + /** + * Perform logout after user close a browser tab without first logging out + */ public void logoutAfterTabDestroyed(){ Desktop desktop = Executions.getCurrent().getDesktop(); if (desktop.isServerPushEnabled()) desktop.enableServerPush(false); + DesktopWatchDog.removeDesktop(desktop); Session session = logout0(); diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/WLogin.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/WLogin.java index 2980055356..5ecae731e5 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/WLogin.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/WLogin.java @@ -63,23 +63,6 @@ public class WLogin extends AbstractUIPart loginWindow = (LoginWindow) loginPage.getFellow("loginWindow"); loginWindow.init(app); - /* IDEMPIERE-1022 - deprecated message - if (!AEnv.isBrowserSupported()) - { - //TODO: localization - String msg = "You might experience slow performance and user interface anomalies using your current browser to access the application. We recommend the use of Firefox, Google Chrome or Apple Safari."; - browserWarningWindow = new Window(); - Div div = new Div(); - div.setStyle("font-size: 9pt"); - div.appendChild(new Text(msg)); - browserWarningWindow.appendChild(div); - browserWarningWindow.setPosition("top,right"); - ZKUpdateUtil.setWidth(browserWarningWindow, "550px"); - browserWarningWindow.setPage(page); - browserWarningWindow.doOverlapped(); - } - */ - boolean mobile = false; if (Executions.getCurrent().getBrowser("mobile") !=null) { mobile = true; diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/session/SessionManager.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/session/SessionManager.java index 84ecec4186..60c746e5a9 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/session/SessionManager.java +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/session/SessionManager.java @@ -84,7 +84,11 @@ public class SessionManager if (app != null) app.logout(); } - + + /** + * Perform logout after user close a browser tab without first logging out. + * Usually this is invoke from {@link SessionContextListener} and developer shouldn't call this directly. + */ public static void logoutSessionAfterBrowserDestroyed() { IWebClient app = getSessionApplication(); @@ -92,6 +96,10 @@ public class SessionManager app.logoutAfterTabDestroyed(); } + /** + * + * @param user + */ public static void changeRole(MUser user){ IWebClient app = getSessionApplication(); if (app != null) diff --git a/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/DesktopWatchDog.java b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/DesktopWatchDog.java new file mode 100644 index 0000000000..e6dae1b8d1 --- /dev/null +++ b/org.adempiere.ui.zk/WEB-INF/src/org/adempiere/webui/util/DesktopWatchDog.java @@ -0,0 +1,149 @@ +/*********************************************************************** + * This file is part of iDempiere ERP Open Source * + * http://www.idempiere.org * + * * + * Copyright (C) Contributors * + * * + * This program is free software; you can redistribute it and/or * + * modify it under the terms of the GNU General Public License * + * as published by the Free Software Foundation; either version 2 * + * of the License, or (at your option) any later version. * + * * + * 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., 51 Franklin Street, Fifth Floor, Boston, * + * MA 02110-1301, USA. * + * * + * Contributors: * + * - hengsin * + **********************************************************************/ +package org.adempiere.webui.util; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedDeque; +import java.util.concurrent.TimeUnit; + +import org.compiere.Adempiere; +import org.zkoss.zk.ui.Desktop; +import org.zkoss.zk.ui.Session; +import org.zkoss.zk.ui.WebApp; +import org.zkoss.zk.ui.sys.DesktopCache; +import org.zkoss.zk.ui.sys.DesktopCtrl; +import org.zkoss.zk.ui.sys.ServerPush; +import org.zkoss.zk.ui.sys.SessionCtrl; +import org.zkoss.zk.ui.sys.WebAppCtrl; + +import fi.jawsy.jawwa.zk.atmosphere.AtmosphereServerPush; + +/** + * watch for disconnected desktop and destroy it + * @author hengsin + * + */ +public class DesktopWatchDog { + + private final static DesktopWatchDog INSTANCE = new DesktopWatchDog(); + + private final ConcurrentLinkedDeque desktops = new ConcurrentLinkedDeque(); + + private DesktopWatchDog() { + Adempiere.getThreadPoolExecutor().scheduleWithFixedDelay(() -> { + doMonitoring(); + }, 60, 40, TimeUnit.SECONDS); + } + + private void doMonitoring() { + List toDestroy = new ArrayList(); + List actives = new ArrayList(); + Iterator iterator = desktops.iterator(); + while (iterator.hasNext()) { + DesktopEntry entry = iterator.next(); + if (!entry.desktop.isAlive()) { + iterator.remove(); + continue; + } + if (entry.desktop.isServerPushEnabled() == false) { + entry.noAtmosphereResourceCount++; + } + ServerPush spush = ((DesktopCtrl)entry.desktop).getServerPush(); + if (spush == null) { + entry.noAtmosphereResourceCount++; + } else if (spush instanceof AtmosphereServerPush) { + AtmosphereServerPush asp = (AtmosphereServerPush) spush; + if (!asp.hasAtmosphereResource()) + entry.noAtmosphereResourceCount++; + else + entry.noAtmosphereResourceCount=0; + } + if (entry.noAtmosphereResourceCount >= 3) { + iterator.remove(); + try { + final WebApp wapp = entry.desktop.getWebApp(); + final Session session = entry.desktop.getSession(); + final DesktopCache desktopCache = ((WebAppCtrl) wapp).getDesktopCache(session); + desktopCache.removeDesktop(entry.desktop); + if (!actives.contains(session) && !toDestroy.contains(session)) + toDestroy.add(session); + } catch (Throwable t) { + t.printStackTrace(); + } + } else { + final Session session = entry.desktop.getSession(); + if (!actives.contains(session)) + actives.add(session); + int index = toDestroy.indexOf(session); + if (index >= 0) + toDestroy.remove(index); + } + } + if (!toDestroy.isEmpty()) { + for(Session session : toDestroy) { + try { + ((SessionCtrl)session).onDestroyed(); + } catch (Throwable t) { + t.printStackTrace(); + } + session.invalidate(); + } + } + } + + private final static class DesktopEntry { + Desktop desktop; + int noAtmosphereResourceCount = 0; + + private DesktopEntry(Desktop desktop) { + this.desktop = desktop; + } + } + + /** + * add desktop to watch list + * @param desktop + */ + public static void addDesktop(Desktop desktop) { + INSTANCE.desktops.add(new DesktopEntry(desktop)); + } + + /** + * remove desktop from watch list + * @param desktop + */ + public static void removeDesktop(Desktop desktop) { + Iterator iterator = INSTANCE.desktops.iterator(); + while (iterator.hasNext()) { + DesktopEntry entry = iterator.next(); + if (entry.desktop == desktop) { + iterator.remove(); + break; + } + } + } +}