From 409453dca8374a9307e8653f31be3e7df7dab3b1 Mon Sep 17 00:00:00 2001 From: Heng Sin Low Date: Thu, 26 Apr 2012 23:15:44 +0800 Subject: [PATCH] IDEMPIERE-175 Performance: Use atmosphere ( long pooling, NIO ) server push --- .../zk/atmosphere/AtmosphereServerPush.java | 34 ++++++++--- .../zk/atmosphere/ZkAtmosphereHandler.java | 20 +++++++ .../src/org/adempiere/webui/apps/AEnv.java | 60 +++++++++++-------- .../webui/dashboard/DPActivities.java | 2 +- .../webui/dashboard/DPRecentItems.java | 2 +- .../webui/desktop/DefaultDesktop.java | 4 +- .../webui/desktop/NavBar2Desktop.java | 4 +- .../webui/desktop/NavBarDesktop.java | 4 +- .../webui/util/ServerPushTemplate.java | 58 +++++++++++++----- 9 files changed, 131 insertions(+), 57 deletions(-) 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 d40cc23222..0baba0e28e 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 @@ -1,3 +1,22 @@ +/** + +This software is licensed under the Apache 2 license, quoted below. + +Copyright 2012 Joonas Javanainen + +Licensed under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. + + */ package fi.jawsy.jawwa.zk.atmosphere; import java.util.concurrent.atomic.AtomicReference; @@ -21,13 +40,13 @@ import org.zkoss.zk.ui.util.Clients; /** * ZK server push implementation based on Atmosphere. - * - * Only supports asynchronous updates (Executions.schedule) and will throw exceptions if synchronous updates - * (Executions.activate/deactivate) is attempted. + * Adapted from https://github.com/Gekkio/jawwa/tree/develop/zk-atmosphere version 0.3.1-SNAPSHOT */ public class AtmosphereServerPush implements ServerPush { - public static final int DEFAULT_TIMEOUT = 1000 * 60 * 5; + private static final String ON_ACTIVATE_DESKTOP = "onActivateDesktop"; + + public static final int DEFAULT_TIMEOUT = 1000 * 60 * 5; private final AtomicReference desktop = new AtomicReference(); @@ -61,7 +80,7 @@ public class AtmosphereServerPush implements ServerPush { EventListener task = new EventListener() { @Override public void onEvent(Event event) throws Exception { - if (event.getName().equals("onNewData")) + if (event.getName().equals(ON_ACTIVATE_DESKTOP)) { synchronized (_mutex) { _carryOver = new ExecutionCarryOver(desktop.get()); @@ -82,7 +101,7 @@ public class AtmosphereServerPush implements ServerPush { }; synchronized (info) { - Executions.schedule(desktop.get(), task, new Event("onNewData")); + Executions.schedule(desktop.get(), task, new Event(ON_ACTIVATE_DESKTOP)); if (info.nActive == 0) info.wait(timeout <= 0 ? 10*60*1000: timeout); } @@ -130,8 +149,7 @@ public class AtmosphereServerPush implements ServerPush { @Override public boolean isActive() { -// throw new UnsupportedOperationException("isActive is not supported by AtmosphereServerPush"); - return true; + return _active != null && _active.nActive > 0; } @Override diff --git a/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/ZkAtmosphereHandler.java b/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/ZkAtmosphereHandler.java index 5d0a7b2399..69fd8f327d 100644 --- a/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/ZkAtmosphereHandler.java +++ b/org.adempiere.ui.zk/WEB-INF/src/fi/jawsy/jawwa/zk/atmosphere/ZkAtmosphereHandler.java @@ -1,3 +1,22 @@ +/** + +This software is licensed under the Apache 2 license, quoted below. + +Copyright 2012 Joonas Javanainen + +Licensed under the Apache License, Version 2.0 (the "License"); you may not +use this file except in compliance with the License. You may obtain a copy of +the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +License for the specific language governing permissions and limitations under +the License. + + */ package fi.jawsy.jawwa.zk.atmosphere; import java.io.IOException; @@ -18,6 +37,7 @@ import org.zkoss.zk.ui.sys.WebAppCtrl; /** * Atmosphere handler that integrates Atmosphere with ZK server push. + * Adapted from https://github.com/Gekkio/jawwa/tree/develop/zk-atmosphere version 0.3.1-SNAPSHOT */ public class ZkAtmosphereHandler implements AtmosphereHandler { 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 574c0a3780..e1b58b14cb 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 @@ -41,6 +41,8 @@ import org.adempiere.webui.component.Window; import org.adempiere.webui.desktop.IDesktop; import org.adempiere.webui.session.SessionManager; import org.adempiere.webui.theme.ThemeManager; +import org.adempiere.webui.util.IServerPushCallback; +import org.adempiere.webui.util.ServerPushTemplate; import org.compiere.acct.Doc; import org.compiere.model.GridWindowVO; import org.compiere.model.Lookup; @@ -744,42 +746,48 @@ public final class AEnv } /** - * Execute task that required access to the zk desktop + * Execute synchronous task in UI thread. * @param runnable */ - public static void executeDesktopTask(Runnable runnable) { - boolean inUIThread = Executions.getCurrent() != null; - boolean desktopActivated = false; - - Desktop desktop = null; - try { - if (!inUIThread) { - desktop = (Desktop) Env.getCtx().get(AdempiereWebUI.ZK_DESKTOP_SESSION_KEY); - if (desktop == null) - return; - //1 second timeout - if (Executions.activate(desktop, 1000)) { - desktopActivated = true; - } else { - return; - } - } - runnable.run(); - } catch (Exception e) { - throw new RuntimeException(e.getLocalizedMessage(), e); - } finally { - if (!inUIThread && desktopActivated) { - Executions.deactivate(desktop); - } - } + public static void executeDesktopTask(final Runnable runnable) { + Desktop desktop = getDesktop(); + ServerPushTemplate template = new ServerPushTemplate(desktop); + template.execute(new IServerPushCallback() { + @Override + public void updateUI() { + runnable.run(); + } + }); } + /** + * Execute asynchronous task in UI thread. + * @param runnable + */ + public static void executeAsyncDesktopTask(final Runnable runnable) { + Desktop desktop = getDesktop(); + ServerPushTemplate template = new ServerPushTemplate(desktop); + template.executeAsync(new IServerPushCallback() { + @Override + public void updateUI() { + runnable.run(); + } + }); + } + + /** + * Get current desktop + * @return Desktop + */ public static Desktop getDesktop() { boolean inUIThread = Executions.getCurrent() != null; return inUIThread ? Executions.getCurrent().getDesktop() : (Desktop) Env.getCtx().get(AdempiereWebUI.ZK_DESKTOP_SESSION_KEY); } + /** + * @return true if running on a tablet + */ public static boolean isTablet() { IDesktop appDesktop = SessionManager.getAppDesktop(); return appDesktop != null ? appDesktop.getClientInfo().tablet : false; 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 54352fdf3a..5e002f58a4 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 @@ -219,7 +219,7 @@ public class DPActivities extends DashboardPanel implements EventListener { noOfWorkflow = getWorkflowCount(); if (isShowUnprocessed) noOfUnprocessed = getUnprocessedCount(); - template.execute(this); + template.executeAsync(this); } @Override 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 f31f2eb29e..b1e32852ce 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 @@ -241,7 +241,7 @@ public class DPRecentItems extends DashboardPanel implements EventListener task = new EventListener() { - @Override - public void onEvent(Event event) throws Exception { - callback.updateUI(); - } - }; - Executions.schedule(desktop, task, new Event("onExecute")); - } else { - callback.updateUI(); - } + EventListener task = new EventListener() { + @Override + public void onEvent(Event event) throws Exception { + callback.updateUI(); + } + }; + Executions.schedule(desktop, task, new Event("onExecute")); } catch (DesktopUnavailableException de) { throw de; } catch (Exception e) { throw new AdempiereException("Failed to update client in server push worker thread.", e); + } + } + + /** + * Execute synchronous task in UI thread. This is implemented + * using Executions.activate/deactivate and will only return after the + * invoked task have ended. For better scalability, if possible, you + * should use executeAsync instead. + * @param callback + */ + public void execute(IServerPushCallback callback) { + boolean inUIThread = Executions.getCurrent() != null; + boolean desktopActivated = false; + + try { + if (!inUIThread) { + //10 minutes timeout + if (Executions.activate(desktop, 10 * 60 * 1000)) { + desktopActivated = true; + } else { + throw new DesktopUnavailableException("Timeout activating desktop."); + } + } + callback.updateUI(); + } catch (DesktopUnavailableException de) { + throw de; + } catch (Exception e) { + throw new AdempiereException("Failed to update client in server push worker thread.", e); + } finally { + if (!inUIThread && desktopActivated) { + Executions.deactivate(desktop); } } - + } + /** * * @return desktop