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;
|
package fi.jawsy.jawwa.zk.atmosphere;
|
||||||
|
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
@ -21,12 +40,12 @@ import org.zkoss.zk.ui.util.Clients;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ZK server push implementation based on Atmosphere.
|
* ZK server push implementation based on Atmosphere.
|
||||||
*
|
* Adapted from https://github.com/Gekkio/jawwa/tree/develop/zk-atmosphere version 0.3.1-SNAPSHOT
|
||||||
* Only supports asynchronous updates (Executions.schedule) and will throw exceptions if synchronous updates
|
|
||||||
* (Executions.activate/deactivate) is attempted.
|
|
||||||
*/
|
*/
|
||||||
public class AtmosphereServerPush implements ServerPush {
|
public class AtmosphereServerPush implements ServerPush {
|
||||||
|
|
||||||
|
private static final String ON_ACTIVATE_DESKTOP = "onActivateDesktop";
|
||||||
|
|
||||||
public static final int DEFAULT_TIMEOUT = 1000 * 60 * 5;
|
public static final int DEFAULT_TIMEOUT = 1000 * 60 * 5;
|
||||||
|
|
||||||
private final AtomicReference<Desktop> desktop = new AtomicReference<Desktop>();
|
private final AtomicReference<Desktop> desktop = new AtomicReference<Desktop>();
|
||||||
|
@ -61,7 +80,7 @@ public class AtmosphereServerPush implements ServerPush {
|
||||||
EventListener<Event> task = new EventListener<Event>() {
|
EventListener<Event> task = new EventListener<Event>() {
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(Event event) throws Exception {
|
public void onEvent(Event event) throws Exception {
|
||||||
if (event.getName().equals("onNewData"))
|
if (event.getName().equals(ON_ACTIVATE_DESKTOP))
|
||||||
{
|
{
|
||||||
synchronized (_mutex) {
|
synchronized (_mutex) {
|
||||||
_carryOver = new ExecutionCarryOver(desktop.get());
|
_carryOver = new ExecutionCarryOver(desktop.get());
|
||||||
|
@ -82,7 +101,7 @@ public class AtmosphereServerPush implements ServerPush {
|
||||||
};
|
};
|
||||||
|
|
||||||
synchronized (info) {
|
synchronized (info) {
|
||||||
Executions.schedule(desktop.get(), task, new Event("onNewData"));
|
Executions.schedule(desktop.get(), task, new Event(ON_ACTIVATE_DESKTOP));
|
||||||
if (info.nActive == 0)
|
if (info.nActive == 0)
|
||||||
info.wait(timeout <= 0 ? 10*60*1000: timeout);
|
info.wait(timeout <= 0 ? 10*60*1000: timeout);
|
||||||
}
|
}
|
||||||
|
@ -130,8 +149,7 @@ public class AtmosphereServerPush implements ServerPush {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isActive() {
|
public boolean isActive() {
|
||||||
// throw new UnsupportedOperationException("isActive is not supported by AtmosphereServerPush");
|
return _active != null && _active.nActive > 0;
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@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;
|
package fi.jawsy.jawwa.zk.atmosphere;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
@ -18,6 +37,7 @@ import org.zkoss.zk.ui.sys.WebAppCtrl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Atmosphere handler that integrates Atmosphere with ZK server push.
|
* 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 {
|
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.desktop.IDesktop;
|
||||||
import org.adempiere.webui.session.SessionManager;
|
import org.adempiere.webui.session.SessionManager;
|
||||||
import org.adempiere.webui.theme.ThemeManager;
|
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.acct.Doc;
|
||||||
import org.compiere.model.GridWindowVO;
|
import org.compiere.model.GridWindowVO;
|
||||||
import org.compiere.model.Lookup;
|
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
|
* @param runnable
|
||||||
*/
|
*/
|
||||||
public static void executeDesktopTask(Runnable runnable) {
|
public static void executeDesktopTask(final Runnable runnable) {
|
||||||
boolean inUIThread = Executions.getCurrent() != null;
|
Desktop desktop = getDesktop();
|
||||||
boolean desktopActivated = false;
|
ServerPushTemplate template = new ServerPushTemplate(desktop);
|
||||||
|
template.execute(new IServerPushCallback() {
|
||||||
Desktop desktop = null;
|
@Override
|
||||||
try {
|
public void updateUI() {
|
||||||
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();
|
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() {
|
public static Desktop getDesktop() {
|
||||||
boolean inUIThread = Executions.getCurrent() != null;
|
boolean inUIThread = Executions.getCurrent() != null;
|
||||||
return inUIThread ? Executions.getCurrent().getDesktop()
|
return inUIThread ? Executions.getCurrent().getDesktop()
|
||||||
: (Desktop) Env.getCtx().get(AdempiereWebUI.ZK_DESKTOP_SESSION_KEY);
|
: (Desktop) Env.getCtx().get(AdempiereWebUI.ZK_DESKTOP_SESSION_KEY);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if running on a tablet
|
||||||
|
*/
|
||||||
public static boolean isTablet() {
|
public static boolean isTablet() {
|
||||||
IDesktop appDesktop = SessionManager.getAppDesktop();
|
IDesktop appDesktop = SessionManager.getAppDesktop();
|
||||||
return appDesktop != null ? appDesktop.getClientInfo().tablet : false;
|
return appDesktop != null ? appDesktop.getClientInfo().tablet : false;
|
||||||
|
|
|
@ -219,7 +219,7 @@ public class DPActivities extends DashboardPanel implements EventListener {
|
||||||
noOfWorkflow = getWorkflowCount();
|
noOfWorkflow = getWorkflowCount();
|
||||||
if (isShowUnprocessed) noOfUnprocessed = getUnprocessedCount();
|
if (isShowUnprocessed) noOfUnprocessed = getUnprocessedCount();
|
||||||
|
|
||||||
template.execute(this);
|
template.executeAsync(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -241,7 +241,7 @@ public class DPRecentItems extends DashboardPanel implements EventListener<Event
|
||||||
@Override
|
@Override
|
||||||
public void refresh(ServerPushTemplate template)
|
public void refresh(ServerPushTemplate template)
|
||||||
{
|
{
|
||||||
template.execute(this);
|
template.executeAsync(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -169,7 +169,7 @@ public class DefaultDesktop extends TabbedDesktop implements MenuListener, Seria
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
ServerPushTemplate template = new ServerPushTemplate(layout.getDesktop());
|
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();
|
noOfRequest = DPActivities.getRequestCount();
|
||||||
noOfWorkflow = DPActivities.getWorkflowCount();
|
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());
|
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();
|
noOfRequest = DPActivities.getRequestCount();
|
||||||
noOfWorkflow = DPActivities.getWorkflowCount();
|
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());
|
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();
|
noOfRequest = DPActivities.getRequestCount();
|
||||||
noOfWorkflow = DPActivities.getWorkflowCount();
|
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
|
* @param callback
|
||||||
*/
|
*/
|
||||||
public void execute(final IServerPushCallback callback) {
|
public void executeAsync(final IServerPushCallback callback) {
|
||||||
boolean inUIThread = Executions.getCurrent() != null;
|
|
||||||
try {
|
try {
|
||||||
if (!inUIThread) {
|
|
||||||
EventListener<Event> task = new EventListener<Event>() {
|
EventListener<Event> task = new EventListener<Event>() {
|
||||||
@Override
|
@Override
|
||||||
public void onEvent(Event event) throws Exception {
|
public void onEvent(Event event) throws Exception {
|
||||||
|
@ -53,9 +52,6 @@ public class ServerPushTemplate {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Executions.schedule(desktop, task, new Event("onExecute"));
|
Executions.schedule(desktop, task, new Event("onExecute"));
|
||||||
} else {
|
|
||||||
callback.updateUI();
|
|
||||||
}
|
|
||||||
} catch (DesktopUnavailableException de) {
|
} catch (DesktopUnavailableException de) {
|
||||||
throw de;
|
throw de;
|
||||||
} catch (Exception e) {
|
} 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
|
* @return desktop
|
||||||
|
|
Loading…
Reference in New Issue