IDEMPIERE-175 Performance: Use atmosphere ( long pooling, NIO ) server push
This commit is contained in:
parent
d1f5d4e3e9
commit
409453dca8
|
@ -1,3 +1,22 @@
|
|||
/**
|
||||
|
||||
This software is licensed under the Apache 2 license, quoted below.
|
||||
|
||||
Copyright 2012 Joonas Javanainen <joonas@jawsy.fi>
|
||||
|
||||
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,12 +40,12 @@ 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 {
|
||||
|
||||
private static final String ON_ACTIVATE_DESKTOP = "onActivateDesktop";
|
||||
|
||||
public static final int DEFAULT_TIMEOUT = 1000 * 60 * 5;
|
||||
|
||||
private final AtomicReference<Desktop> desktop = new AtomicReference<Desktop>();
|
||||
|
@ -61,7 +80,7 @@ public class AtmosphereServerPush implements ServerPush {
|
|||
EventListener<Event> task = new EventListener<Event>() {
|
||||
@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
|
||||
|
|
|
@ -1,3 +1,22 @@
|
|||
/**
|
||||
|
||||
This software is licensed under the Apache 2 license, quoted below.
|
||||
|
||||
Copyright 2012 Joonas Javanainen <joonas@jawsy.fi>
|
||||
|
||||
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 {
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
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();
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException(e.getLocalizedMessage(), e);
|
||||
} finally {
|
||||
if (!inUIThread && desktopActivated) {
|
||||
Executions.deactivate(desktop);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
|
|
@ -219,7 +219,7 @@ public class DPActivities extends DashboardPanel implements EventListener {
|
|||
noOfWorkflow = getWorkflowCount();
|
||||
if (isShowUnprocessed) noOfUnprocessed = getUnprocessedCount();
|
||||
|
||||
template.execute(this);
|
||||
template.executeAsync(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -241,7 +241,7 @@ public class DPRecentItems extends DashboardPanel implements EventListener<Event
|
|||
@Override
|
||||
public void refresh(ServerPushTemplate template)
|
||||
{
|
||||
template.execute(this);
|
||||
template.executeAsync(this);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -169,7 +169,7 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
|
|||
}
|
||||
};
|
||||
ServerPushTemplate template = new ServerPushTemplate(layout.getDesktop());
|
||||
template.execute(callback);
|
||||
template.executeAsync(callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -226,7 +226,7 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
|
|||
noOfRequest = DPActivities.getRequestCount();
|
||||
noOfWorkflow = DPActivities.getWorkflowCount();
|
||||
|
||||
template.execute(this);
|
||||
template.executeAsync(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -205,7 +205,7 @@ public class NavBar2Desktop extends TabbedDesktop implements MenuListener, Seria
|
|||
}
|
||||
};
|
||||
ServerPushTemplate template = new ServerPushTemplate(layout.getDesktop());
|
||||
template.execute(callback);
|
||||
template.executeAsync(callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -256,7 +256,7 @@ public class NavBar2Desktop extends TabbedDesktop implements MenuListener, Seria
|
|||
noOfRequest = DPActivities.getRequestCount();
|
||||
noOfWorkflow = DPActivities.getWorkflowCount();
|
||||
|
||||
template.execute(this);
|
||||
template.executeAsync(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -207,7 +207,7 @@ public class NavBarDesktop extends TabbedDesktop implements MenuListener, Serial
|
|||
}
|
||||
};
|
||||
ServerPushTemplate template = new ServerPushTemplate(layout.getDesktop());
|
||||
template.execute(callback);
|
||||
template.executeAsync(callback);
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -271,7 +271,7 @@ public class NavBarDesktop extends TabbedDesktop implements MenuListener, Serial
|
|||
noOfRequest = DPActivities.getRequestCount();
|
||||
noOfWorkflow = DPActivities.getWorkflowCount();
|
||||
|
||||
template.execute(this);
|
||||
template.executeAsync(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -39,13 +39,12 @@ public class ServerPushTemplate {
|
|||
}
|
||||
|
||||
/**
|
||||
* Execute callback in UI thread
|
||||
* Execute asynchronous task in UI thread. This is implemented
|
||||
* using Executions.schedule and will return immediately
|
||||
* @param callback
|
||||
*/
|
||||
public void execute(final IServerPushCallback callback) {
|
||||
boolean inUIThread = Executions.getCurrent() != null;
|
||||
public void executeAsync(final IServerPushCallback callback) {
|
||||
try {
|
||||
if (!inUIThread) {
|
||||
EventListener<Event> task = new EventListener<Event>() {
|
||||
@Override
|
||||
public void onEvent(Event event) throws Exception {
|
||||
|
@ -53,9 +52,6 @@ public class ServerPushTemplate {
|
|||
}
|
||||
};
|
||||
Executions.schedule(desktop, task, new Event("onExecute"));
|
||||
} else {
|
||||
callback.updateUI();
|
||||
}
|
||||
} catch (DesktopUnavailableException de) {
|
||||
throw de;
|
||||
} catch (Exception e) {
|
||||
|
@ -63,6 +59,38 @@ public class ServerPushTemplate {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
|
Loading…
Reference in New Issue