IDEMPIERE-5049 Zk Session and Desktop object not destroy immediately after logout (#994)
- Remove 5 second delay for remove of desktop after logout - added X-PING custom http header for "ping" to index.zul - added watch for disconnected desktop
This commit is contained in:
parent
07202f7b61
commit
06698688ef
|
@ -301,6 +301,14 @@ public class AtmosphereServerPush implements ServerPush {
|
||||||
startClientPush(desktop.get());
|
startClientPush(desktop.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @return true if it is holding an atmosphere resource
|
||||||
|
*/
|
||||||
|
public boolean hasAtmosphereResource() {
|
||||||
|
return this.resource.get() != null;
|
||||||
|
}
|
||||||
|
|
||||||
private class Schedule<T extends Event> {
|
private class Schedule<T extends Event> {
|
||||||
private EventListener<T> task;
|
private EventListener<T> task;
|
||||||
private T event;
|
private T event;
|
||||||
|
|
|
@ -44,6 +44,7 @@ import org.adempiere.webui.session.SessionManager;
|
||||||
import org.adempiere.webui.theme.ITheme;
|
import org.adempiere.webui.theme.ITheme;
|
||||||
import org.adempiere.webui.theme.ThemeManager;
|
import org.adempiere.webui.theme.ThemeManager;
|
||||||
import org.adempiere.webui.util.BrowserToken;
|
import org.adempiere.webui.util.BrowserToken;
|
||||||
|
import org.adempiere.webui.util.DesktopWatchDog;
|
||||||
import org.adempiere.webui.util.UserPreference;
|
import org.adempiere.webui.util.UserPreference;
|
||||||
import org.compiere.Adempiere;
|
import org.compiere.Adempiere;
|
||||||
import org.compiere.model.MRole;
|
import org.compiere.model.MRole;
|
||||||
|
@ -140,12 +141,20 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
|
||||||
|
|
||||||
public void onCreate()
|
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);
|
Executions.getCurrent().getDesktop().enableServerPush(true);
|
||||||
|
DesktopWatchDog.addDesktop(Executions.getCurrent().getDesktop());
|
||||||
|
|
||||||
SessionManager.setSessionApplication(this);
|
SessionManager.setSessionApplication(this);
|
||||||
Session session = Executions.getCurrent().getDesktop().getSession();
|
final Session session = Executions.getCurrent().getDesktop().getSession();
|
||||||
|
|
||||||
Properties ctx = Env.getCtx();
|
Properties ctx = Env.getCtx();
|
||||||
langSession = Env.getContext(ctx, Env.LANGUAGE);
|
langSession = Env.getContext(ctx, Env.LANGUAGE);
|
||||||
|
@ -174,6 +183,25 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
|
||||||
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()
|
public void onOk()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
@ -386,14 +414,6 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
|
||||||
|
|
||||||
final WebApp wapp = desktop.getWebApp();
|
final WebApp wapp = desktop.getWebApp();
|
||||||
final DesktopCache desktopCache = ((WebAppCtrl) wapp).getDesktopCache(desktop.getSession());
|
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();
|
final Session session = logout0();
|
||||||
|
|
||||||
//clear context, invalidate session
|
//clear context, invalidate session
|
||||||
|
@ -403,6 +423,13 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
|
||||||
|
|
||||||
//redirect to login page
|
//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) {
|
private void destroySession(final Session session) {
|
||||||
|
@ -414,10 +441,14 @@ public class AdempiereWebUI extends Window implements EventListener<Event>, IWeb
|
||||||
session.invalidate();
|
session.invalidate();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Perform logout after user close a browser tab without first logging out
|
||||||
|
*/
|
||||||
public void logoutAfterTabDestroyed(){
|
public void logoutAfterTabDestroyed(){
|
||||||
Desktop desktop = Executions.getCurrent().getDesktop();
|
Desktop desktop = Executions.getCurrent().getDesktop();
|
||||||
if (desktop.isServerPushEnabled())
|
if (desktop.isServerPushEnabled())
|
||||||
desktop.enableServerPush(false);
|
desktop.enableServerPush(false);
|
||||||
|
DesktopWatchDog.removeDesktop(desktop);
|
||||||
|
|
||||||
Session session = logout0();
|
Session session = logout0();
|
||||||
|
|
||||||
|
|
|
@ -63,23 +63,6 @@ public class WLogin extends AbstractUIPart
|
||||||
loginWindow = (LoginWindow) loginPage.getFellow("loginWindow");
|
loginWindow = (LoginWindow) loginPage.getFellow("loginWindow");
|
||||||
loginWindow.init(app);
|
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;
|
boolean mobile = false;
|
||||||
if (Executions.getCurrent().getBrowser("mobile") !=null) {
|
if (Executions.getCurrent().getBrowser("mobile") !=null) {
|
||||||
mobile = true;
|
mobile = true;
|
||||||
|
|
|
@ -85,6 +85,10 @@ public class SessionManager
|
||||||
app.logout();
|
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()
|
public static void logoutSessionAfterBrowserDestroyed()
|
||||||
{
|
{
|
||||||
IWebClient app = getSessionApplication();
|
IWebClient app = getSessionApplication();
|
||||||
|
@ -92,6 +96,10 @@ public class SessionManager
|
||||||
app.logoutAfterTabDestroyed();
|
app.logoutAfterTabDestroyed();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param user
|
||||||
|
*/
|
||||||
public static void changeRole(MUser user){
|
public static void changeRole(MUser user){
|
||||||
IWebClient app = getSessionApplication();
|
IWebClient app = getSessionApplication();
|
||||||
if (app != null)
|
if (app != null)
|
||||||
|
|
|
@ -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<DesktopEntry> desktops = new ConcurrentLinkedDeque<DesktopWatchDog.DesktopEntry>();
|
||||||
|
|
||||||
|
private DesktopWatchDog() {
|
||||||
|
Adempiere.getThreadPoolExecutor().scheduleWithFixedDelay(() -> {
|
||||||
|
doMonitoring();
|
||||||
|
}, 60, 40, TimeUnit.SECONDS);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doMonitoring() {
|
||||||
|
List<Session> toDestroy = new ArrayList<Session>();
|
||||||
|
List<Session> actives = new ArrayList<Session>();
|
||||||
|
Iterator<DesktopEntry> 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<DesktopEntry> iterator = INSTANCE.desktops.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
DesktopEntry entry = iterator.next();
|
||||||
|
if (entry.desktop == desktop) {
|
||||||
|
iterator.remove();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue