[IDEMPIERE-175] Performance: Use atmosphere ( long pooling, NIO ) server push
This commit is contained in:
parent
12d8c0bcd7
commit
baada05fe5
|
@ -1,8 +1,13 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="WEB-INF/src"/>
|
||||
<classpathentry exported="true" kind="lib" path="WEB-INF/classes/" sourcepath="WEB-INF/src"/>
|
||||
<classpathentry kind="output" path="WEB-INF/classes"/>
|
||||
</classpath>
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6"/>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="WEB-INF/src"/>
|
||||
<classpathentry exported="true" kind="lib" path="WEB-INF/classes/" sourcepath="WEB-INF/src"/>
|
||||
<classpathentry kind="lib" path="WEB-INF/lib/slf4j-api-1.6.1.jar"/>
|
||||
<classpathentry kind="lib" path="WEB-INF/lib/atmosphere-compat-jbossweb-0.9.jar"/>
|
||||
<classpathentry kind="lib" path="WEB-INF/lib/atmosphere-compat-tomcat-0.9.jar"/>
|
||||
<classpathentry kind="lib" path="WEB-INF/lib/atmosphere-compat-tomcat7-0.9.jar"/>
|
||||
<classpathentry kind="lib" path="WEB-INF/lib/atmosphere-runtime-0.9.jar" sourcepath="C:/idempiere-zk6/org.adempiere.ui.zk/WEB-INF/lib/src/atmosphere-runtime-0.9-sources.jar"/>
|
||||
<classpathentry kind="output" path="WEB-INF/classes"/>
|
||||
</classpath>
|
||||
|
|
|
@ -3,7 +3,20 @@ Bundle-ManifestVersion: 2
|
|||
Bundle-Name: Zk Web Client
|
||||
Bundle-SymbolicName: org.adempiere.ui.zk;singleton:=true
|
||||
Bundle-Version: 1.0.0.qualifier
|
||||
Web-ContextPath: webui
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
|
||||
Import-Package: javax.servlet,
|
||||
javax.servlet.http,
|
||||
metainfo.zk,
|
||||
org.apache.commons.codec.binary,
|
||||
org.apache.ecs,
|
||||
org.apache.ecs.xhtml,
|
||||
org.osgi.framework;version="1.5.0"
|
||||
Bundle-ClassPath: WEB-INF/classes/,
|
||||
WEB-INF/lib/atmosphere-runtime-0.9.jar,
|
||||
WEB-INF/lib/atmosphere-compat-jbossweb-0.9.jar,
|
||||
WEB-INF/lib/atmosphere-compat-tomcat-0.9.jar,
|
||||
WEB-INF/lib/atmosphere-compat-tomcat7-0.9.jar,
|
||||
WEB-INF/lib/slf4j-api-1.6.1.jar
|
||||
Export-Package: metainfo.zk,
|
||||
org.adempiere.webui,
|
||||
org.adempiere.webui.acct,
|
||||
|
@ -27,22 +40,31 @@ Export-Package: metainfo.zk,
|
|||
org.adempiere.webui.session,
|
||||
org.adempiere.webui.theme,
|
||||
org.adempiere.webui.util,
|
||||
org.adempiere.webui.window
|
||||
org.adempiere.webui.window,
|
||||
org.atmosphere.cache,
|
||||
org.atmosphere.client,
|
||||
org.atmosphere.config,
|
||||
org.atmosphere.container,
|
||||
org.atmosphere.container.version,
|
||||
org.atmosphere.cpr,
|
||||
org.atmosphere.di,
|
||||
org.atmosphere.handler,
|
||||
org.atmosphere.util,
|
||||
org.atmosphere.util.uri,
|
||||
org.atmosphere.websocket,
|
||||
org.atmosphere.websocket.protocol,
|
||||
org.jboss.servlet.http,
|
||||
org.apache.catalina,
|
||||
org.apache.catalina.comet,
|
||||
org.slf4j.helpers,
|
||||
org.slf4j.spi
|
||||
Require-Bundle: org.adempiere.report.jasper;bundle-version="1.0.0",
|
||||
org.adempiere.base;bundle-version="1.0.0",
|
||||
org.adempiere.report.jasper.library;bundle-version="1.0.0",
|
||||
org.adempiere.ui;bundle-version="1.0.0",
|
||||
org.zkoss.zk.library;bundle-version="6.0.0"
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
|
||||
Eclipse-ExtensibleAPI: true
|
||||
Import-Package: javax.servlet,
|
||||
javax.servlet.http,
|
||||
metainfo.zk,
|
||||
org.apache.commons.codec.binary,
|
||||
org.apache.ecs,
|
||||
org.apache.ecs.xhtml,
|
||||
org.osgi.framework;version="1.5.0"
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Bundle-Activator: org.adempiere.webui.WebUIActivator
|
||||
Bundle-ClassPath: WEB-INF/classes/
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Eclipse-ExtensibleAPI: true
|
||||
Eclipse-RegisterBuddy: org.zkoss.zk.library
|
||||
Web-ContextPath: webui
|
||||
|
|
|
@ -0,0 +1,366 @@
|
|||
package fi.jawsy.jawwa.zk.atmosphere;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.atmosphere.cpr.AtmosphereResource;
|
||||
import org.zkoss.lang.Library;
|
||||
import org.zkoss.util.logging.Log;
|
||||
import org.zkoss.zk.au.out.AuScript;
|
||||
import org.zkoss.zk.ui.Desktop;
|
||||
import org.zkoss.zk.ui.DesktopUnavailableException;
|
||||
import org.zkoss.zk.ui.Execution;
|
||||
import org.zkoss.zk.ui.Executions;
|
||||
import org.zkoss.zk.ui.UiException;
|
||||
import org.zkoss.zk.ui.event.Event;
|
||||
import org.zkoss.zk.ui.event.EventListener;
|
||||
import org.zkoss.zk.ui.impl.ExecutionCarryOver;
|
||||
import org.zkoss.zk.ui.sys.DesktopCtrl;
|
||||
import org.zkoss.zk.ui.sys.Scheduler;
|
||||
import org.zkoss.zk.ui.sys.ServerPush;
|
||||
import org.zkoss.zk.ui.util.Clients;
|
||||
import org.zkoss.zk.ui.util.Configuration;
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
public class AtmosphereServerPush implements ServerPush {
|
||||
private static final Log log = Log.lookup(AtmosphereServerPush.class);
|
||||
/** Denote a server-push thread gives up the activation (timeout). */
|
||||
private static final int GIVEUP = -99;
|
||||
|
||||
private Desktop _desktop;
|
||||
/** List of ThreadInfo. */
|
||||
private final List<ThreadInfo> _pending = new LinkedList<ThreadInfo>();
|
||||
/** The active thread. */
|
||||
private ThreadInfo _active;
|
||||
/** The info to carray over from onPiggyback to the server-push thread. */
|
||||
private ExecutionCarryOver _carryOver;
|
||||
// private final int _min, _max, _factor;
|
||||
/** A mutex that is used by this object to wait for the server-push thread
|
||||
* to complete.
|
||||
*/
|
||||
private final Object _mutex = new Object();
|
||||
|
||||
public static final int DEFAULT_TIMEOUT = 1000 * 60 * 5;
|
||||
private final AtomicReference<AtmosphereResource> resource = new AtomicReference<AtmosphereResource>();
|
||||
private final int timeout;
|
||||
|
||||
public AtmosphereServerPush() {
|
||||
// this(-1, -1, -1);
|
||||
// }
|
||||
//
|
||||
// public AtmosphereServerPush(int min, int max, int factor) {
|
||||
// _min = min;
|
||||
// _max = max;
|
||||
// _factor = factor;
|
||||
|
||||
String timeoutString = Library.getProperty("fi.jawsy.jawwa.zk.atmosphere.timeout");
|
||||
if (timeoutString == null || timeoutString.length() == 0) {
|
||||
timeout = DEFAULT_TIMEOUT;
|
||||
} else {
|
||||
timeout = Integer.valueOf(timeoutString);
|
||||
}
|
||||
}
|
||||
|
||||
protected void startClientPush() {
|
||||
// Clients.response("zk.clientpush", new AuScript(null, getStartScript()));
|
||||
Clients.response("jawwa.atmosphere.serverpush", new AuScript(null, getStartScript()));
|
||||
}
|
||||
|
||||
protected void stopClientPush() {
|
||||
// Clients.response("zk.clientpush", new AuScript(null, getStopScript()));
|
||||
Clients.response("jawwa.atmosphere.serverpush", new AuScript(null, getStopScript()));
|
||||
}
|
||||
|
||||
protected String getStartScript() {
|
||||
// final String start = _desktop.getWebApp().getConfiguration()
|
||||
// .getPreference("PollingServerPush.start", null);
|
||||
// if (start != null)
|
||||
// return start;
|
||||
//
|
||||
// final StringBuffer sb = new StringBuffer(128)
|
||||
// .append("zk.load('zk.cpsp');zk.afterLoad(function(){zk.cpsp.start('")
|
||||
// .append(_desktop.getId()).append('\'');
|
||||
//
|
||||
// final int min = _min > 0 ? _min: getIntPref("PollingServerPush.delay.min"),
|
||||
// max = _max > 0 ? _max: getIntPref("PollingServerPush.delay.max"),
|
||||
// factor = _factor > 0 ? _factor: getIntPref("PollingServerPush.delay.factor");
|
||||
// if (min > 0 || max > 0 || factor > 0)
|
||||
// sb.append(',').append(min).append(',').append(max)
|
||||
// .append(',').append(factor);
|
||||
//
|
||||
// return sb.append(");});").toString();
|
||||
|
||||
int clientTimeout = timeout + 1000 * 60;
|
||||
return "jawwa.atmosphere.startServerPush('" + _desktop.getId() + "', " + clientTimeout + ");";
|
||||
}
|
||||
|
||||
// private int getIntPref(String key) {
|
||||
// final String s = _desktop.getWebApp().getConfiguration()
|
||||
// .getPreference(key, null);
|
||||
// if (s != null) {
|
||||
// try {
|
||||
// return Integer.parseInt(s);
|
||||
// } catch (NumberFormatException ex) {
|
||||
// log.warning("Not a number specified at "+key);
|
||||
// }
|
||||
// }
|
||||
// return -1;
|
||||
// }
|
||||
|
||||
protected String getStopScript() {
|
||||
// final String stop = _desktop.getWebApp().getConfiguration()
|
||||
// .getPreference("PollingServerPush.stop", null);
|
||||
// return stop != null ? stop:
|
||||
// "zk.cpsp.stop('" + _desktop.getId() + "');";
|
||||
return "jawwa.atmosphere.stopServerPush('" + _desktop.getId() + "');";
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isActive() {
|
||||
return _active != null && _active.nActive > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(Desktop desktop) {
|
||||
if (_desktop != null) {
|
||||
log.warning("Ignored: Sever-push already started");
|
||||
return;
|
||||
}
|
||||
|
||||
_desktop = desktop;
|
||||
startClientPush();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void stop() {
|
||||
if (_desktop == null) {
|
||||
log.warning("Ignored: Sever-push not started");
|
||||
return;
|
||||
}
|
||||
|
||||
final Execution exec = Executions.getCurrent();
|
||||
final boolean inexec = exec != null && exec.getDesktop() == _desktop;
|
||||
//it might be caused by DesktopCache expunge (when creating another desktop)
|
||||
try {
|
||||
if (inexec && _desktop.isAlive()) //Bug 1815480: don't send if timeout
|
||||
stopClientPush();
|
||||
commitResponse();
|
||||
} finally {
|
||||
_desktop = null; //to cause DesktopUnavailableException being thrown
|
||||
wakePending();
|
||||
|
||||
//if inexec, either in working thread, or other event listener
|
||||
//if in working thread, we cannot notify here (too early to wake).
|
||||
//if other listener, no need notify (since onPiggyback not running)
|
||||
if (!inexec) {
|
||||
synchronized (_mutex) {
|
||||
_mutex.notify(); //wake up onPiggyback
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void wakePending() {
|
||||
synchronized (_pending) {
|
||||
for (ThreadInfo info: _pending) {
|
||||
synchronized (info) {
|
||||
info.notify();
|
||||
}
|
||||
}
|
||||
_pending.clear();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onPiggyback() {
|
||||
final Configuration config = _desktop.getWebApp().getConfiguration();
|
||||
long tmexpired = 0;
|
||||
for (int cnt = 0; !_pending.isEmpty();) {
|
||||
//Don't hold the client too long.
|
||||
//In addition, an ill-written code might activate again
|
||||
//before onPiggyback returns. It causes dead-loop in this case.
|
||||
if (tmexpired == 0) { //first time
|
||||
tmexpired = System.currentTimeMillis()
|
||||
+ (config.getMaxProcessTime() >> 1);
|
||||
cnt = _pending.size() + 3;
|
||||
} else if (--cnt < 0 || System.currentTimeMillis() > tmexpired) {
|
||||
break;
|
||||
}
|
||||
|
||||
final ThreadInfo info;
|
||||
synchronized (_pending) {
|
||||
if (_pending.isEmpty())
|
||||
return; //nothing to do
|
||||
info = _pending.remove(0);
|
||||
}
|
||||
|
||||
//Note: we have to sync _mutex before info. Otherwise,
|
||||
//sync(info) might cause deactivate() to run before _mutex.wait
|
||||
synchronized (_mutex) {
|
||||
_carryOver = new ExecutionCarryOver(_desktop);
|
||||
|
||||
synchronized (info) {
|
||||
if (info.nActive == GIVEUP)
|
||||
continue; //give up and try next
|
||||
info.nActive = 1; //granted
|
||||
info.notify();
|
||||
}
|
||||
|
||||
if (_desktop == null) //just in case
|
||||
break;
|
||||
|
||||
try {
|
||||
_mutex.wait(); //wait until the server push is done
|
||||
} catch (InterruptedException ex) {
|
||||
throw UiException.Aide.wrap(ex);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public <T extends Event>
|
||||
void schedule(EventListener<T> listener, T event, Scheduler<T> scheduler) {
|
||||
scheduler.schedule(listener, event); //delegate back
|
||||
commitResponse();
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean activate(long timeout) throws InterruptedException, DesktopUnavailableException {
|
||||
final Thread curr = Thread.currentThread();
|
||||
if (_active != null && _active.thread.equals(curr)) { //re-activate
|
||||
++_active.nActive;
|
||||
return true;
|
||||
}
|
||||
|
||||
final ThreadInfo info = new ThreadInfo(curr);
|
||||
synchronized (_pending) {
|
||||
if (_desktop != null)
|
||||
_pending.add(info);
|
||||
}
|
||||
|
||||
boolean loop;
|
||||
do {
|
||||
loop = false;
|
||||
synchronized (info) {
|
||||
if (_desktop != null) {
|
||||
if (info.nActive == 0) //not granted yet
|
||||
info.wait(timeout <= 0 ? 10*60*1000: timeout);
|
||||
|
||||
if (info.nActive <= 0) { //not granted
|
||||
boolean bTimeout = timeout > 0;
|
||||
boolean bDead = _desktop == null || !_desktop.isAlive();
|
||||
if (bTimeout || bDead) { //not timeout
|
||||
info.nActive = GIVEUP; //denote timeout (and give up)
|
||||
synchronized (_pending) { //undo pending
|
||||
_pending.remove(info);
|
||||
}
|
||||
|
||||
if (bDead)
|
||||
throw new DesktopUnavailableException("Stopped");
|
||||
return false; //timeout
|
||||
}
|
||||
|
||||
log.debug("Executions.activate() took more than 10 minutes");
|
||||
loop = true; //try again
|
||||
}
|
||||
}
|
||||
}
|
||||
} while (loop);
|
||||
|
||||
if (_desktop == null)
|
||||
throw new DesktopUnavailableException("Stopped");
|
||||
|
||||
_carryOver.carryOver();
|
||||
_active = info;
|
||||
return true;
|
||||
|
||||
//Note: we don't mimic inEventListener since 1) ZK doesn't assume it
|
||||
//2) Window depends on it
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean deactivate(boolean stop) {
|
||||
boolean stopped = false;
|
||||
if (_active != null &&
|
||||
Thread.currentThread().equals(_active.thread)) {
|
||||
if (--_active.nActive <= 0) {
|
||||
if (stop)
|
||||
stopClientPush();
|
||||
|
||||
_carryOver.cleanup();
|
||||
_carryOver = null;
|
||||
_active.nActive = 0; //just in case
|
||||
_active = null;
|
||||
|
||||
if (stop) {
|
||||
wakePending();
|
||||
_desktop = null;
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
//wake up onPiggyback
|
||||
synchronized (_mutex) {
|
||||
_mutex.notify();
|
||||
}
|
||||
|
||||
try {Thread.sleep(100);} catch (Throwable ex) {}
|
||||
//to minimize the chance that the server-push thread
|
||||
//activate again, before onPiggback polls next _pending
|
||||
}
|
||||
}
|
||||
return stopped;
|
||||
}
|
||||
|
||||
public void clearResource(AtmosphereResource resource) {
|
||||
this.resource.compareAndSet(resource, null);
|
||||
}
|
||||
|
||||
private void commitResponse() {
|
||||
AtmosphereResource resource = this.resource.getAndSet(null);
|
||||
if (resource != null) {
|
||||
resource.resume();
|
||||
}
|
||||
}
|
||||
|
||||
public void updateResource(AtmosphereResource resource) {
|
||||
commitResponse();
|
||||
|
||||
boolean shouldSuspend = true;
|
||||
if (_desktop == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (_desktop instanceof DesktopCtrl)
|
||||
{
|
||||
DesktopCtrl desktopCtrl = (DesktopCtrl) _desktop;
|
||||
shouldSuspend = !desktopCtrl.scheduledServerPush();
|
||||
}
|
||||
|
||||
if (shouldSuspend) {
|
||||
resource.suspend(timeout, false);
|
||||
this.resource.set(resource);
|
||||
} else {
|
||||
this.resource.set(null);
|
||||
}
|
||||
}
|
||||
|
||||
private static class ThreadInfo {
|
||||
private final Thread thread;
|
||||
/** # of activate() was called. */
|
||||
private int nActive;
|
||||
private ThreadInfo(Thread thread) {
|
||||
this.thread = thread;
|
||||
}
|
||||
public String toString() {
|
||||
return "[" + thread + ',' + nActive + ']';
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,120 @@
|
|||
package fi.jawsy.jawwa.zk.atmosphere;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.atmosphere.cpr.AtmosphereHandler;
|
||||
import org.atmosphere.cpr.AtmosphereRequest;
|
||||
import org.atmosphere.cpr.AtmosphereResource;
|
||||
import org.atmosphere.cpr.AtmosphereResourceEvent;
|
||||
import org.atmosphere.cpr.AtmosphereResponse;
|
||||
import org.zkoss.zk.ui.Desktop;
|
||||
import org.zkoss.zk.ui.Session;
|
||||
import org.zkoss.zk.ui.WebApp;
|
||||
import org.zkoss.zk.ui.http.WebManager;
|
||||
import org.zkoss.zk.ui.sys.DesktopCtrl;
|
||||
import org.zkoss.zk.ui.sys.ServerPush;
|
||||
import org.zkoss.zk.ui.sys.WebAppCtrl;
|
||||
|
||||
/**
|
||||
* Atmosphere handler that integrates Atmosphere with ZK server push.
|
||||
*/
|
||||
public class ZkAtmosphereHandler implements AtmosphereHandler {
|
||||
private String err;
|
||||
|
||||
private AtmosphereServerPush getServerPush(AtmosphereResource resource) {
|
||||
AtmosphereRequest request = resource.getRequest();
|
||||
|
||||
Session session = WebManager.getSession(resource.getAtmosphereConfig().getServletContext(), request, false);
|
||||
|
||||
if (session == null)
|
||||
{
|
||||
err = "Could not find session";
|
||||
return null;
|
||||
}
|
||||
else
|
||||
{
|
||||
String desktopId = request.getParameter("dtid");
|
||||
if (desktopId == null || desktopId.length() == 0)
|
||||
{
|
||||
err = "Could not find desktop id";
|
||||
return null;
|
||||
}
|
||||
|
||||
WebApp webApp = session.getWebApp();
|
||||
if (webApp instanceof WebAppCtrl)
|
||||
{
|
||||
WebAppCtrl webAppCtrl = (WebAppCtrl) webApp;
|
||||
Desktop desktop = webAppCtrl.getDesktopCache(session).getDesktopIfAny(desktopId);
|
||||
if (desktop == null)
|
||||
{
|
||||
err = "Could not find desktop";
|
||||
return null;
|
||||
}
|
||||
|
||||
if (desktop instanceof DesktopCtrl)
|
||||
{
|
||||
DesktopCtrl desktopCtrl = (DesktopCtrl) desktop;
|
||||
|
||||
ServerPush serverPush = desktopCtrl.getServerPush();
|
||||
if (serverPush == null)
|
||||
{
|
||||
err = "Server push is not enabled";
|
||||
return null;
|
||||
}
|
||||
|
||||
if (desktopCtrl.getServerPush() instanceof AtmosphereServerPush)
|
||||
{
|
||||
AtmosphereServerPush atmosphereServerPush = (AtmosphereServerPush) serverPush;
|
||||
if (atmosphereServerPush != null)
|
||||
return atmosphereServerPush;
|
||||
}
|
||||
|
||||
err = "Server push implementation is not AtmosphereServerPush";
|
||||
return null;
|
||||
}
|
||||
|
||||
err = "Desktop does not implement DesktopCtrl";
|
||||
return null;
|
||||
}
|
||||
|
||||
err = "Webapp does not implement WebAppCtrl";
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onRequest(AtmosphereResource resource) throws IOException {
|
||||
AtmosphereResponse response = resource.getResponse();
|
||||
|
||||
response.setContentType("text/plain");
|
||||
|
||||
AtmosphereServerPush serverPush = getServerPush(resource);
|
||||
if (serverPush == null)
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
|
||||
response.getWriter().write(err);
|
||||
return;
|
||||
}
|
||||
|
||||
serverPush.updateResource(resource);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStateChange(AtmosphereResourceEvent event) throws IOException {
|
||||
AtmosphereResource resource = event.getResource();
|
||||
|
||||
if (event.isCancelled() || event.isResumedOnTimeout()) {
|
||||
AtmosphereServerPush serverPush = getServerPush(resource);
|
||||
if (serverPush != null)
|
||||
serverPush.clearResource(resource);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void destroy() {
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -38,4 +38,6 @@ Copyright (C) 2007 Ashley G Ramdass (ADempiere WebUI).
|
|||
<javascript src="/js/persist-min.js" charset="UTF-8"/>
|
||||
<javascript src="/js/token.js" charset="UTF-8"/>
|
||||
|
||||
<javascript package="jawwa.atmosphere" merge="true" />
|
||||
|
||||
</language>
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
(function() {
|
||||
jawwa.atmosphere.startServerPush = function(dtid, timeout) {
|
||||
var dt = zk.Desktop.$(dtid);
|
||||
if (dt._serverpush)
|
||||
dt._serverpush.stop();
|
||||
|
||||
var spush = new jawwa.atmosphere.ServerPush(dt, timeout);
|
||||
spush.start();
|
||||
};
|
||||
jawwa.atmosphere.stopServerPush = function(dtid) {
|
||||
var dt = zk.Desktop.$(dtid);
|
||||
if (dt._serverpush)
|
||||
dt._serverpush.stop();
|
||||
};
|
||||
jawwa.atmosphere.ServerPush = zk.$extends(zk.Object, {
|
||||
desktop: null,
|
||||
active: false,
|
||||
timeout: 300000,
|
||||
delay: 1000,
|
||||
failures: 0,
|
||||
|
||||
$init: function(desktop, timeout) {
|
||||
this.desktop = desktop;
|
||||
this.timeout = timeout;
|
||||
},
|
||||
_schedule: function() {
|
||||
if (this.failures < 10) {
|
||||
var delay = this.delay * Math.pow(2, Math.min(this.failures, 7));
|
||||
setTimeout(this.proxy(this._send), delay);
|
||||
} else {
|
||||
this.stop();
|
||||
}
|
||||
},
|
||||
_send: function() {
|
||||
if (!this.active)
|
||||
return;
|
||||
|
||||
var me = this;
|
||||
var jqxhr = $.ajax({
|
||||
url: zk.ajaxURI("/comet", {
|
||||
au: true
|
||||
}),
|
||||
type: "GET",
|
||||
cache: false,
|
||||
async: true,
|
||||
global: false,
|
||||
data: {
|
||||
dtid: this.desktop.id
|
||||
},
|
||||
accepts: "text/plain",
|
||||
dataType: "text/plain",
|
||||
timeout: me.timeout,
|
||||
error: function(jqxhr, textStatus, errorThrown) {
|
||||
me.failures += 1;
|
||||
me._schedule();
|
||||
},
|
||||
success: function(data) {
|
||||
zAu.cmd0.echo(me.desktop);
|
||||
me.failures = 0;
|
||||
me._schedule();
|
||||
}
|
||||
});
|
||||
this._req = jqxhr;
|
||||
},
|
||||
start: function() {
|
||||
this.desktop._serverpush = this;
|
||||
this.active = true;
|
||||
this._send();
|
||||
},
|
||||
stop: function() {
|
||||
this.desktop._serverpush = null;
|
||||
this.active = false;
|
||||
if (this._req) {
|
||||
this._req.abort();
|
||||
this._req = null;
|
||||
}
|
||||
}
|
||||
});
|
||||
})();
|
|
@ -0,0 +1,4 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<package name="jawwa.atmosphere" language="xul/html">
|
||||
<script src="serverpush.js" />
|
||||
</package>
|
|
@ -1,6 +1,27 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<web-app id="WebApp_ID" version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
|
||||
<display-name>ADempiere WebUI</display-name>
|
||||
|
||||
<servlet>
|
||||
<servlet-name>AtmosphereServlet</servlet-name>
|
||||
<servlet-class>org.atmosphere.cpr.AtmosphereServlet</servlet-class>
|
||||
<init-param>
|
||||
<param-name>org.atmosphere.cpr.AtmosphereHandler</param-name>
|
||||
<param-value>fi.jawsy.jawwa.zk.atmosphere.ZkAtmosphereHandler</param-value>
|
||||
</init-param>
|
||||
<init-param>
|
||||
<param-name>org.atmosphere.cpr.AtmosphereHandler.contextRoot</param-name>
|
||||
<param-value>/zkau/comet</param-value>
|
||||
</init-param>
|
||||
<load-on-startup>0</load-on-startup>
|
||||
<!-- Remove async-supported if you are not using Servlet 3.0 -->
|
||||
<async-supported>true</async-supported>
|
||||
</servlet>
|
||||
<servlet-mapping>
|
||||
<servlet-name>AtmosphereServlet</servlet-name>
|
||||
<url-pattern>/zkau/comet</url-pattern>
|
||||
</servlet-mapping>
|
||||
|
||||
<!-- /// -->
|
||||
<!-- DSP -->
|
||||
<servlet>
|
||||
|
|
|
@ -51,7 +51,8 @@
|
|||
-->
|
||||
<device-config>
|
||||
<device-type>ajax</device-type>
|
||||
<server-push-class>org.zkoss.zk.ui.impl.PollingServerPush</server-push-class>
|
||||
<!-- <server-push-class>org.zkoss.zk.ui.impl.PollingServerPush</server-push-class> -->
|
||||
<server-push-class>fi.jawsy.jawwa.zk.atmosphere.AtmosphereServerPush</server-push-class>
|
||||
</device-config>
|
||||
|
||||
<system-config>
|
||||
|
|
|
@ -10,7 +10,12 @@ bin.includes = META-INF/,\
|
|||
zul/,\
|
||||
timeout.zul,\
|
||||
plugin.xml,\
|
||||
WEB-INF/classes/
|
||||
WEB-INF/classes/,\
|
||||
WEB-INF/lib/atmosphere-runtime-0.9.jar,\
|
||||
WEB-INF/lib/atmosphere-compat-jbossweb-0.9.jar,\
|
||||
WEB-INF/lib/atmosphere-compat-tomcat-0.9.jar,\
|
||||
WEB-INF/lib/atmosphere-compat-tomcat7-0.9.jar,\
|
||||
WEB-INF/lib/slf4j-api-1.6.1.jar
|
||||
src.includes = WEB-INF/classes/,\
|
||||
WEB-INF/tld/,\
|
||||
WEB-INF/web.xml,\
|
||||
|
@ -25,5 +30,5 @@ src.includes = WEB-INF/classes/,\
|
|||
zul/
|
||||
bin.excludes = WEB-INF/src/,\
|
||||
WEB-INF/web-2.5.xml
|
||||
source.. = WEB-INF/src/
|
||||
jars.compile.order =
|
||||
|
||||
|
|
Loading…
Reference in New Issue