diff --git a/org.adempiere.base/src/org/compiere/Adempiere.java b/org.adempiere.base/src/org/compiere/Adempiere.java
index 73ad45a85a..dc483443c0 100644
--- a/org.adempiere.base/src/org/compiere/Adempiere.java
+++ b/org.adempiere.base/src/org/compiere/Adempiere.java
@@ -553,7 +553,7 @@ public final class Adempiere
} // startup
private static void createThreadPool() {
- int max = Runtime.getRuntime().availableProcessors() * 3;
+ int max = Runtime.getRuntime().availableProcessors() * 20;
int min = max / 2;
int defaultMax = max;
int defaultMin = min;
diff --git a/org.adempiere.ui.zk/.classpath b/org.adempiere.ui.zk/.classpath
index 291b7a5ac1..86305e2f48 100644
--- a/org.adempiere.ui.zk/.classpath
+++ b/org.adempiere.ui.zk/.classpath
@@ -1,12 +1,12 @@
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/org.adempiere.ui.zk/META-INF/MANIFEST.MF b/org.adempiere.ui.zk/META-INF/MANIFEST.MF
index 45c5b862bb..2ece757b45 100644
--- a/org.adempiere.ui.zk/META-INF/MANIFEST.MF
+++ b/org.adempiere.ui.zk/META-INF/MANIFEST.MF
@@ -16,12 +16,13 @@ Import-Package: javax.servlet,
org.slf4j.helpers;version="1.6.1",
org.slf4j.spi;version="1.6.1"
DynamicImport-Package: action.images
-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/calendar.jar
+Bundle-ClassPath: .,
+ WEB-INF/classes/,
+ WEB-INF/lib/calendar.jar,
+ WEB-INF/lib/atmosphere-compat-jbossweb-1.0.4.jar,
+ WEB-INF/lib/atmosphere-compat-tomcat-1.0.4.jar,
+ WEB-INF/lib/atmosphere-compat-tomcat7-1.0.4.jar,
+ WEB-INF/lib/atmosphere-runtime-1.0.4.jar
Export-Package: metainfo.zk,
org.adempiere.webui,
org.adempiere.webui.acct,
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 8b03c20341..a9c8ef6dda 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
@@ -20,9 +20,11 @@ the License.
package fi.jawsy.jawwa.zk.atmosphere;
import java.io.IOException;
+import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import org.atmosphere.cpr.AtmosphereResource;
+import org.compiere.Adempiere;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.zkoss.lang.Library;
@@ -34,6 +36,7 @@ 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;
@@ -112,35 +115,19 @@ public class AtmosphereServerPush implements ServerPush {
return true;
}
- public synchronized void clearResource(AtmosphereResource resource) {
+ public void clearResource(AtmosphereResource resource) {
this.resource.compareAndSet(resource, null);
}
- private synchronized void commitResponse() throws IOException {
+ private boolean commitResponse() throws IOException {
AtmosphereResource resource = this.resource.getAndSet(null);
- if (resource != null) {
- resource.resume();
- }
+ if (resource != null && resource.isSuspended()) {
+ resource.resume();
+ return true;
+ }
+ return false;
}
- private synchronized void onPush() throws IOException {
- AtmosphereResource resource = this.resource.get();
- if (resource != null) {
- switch (resource.transport()) {
- case POLLING:
- case LONG_POLLING:
- if (resource.isSuspended())
- commitResponse();
- break;
- case WEBSOCKET :
- case STREAMING:
- resource.getResponse().getWriter().write("");
- resource.getResponse().getWriter().flush();
- break;
- }
- }
- }
-
@Override
public boolean deactivate(boolean stop) {
boolean stopped = false;
@@ -175,11 +162,11 @@ public class AtmosphereServerPush implements ServerPush {
}
@Override
- public synchronized void schedule(EventListener task, T event,
+ public void schedule(EventListener task, T event,
Scheduler scheduler) {
scheduler.schedule(task, event);
try {
- onPush();
+ commitResponse();
} catch (IOException e) {
log.error(e.getLocalizedMessage(), e);
}
@@ -215,26 +202,53 @@ public class AtmosphereServerPush implements ServerPush {
}
}
- public synchronized void onRequest(AtmosphereResource resource) {
- if (this.resource.get() != null) {
- AtmosphereResource aResource = this.resource.get();
- if (aResource != resource) {
- try {
- onPush();
- } catch (IOException e) {
- log.error(e.getLocalizedMessage(), e);
- }
- }
- }
-
- this.resource.set(resource);
- if (log.isTraceEnabled()) {
+ public void onRequest(AtmosphereResource resource) {
+ if (log.isTraceEnabled()) {
log.trace(resource.transport().name());
}
- if (!resource.isSuspended()) {
- resource.suspend(-1, true);
- }
+
+ try {
+ commitResponse();
+ } catch (IOException e) {
+ log.error(e.getLocalizedMessage(), e);
+ }
+
+ DesktopCtrl desktopCtrl = (DesktopCtrl) this.desktop.get();
+ if (desktopCtrl == null) {
+ log.error("No desktop available");
+ return;
+ }
+
+ boolean suspend = !desktopCtrl.scheduledServerPush();
+ if (suspend) {
+ if (!resource.isSuspended()) {
+ resource.suspend(-1, true);
+ }
+ this.resource.set(resource);
+
+ //check again, just in case task is schedule between the resource.suspend and resource.set call
+ if (desktopCtrl.scheduledServerPush()) {
+ scheduleCommit();
+ }
+ }
}
+
+ private void scheduleCommit() {
+ Adempiere.getThreadPoolExecutor().schedule(new Runnable() {
+ @Override
+ public void run() {
+ DesktopCtrl desktopCtrl = (DesktopCtrl) AtmosphereServerPush.this.desktop.get();
+ if (desktopCtrl == null) return;
+ if (desktopCtrl.scheduledServerPush()) {
+ try {
+ commitResponse();
+ } catch (IOException e) {
+ log.error(e.getLocalizedMessage(), e);
+ }
+ }
+ }
+ }, 100, TimeUnit.MILLISECONDS);
+ }
private static class ThreadInfo {
private final Thread thread;
diff --git a/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/jquery.atmosphere.js b/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/jquery.atmosphere.js
index 1b90e0df6d..286c728330 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/jquery.atmosphere.js
+++ b/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/jquery.atmosphere.js
@@ -1,4 +1,6 @@
/**
+ * Copyright 2012 Jeanfrancois Arcand
+ *
* 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
@@ -12,23 +14,32 @@
* limitations under the License.
*/
/*
- * Part of this code has been taked from
- *
- * jQuery Stream @VERSION
- * Comet Streaming JavaScript Library
- * http://code.google.com/p/jquery-stream/
+ * IE streaming/XDR supports is copied/highly inspired by http://code.google.com/p/jquery-stream/
*
* Copyright 2011, Donghwan Kim
* Licensed under the Apache License, Version 2.0
* http://www.apache.org/licenses/LICENSE-2.0
*
- * Compatible with jQuery 1.5+
+ * LocalStorage supports is copied/highly inspired by https://github.com/flowersinthesand/jquery-socket
+ * Copyright 2011, Donghwan Kim
+ * Licensed under the Apache License, Version 2.0
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * */
+/**
+ * Official documentation of this library: https://github.com/Atmosphere/atmosphere/wiki/jQuery.atmosphere.js-API
*/
jQuery.atmosphere = function() {
- jQuery(window).unload(function() {
+ jQuery(window).bind("unload.atmosphere", function() {
jQuery.atmosphere.unsubscribe();
});
+ // Prevent ESC to kill the connection from Firefox.
+ jQuery(window).keypress(function(e){
+ if(e.keyCode == 27){
+ e.preventDefault();
+ }
+ });
+
var parseHeaders = function(headerString) {
var match, rheaders = /^(.*?):[ \t]*([^\r\n]*)\r?$/mg, headers = {};
while (match = rheaders.exec(headerString)) {
@@ -38,7 +49,7 @@ jQuery.atmosphere = function() {
};
return {
- version : 0.9,
+ version : "1.0.3",
requests : [],
callbacks : [],
@@ -54,6 +65,10 @@ jQuery.atmosphere = function() {
},
onMessagePublished : function(response) {
},
+ onTransportFailure : function(response) {
+ },
+ onLocalMessage : function (response) {
+ },
AtmosphereRequest : function(options) {
@@ -66,15 +81,12 @@ jQuery.atmosphere = function() {
method: 'GET',
headers: {},
contentType : '',
- cache: true,
- async: true,
- ifModified: false,
callback: null,
- dataType: '',
url : '',
data : '',
suspend : true,
maxRequest : 60,
+ reconnect : true,
maxStreamingLength : 10000000,
lastIndex : 0,
logLevel : 'info',
@@ -95,6 +107,11 @@ jQuery.atmosphere = function() {
trackMessageLength : false ,
messageDelimiter : '|',
connectTimeout : -1,
+ reconnectInterval : 0,
+ dropAtmosphereHeaders : true,
+ uuid : 0,
+ shared : false,
+ readResponsesHeaders : true,
onError : function(response) {
},
onClose : function(response) {
@@ -106,6 +123,10 @@ jQuery.atmosphere = function() {
onReconnect : function(request, response) {
},
onMessagePublished : function(response) {
+ },
+ onTransportFailure : function (reason, request) {
+ },
+ onLocalMessage : function (request) {
}
};
@@ -116,21 +137,15 @@ jQuery.atmosphere = function() {
var _response = {
status: 200,
responseBody : '',
- expectedBodySize : -1,
headers : [],
state : "messageReceived",
transport : "polling",
error: null,
+ request : null,
+ partialMessage : "",
id : 0
};
- /**
- * {number} Request id.
- *
- * @private
- */
- var _uuid = 0;
-
/**
* {websocket} Opened web socket.
*
@@ -188,6 +203,33 @@ jQuery.atmosphere = function() {
*/
var _abordingConnection = false;
+ /**
+ * A local "channel' of communication.
+ * @private
+ */
+ var _localSocketF = null;
+
+ /**
+ * The storage used.
+ * @private
+ */
+ var _storageService;
+
+ /**
+ * Local communication
+ * @private
+ */
+ var _localStorageService = null;
+
+ /**
+ * A Unique ID
+ * @private
+ */
+ var guid = jQuery.now();
+
+ /** Trace time */
+ var _traceTimer;
+
// Automatic call to subscribe
_subscribe(options);
@@ -197,7 +239,6 @@ jQuery.atmosphere = function() {
* @private
*/
function _init() {
- _uuid = 0;
_subscribed = true;
_abordingConnection = false;
_requestCount = 0;
@@ -213,7 +254,7 @@ jQuery.atmosphere = function() {
* @private
*/
function _reinit() {
- _close();
+ _clearState();
_init();
}
@@ -229,9 +270,6 @@ jQuery.atmosphere = function() {
_reinit();
_request = jQuery.extend(_request, options);
- _uuid = jQuery.atmosphere.guid();
-
- _execute();
}
/**
@@ -267,40 +305,393 @@ jQuery.atmosphere = function() {
* @private
*/
function _execute() {
+ // Shared across multiple tabs/windows.
+ if (_request.shared) {
+
+ var version = 0;
+ if (navigator.appVersion.indexOf("MSIE") != -1) {
+ version = parseFloat(navigator.appVersion.split("MSIE")[1]);
+ }
+
+ // Multi Tab aren't working on IE 8. Tested with atmosphere.js and jquery-socket.js
+ // both pops up a blank page.
+ if (version != 8) {
+ _localStorageService = _local(_request);
+ if (_localStorageService != null) {
+ if (_request.logLevel == 'debug') {
+ jQuery.atmosphere.debug("Storage service available. All communication will be local");
+ }
+
+ if (_localStorageService.open(_request)) {
+ // Local connection.
+ return;
+ }
+ }
+
+ if (_request.logLevel == 'debug') {
+ jQuery.atmosphere.debug("No Storage service available.");
+ }
+ } else {
+ jQuery.atmosphere.info("Multi tab not supported on IE 8.");
+ }
+ // Wasn't local or an error occurred
+ _localStorageService = null;
+ }
+
if (_request.transport != 'websocket' && _request.transport != 'sse') {
- _open('opening', _request.transport);
+ _open('opening', _request.transport, _request);
_executeRequest();
} else if (_request.transport == 'websocket') {
if (!_supportWebsocket()) {
- jQuery.atmosphere.log(_request.logLevel, ["Websocket is not supported, using request.fallbackTransport (" + _request.fallbackTransport + ")"]);
- _open('opening', _request.fallbackTransport);
- _reconnectWithFallbackTransport();
+ _reconnectWithFallbackTransport("Websocket is not supported, using request.fallbackTransport (" + _request.fallbackTransport + ")");
} else {
_executeWebSocket(false);
}
} else if (_request.transport == 'sse') {
if (!_supportSSE()) {
- jQuery.atmosphere.log(_request.logLevel, ["Server Side Events(SSE) is not supported, using request.fallbackTransport (" + _request.fallbackTransport + ")"]);
- _open('opening', _request.fallbackTransport);
- _reconnectWithFallbackTransport();
+ _reconnectWithFallbackTransport("Server Side Events(SSE) is not supported, using request.fallbackTransport (" + _request.fallbackTransport + ")");
} else {
_executeSSE(false);
}
}
}
+ function _local(request) {
+ var trace, connector, orphan, name = "atmosphere-" + request.url, connectors = {
+ storage: function() {
+ if (!jQuery.atmosphere.supportStorage()) {
+ return;
+ }
+
+ var storage = window.localStorage,
+ get = function(key) {
+ return jQuery.parseJSON(storage.getItem(name + "-" + key));
+ },
+ set = function(key, value) {
+ storage.setItem(name + "-" + key, jQuery.stringifyJSON(value));
+ };
+
+ return {
+ init: function() {
+ set("children", get("children").concat([guid]));
+ jQuery(window).on("storage.socket", function(event) {
+ event = event.originalEvent;
+ if (event.key === name && event.newValue) {
+ listener(event.newValue);
+ }
+ });
+ return get("opened");
+ },
+ signal: function(type, data) {
+ storage.setItem(name, jQuery.stringifyJSON({target: "p", type: type, data: data}));
+ },
+ close: function() {
+ var index, children = get("children");
+
+ jQuery(window).off("storage.socket");
+ if (children) {
+ index = jQuery.inArray(request.id, children);
+ if (index > -1) {
+ children.splice(index, 1);
+ set("children", children);
+ }
+ }
+ }
+ };
+ },
+ windowref: function() {
+ var win = window.open("", name.replace(/\W/g, ""));
+
+ if (!win || win.closed || !win.callbacks) {
+ return;
+ }
+
+ return {
+ init: function() {
+ win.callbacks.push(listener);
+ win.children.push(guid);
+ return win.opened;
+ },
+ signal: function(type, data) {
+ if (!win.closed && win.fire) {
+ win.fire(jQuery.stringifyJSON({target: "p", type: type, data: data}));
+ }
+ },
+ close : function() {
+ function remove(array, e) {
+ var index = jQuery.inArray(e, array);
+ if (index > -1) {
+ array.splice(index, 1);
+ }
+ }
+
+ // Removes traces only if the parent is alive
+ if (!orphan) {
+ remove(win.callbacks, listener);
+ remove(win.children, guid);
+ }
+ }
+
+ };
+ }
+ };
+
+ // Receives open, close and message command from the parent
+ function listener(string) {
+ var command = jQuery.parseJSON(string), data = command.data;
+
+ if (command.target === "c") {
+ switch (command.type) {
+ case "open":
+ _open("opening", 'local', _request)
+ break;
+ case "close":
+ if (!orphan) {
+ orphan = true;
+ if (data.reason === "aborted") {
+ _close();
+ } else {
+ // Gives the heir some time to reconnect
+ if (data.heir === guid) {
+ _execute();
+ } else {
+ setTimeout(function() {
+ _execute();
+ }, 100);
+ }
+ }
+ }
+ break;
+ case "message":
+ _prepareCallback(data, "messageReceived", 200, request.transport);
+ break;
+ case "localMessage":
+ _localMessage(data);
+ break;
+ }
+ }
+ }
+
+ function findTrace() {
+ var matcher = new RegExp("(?:^|; )(" + encodeURIComponent(name) + ")=([^;]*)").exec(document.cookie);
+ if (matcher) {
+ return jQuery.parseJSON(decodeURIComponent(matcher[2]));
+ }
+ }
+
+ // Finds and validates the parent socket's trace from the cookie
+ trace = findTrace();
+ if (!trace || jQuery.now() - trace.ts > 1000) {
+ return;
+ }
+
+ // Chooses a connector
+ connector = connectors.storage() || connectors.windowref();
+ if (!connector) {
+ return;
+ }
+
+ return {
+ open: function() {
+ var parentOpened;
+
+ // Checks the shared one is alive
+ _traceTimer = setInterval(function() {
+ var oldTrace = trace;
+ trace = findTrace();
+ if (!trace || oldTrace.ts === trace.ts) {
+ // Simulates a close signal
+ listener(jQuery.stringifyJSON({target: "c", type: "close", data: {reason: "error", heir: oldTrace.heir}}));
+ }
+ }, 1000);
+
+ parentOpened = connector.init();
+ if (parentOpened) {
+ // Firing the open event without delay robs the user of the opportunity to bind connecting event handlers
+ setTimeout(function() {
+ _open("opening", 'local', request)
+ }, 50);
+ }
+ return parentOpened;
+ },
+ send: function(event) {
+ connector.signal("send", event);
+ },
+ localSend: function(event) {
+ connector.signal("localSend", jQuery.stringifyJSON({id: guid , event: event}));
+ },
+ close: function() {
+ // Do not signal the parent if this method is executed by the unload event handler
+ if (!_abordingConnection) {
+ clearInterval(_traceTimer);
+ connector.signal("close");
+ connector.close();
+ }
+ }
+ };
+ };
+
+ function share() {
+ var storageService, name = "atmosphere-" + _request.url, servers = {
+ // Powered by the storage event and the localStorage
+ // http://www.w3.org/TR/webstorage/#event-storage
+ storage: function() {
+ if (!jQuery.atmosphere.supportStorage()) {
+ return;
+ }
+
+ var storage = window.localStorage;
+
+ return {
+ init: function() {
+ // Handles the storage event
+ jQuery(window).on("storage.socket", function(event) {
+ event = event.originalEvent;
+ // When a deletion, newValue initialized to null
+ if (event.key === name && event.newValue) {
+ listener(event.newValue);
+ }
+ });
+ },
+ signal: function(type, data) {
+ storage.setItem(name, jQuery.stringifyJSON({target: "c", type: type, data: data}));
+ },
+ get: function(key) {
+ return jQuery.parseJSON(storage.getItem(name + "-" + key));
+ },
+ set: function(key, value) {
+ storage.setItem(name + "-" + key, jQuery.stringifyJSON(value));
+ },
+ close : function() {
+ jQuery(window).off("storage.socket");
+ storage.removeItem(name);
+ storage.removeItem(name + "-opened");
+ storage.removeItem(name + "-children");
+ }
+
+ };
+ },
+ // Powered by the window.open method
+ // https://developer.mozilla.org/en/DOM/window.open
+ windowref: function() {
+ // Internet Explorer raises an invalid argument error
+ // when calling the window.open method with the name containing non-word characters
+ var neim = name.replace(/\W/g, ""), win = (jQuery('iframe[name="' + neim + '"]')[0]
+ || jQuery('').hide().appendTo("body")[0]).contentWindow;
+
+ return {
+ init: function() {
+ // Callbacks from different windows
+ win.callbacks = [listener];
+ // In IE 8 and less, only string argument can be safely passed to the function in other window
+ win.fire = function(string) {
+ var i;
+
+ for (i = 0; i < win.callbacks.length; i++) {
+ win.callbacks[i](string);
+ }
+ };
+ },
+ signal: function(type, data) {
+ if (!win.closed && win.fire) {
+ win.fire(jQuery.stringifyJSON({target: "c", type: type, data: data}));
+ }
+ },
+ get: function(key) {
+ return !win.closed ? win[key] : null;
+ },
+ set: function(key, value) {
+ if (!win.closed) {
+ win[key] = value;
+ }
+ },
+ close : function() {}
+ };
+ }
+ };
+
+
+ // Receives send and close command from the children
+ function listener(string) {
+ var command = jQuery.parseJSON(string), data = command.data;
+
+ if (command.target === "p") {
+ switch (command.type) {
+ case "send":
+ _push(data);
+ break;
+ case "localSend":
+ _localMessage(data);
+ break;
+ case "close":
+ _close();
+ break;
+ }
+ }
+ }
+
+ _localSocketF = function propagateMessageEvent(context) {
+ storageService.signal("message", context);
+ }
+
+ function leaveTrace() {
+ document.cookie = encodeURIComponent(name) + "=" +
+ // Opera's JSON implementation ignores a number whose a last digit of 0 strangely
+ // but has no problem with a number whose a last digit of 9 + 1
+ encodeURIComponent(jQuery.stringifyJSON({ts: jQuery.now() + 1, heir: (storageService.get("children") || [])[0]}));
+ }
+
+ // Chooses a storageService
+ storageService = servers.storage() || servers.windowref();
+ storageService.init();
+
+ if (_request.logLevel == 'debug') {
+ jQuery.atmosphere.debug("Installed StorageService " + storageService);
+ }
+
+ // List of children sockets
+ storageService.set("children", []);
+
+ if (storageService.get("opened") != null && !storageService.get("opened")) {
+ // Flag indicating the parent socket is opened
+ storageService.set("opened", false);
+ }
+ // Leaves traces
+ leaveTrace();
+ _traceTimer = setInterval(leaveTrace, 1000);
+
+ _storageService = storageService;
+ }
+
/**
* @private
*/
- function _open(state, transport) {
+ function _open(state, transport, request) {
+ if (_request.shared && transport != 'local') {
+ share();
+ }
+
+ if (_storageService != null) {
+ _storageService.set("opened", true);
+ }
+
+ request.close = function() {
+ _close();
+ request.reconnect = false;
+ }
+
+ _response.request = request;
var prevState = _response.state;
_response.state = state;
_response.status = 200;
var prevTransport = _response.transport;
_response.transport = transport;
- _response.responseBody = "";
+
+ var _body = _response.responseBody;
_invokeCallback();
+ _response.responseBody = _body;
+
_response.state = prevState;
_response.transport = prevTransport;
}
@@ -314,6 +705,9 @@ jQuery.atmosphere = function() {
* @private
*/
function _jsonp(request) {
+ // When CORS is enabled, make sure we force the proper transport.
+ request.transport="jsonp";
+
var rq = _request;
if ((request != null) && (typeof(request) != 'undefined')) {
rq = request;
@@ -324,7 +718,7 @@ jQuery.atmosphere = function() {
if (rq.attachHeadersAsQueryString) {
url = _attachHeaders(rq);
if (data != '') {
- url += "&X-Atmosphere-Post-Body=" + data;
+ url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data);
}
data = '';
}
@@ -334,7 +728,7 @@ jQuery.atmosphere = function() {
type : rq.method,
dataType: "jsonp",
error : function(jqXHR, textStatus, errorThrown) {
- if (jqXHR.status < 300) {
+ if (jqXHR.status < 300 && rq.requestCount++ < rq.maxRequest) {
_reconnect(_jqxhr, rq);
} else {
_prepareCallback(textStatus, "error", jqXHR.status, rq.transport);
@@ -342,23 +736,89 @@ jQuery.atmosphere = function() {
},
jsonp : "jsonpTransport",
success: function(json) {
- if (rq.executeCallbackBeforeReconnect) {
- _reconnect(_jqxhr, rq);
- }
- var msg = json.message;
- if (msg != null && typeof msg != 'string') {
- try {
- msg = jQuery.stringifyJSON(msg);
- } catch (err) {
- // The message was partial
+ if (rq.requestCount++ < rq.maxRequest) {
+ _readHeaders(_jqxhr, rq);
+
+ if (!rq.executeCallbackBeforeReconnect) {
+ _reconnect(_jqxhr, rq);
}
+
+ var msg = json.message;
+ if (msg != null && typeof msg != 'string') {
+ try {
+ msg = jQuery.stringifyJSON(msg);
+ } catch (err) {
+ // The message was partial
+ }
+ }
+
+ _prepareCallback(msg, "messageReceived", 200, rq.transport);
+
+ if (rq.executeCallbackBeforeReconnect) {
+ _reconnect(_jqxhr, rq);
+ }
+ } else {
+ jQuery.atmosphere.log(_request.logLevel, ["JSONP reconnect maximum try reached " + _request.requestCount]);
+ _onError();
}
+ },
+ data : rq.data,
+ beforeSend : function(jqXHR) {
+ _doRequest(jqXHR, rq, false);
+ }
+ });
+ }
- _prepareCallback(msg, "messageReceived", 200, rq.transport);
+ /**
+ * Execute request using ajax transport.
+ *
+ * @param request
+ * {Object} request Request parameters, if
+ * undefined _request object will be used.
+ * @private
+ */
+ function _ajax(request) {
+ var rq = _request;
+ if ((request != null) && (typeof(request) != 'undefined')) {
+ rq = request;
+ }
- if (!rq.executeCallbackBeforeReconnect) {
+ var url = rq.url;
+ var data = rq.data;
+ if (rq.attachHeadersAsQueryString) {
+ url = _attachHeaders(rq);
+ if (data != '') {
+ url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(data);
+ }
+ data = '';
+ }
+
+ _jqxhr = jQuery.ajax({
+ url : url,
+ type : rq.method,
+ error : function(jqXHR, textStatus, errorThrown) {
+ if (jqXHR.status < 300 && rq.requestCount++ < rq.maxRequest) {
_reconnect(_jqxhr, rq);
+ } else {
+ _prepareCallback(textStatus, "error", jqXHR.status, rq.transport);
+ }
+ },
+ success: function(data, textStatus, jqXHR) {
+
+ if (rq.requestCount++ < rq.maxRequest) {
+ if (!rq.executeCallbackBeforeReconnect) {
+ _reconnect(_jqxhr, rq);
+ }
+
+ _prepareCallback(data, "messageReceived", 200, rq.transport);
+
+ if (rq.executeCallbackBeforeReconnect) {
+ _reconnect(_jqxhr, rq);
+ }
+ } else {
+ jQuery.atmosphere.log(_request.logLevel, ["AJAX reconnect maximum try reached " + _request.requestCount]);
+ _onError();
}
},
data : rq.data,
@@ -396,12 +856,8 @@ jQuery.atmosphere = function() {
* @private
*/
function _buildWebSocketUrl() {
- var url = _request.url;
- url = _attachHeaders();
- if (url.indexOf("http") == -1 && url.indexOf("ws") == -1) {
- url = jQuery.atmosphere.parseUri(document.location, url);
- }
- return url.replace('http:', 'ws:').replace('https:', 'wss:');
+ var url = _attachHeaders(_request);
+ return decodeURI(jQuery('')[0].href.replace(/^http/, "ws"));
}
/**
@@ -411,8 +867,7 @@ jQuery.atmosphere = function() {
* @private
*/
function _buildSSEUrl() {
- var url = _request.url;
- url = _attachHeaders();
+ var url = _attachHeaders(_request);
return url;
}
@@ -429,10 +884,21 @@ jQuery.atmosphere = function() {
var location = _buildSSEUrl(_request.url);
- jQuery.atmosphere.log(_request.logLevel, ["Invoking executeSSE"]);
if (_request.logLevel == 'debug') {
+ jQuery.atmosphere.debug("Invoking executeSSE");
jQuery.atmosphere.debug("Using URL: " + location);
}
+
+ if (sseOpened) {
+ _open('re-opening', "sse", _request);
+ }
+
+ if (!_request.reconnect) {
+ if (_sse != null) {
+ _sse.close();
+ }
+ return;
+ }
_sse = new EventSource(location, {withCredentials: _request.withCredentials});
if (_request.connectTimeout > 0) {
@@ -448,9 +914,9 @@ jQuery.atmosphere = function() {
jQuery.atmosphere.debug("SSE successfully opened");
}
- _subscribed = true;
- _open(sseOpened ? 're-opening' : 'opening', "sse");
-
+ if (!sseOpened) {
+ _open('opening', "sse", _request);
+ }
sseOpened = true;
if (_request.method == 'POST') {
@@ -460,12 +926,21 @@ jQuery.atmosphere = function() {
};
_sse.onmessage = function(message) {
+ if (message.origin != "http://" + window.location.host) {
+ jQuery.atmosphere.log(_request.logLevel, ["Origin was not " + "http://" + window.location.host]);
+ return;
+ }
+
_response.state = 'messageReceived';
_response.status = 200;
var message = message.data;
var skipCallbackInvocation = _trackMessageSize(message, _request, _response);
+ if (jQuery.trim(message).length == 0) {
+ skipCallbackInvocation = true;
+ }
+
if (!skipCallbackInvocation) {
_invokeCallback();
_response.responseBody = '';
@@ -474,27 +949,24 @@ jQuery.atmosphere = function() {
_sse.onerror = function(message) {
- clearTimeout(_request.id)
+ clearTimeout(_request.id);
_response.state = 'closed';
_response.responseBody = "";
- _response.status = 200;
+ _response.status = !sseOpened ? 501 : 200;
_invokeCallback();
+ _sse.close();
- sseOpened = false;
if (_abordingConnection) {
- _abordingConnection = false;
jQuery.atmosphere.log(_request.logLevel, ["SSE closed normally"]);
-
} else if (!sseOpened) {
- jQuery.atmosphere.log(_request.logLevel, ["SSE failed. Downgrading to fallback transport and resending"]);
- _open('opening', _request.fallbackTransport);
- _reconnectWithFallbackTransport();
-
- } else if ((_subscribed) && (_response.transport == 'sse')) {
+ _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending");
+ } else if (_request.reconnect && (_response.transport == 'sse')) {
+ _request.requestCount = _requestCount;
if (_requestCount++ < _request.maxRequest) {
- _request.requestCount = _requestCount;
+ _request.id = setTimeout(function() {
+ _executeSSE(true);
+ }, _request.reconnectInterval);
_response.responseBody = "";
- _executeSSE(true);
} else {
jQuery.atmosphere.log(_request.logLevel, ["SSE reconnect maximum try reached " + _request.requestCount]);
_onError();
@@ -515,12 +987,24 @@ jQuery.atmosphere = function() {
_response.transport = "websocket";
var location = _buildWebSocketUrl(_request.url);
+ var closed = false;
- jQuery.atmosphere.log(_request.logLevel, ["Invoking executeWebSocket"]);
if (_request.logLevel == 'debug') {
+ jQuery.atmosphere.debug("Invoking executeWebSocket");
jQuery.atmosphere.debug("Using URL: " + location);
}
+ if (webSocketOpened) {
+ _open('re-opening', "websocket", _request);
+ }
+
+ if (!_request.reconnect) {
+ if (_websocket != null) {
+ _websocket.close();
+ }
+ return;
+ }
+
_websocket = _getWebSocket(location);
if (_request.connectTimeout > 0) {
@@ -532,6 +1016,11 @@ jQuery.atmosphere = function() {
wasClean : false
};
_websocket.onclose(_message);
+ // Close it anyway
+ try {
+ _websocket.close();
+ } catch (e) {
+ }
}
}, _request.connectTimeout);
}
@@ -541,8 +1030,9 @@ jQuery.atmosphere = function() {
jQuery.atmosphere.debug("Websocket successfully opened");
}
- _subscribed = true;
- _open(webSocketOpened ? 're-opening' : 'opening', "websocket");
+ if (!webSocketOpened) {
+ _open('opening', "websocket", _request);
+ }
webSocketOpened = true;
@@ -574,6 +1064,8 @@ jQuery.atmosphere = function() {
};
_websocket.onclose = function(message) {
+ if (closed) return
+
var reason = message.reason;
if (reason === "") {
switch (message.code) {
@@ -610,21 +1102,19 @@ jQuery.atmosphere = function() {
_response.state = 'closed';
_response.responseBody = "";
- _response.status = 200;
+ _response.status = !webSocketOpened ? 501 : 200;
_invokeCallback();
clearTimeout(_request.id)
+ closed = true;
+
if (_abordingConnection) {
- _abordingConnection = false;
jQuery.atmosphere.log(_request.logLevel, ["Websocket closed normally"]);
-
} else if (!webSocketOpened) {
- jQuery.atmosphere.log(_request.logLevel, ["Websocket failed. Downgrading to Comet and resending"]);
- _open('opening', _request.fallbackTransport);
- _reconnectWithFallbackTransport();
+ _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending");
- } else if ((_subscribed) && (_response.transport == 'websocket')) {
- if (_requestCount++ < _request.maxRequest) {
+ } else if (_request.reconnect && _response.transport == 'websocket') {
+ if (_request.reconnect && _requestCount++ < _request.maxRequest) {
_request.requestCount = _requestCount;
_response.responseBody = "";
_executeWebSocket(true);
@@ -654,23 +1144,35 @@ jQuery.atmosphere = function() {
*/
function _trackMessageSize(message, request, response) {
if (request.trackMessageLength) {
- // The message length is the included within the message
+
+ // If we have found partial message, prepend them.
+ if (response.partialMessage.length != 0) {
+ message = response.partialMessage + message;
+ }
+
+ var messages = [];
+ var messageLength = 0;
var messageStart = message.indexOf(request.messageDelimiter);
+ while (messageStart != -1) {
+ messageLength = message.substring(messageLength, messageStart);
+ message = message.substring(messageStart + request.messageDelimiter.length, message.length);
- var length = response.expectedBodySize;
- if (messageStart != -1) {
- length = message.substring(0, messageStart);
- message = message.substring(messageStart + 1);
- response.expectedBodySize = length;
+ if (message.length == 0 || message.length < messageLength) break;
+
+ messageStart = message.indexOf(request.messageDelimiter);
+ messages.push(message.substring(0, messageLength));
}
- if (messageStart != -1) {
- response.responseBody = message;
+ if (messages.length == 0 || (messageStart != -1 && message.length != 0 && messageLength != message.length)){
+ response.partialMessage = messageLength + request.messageDelimiter + message ;
} else {
- response.responseBody += message;
+ response.partialMessage = "";
}
- if (response.responseBody.length != length) {
+ if (messages.length != 0) {
+ response.responseBody = messages.join(request.messageDelimiter);
+ return false;
+ } else {
return true;
}
} else {
@@ -685,11 +1187,23 @@ jQuery.atmosphere = function() {
*
* @private
*/
- function _reconnectWithFallbackTransport() {
+ function _reconnectWithFallbackTransport(errorMessage) {
+ jQuery.atmosphere.log(_request.logLevel, [errorMessage]);
+
+ if (typeof(_request.onTransportFailure) != 'undefined') {
+ _request.onTransportFailure(errorMessage, _request);
+ } else if (typeof(jQuery.atmosphere.onTransportFailure) != 'undefined') {
+ jQuery.atmosphere.onTransportFailure(errorMessage, _request);
+ }
+
_request.transport = _request.fallbackTransport;
- _request.method = _request.fallbackMethod;
- _response.transport = _request.fallbackTransport;
- _executeRequest();
+ if (_request.reconnect && _request.transport != 'none' || _request.transport == null) {
+ _request.method = _request.fallbackMethod;
+ _response.transport = _request.fallbackTransport;
+ _request.id = setTimeout(function() {
+ _execute();
+ }, _request.reconnectInterval);
+ }
}
/**
@@ -720,7 +1234,7 @@ jQuery.atmosphere = function() {
}
url += (url.indexOf('?') != -1) ? '&' : '?';
- url += "X-Atmosphere-tracking-id=" + _uuid;
+ url += "X-Atmosphere-tracking-id=" + rq.uuid;
url += "&X-Atmosphere-Framework=" + jQuery.atmosphere.version;
url += "&X-Atmosphere-Transport=" + rq.transport;
@@ -739,8 +1253,8 @@ jQuery.atmosphere = function() {
}
jQuery.each(rq.headers, function(name, value) {
- var h = jQuery.isFunction(value) ? value.call(this, ajaxRequest, request, create) : value;
- if (h) {
+ var h = jQuery.isFunction(value) ? value.call(this, rq, request, _response) : value;
+ if (h != null) {
url += "&" + encodeURIComponent(name) + "=" + encodeURIComponent(h);
}
});
@@ -759,18 +1273,19 @@ jQuery.atmosphere = function() {
function _buildAjaxRequest() {
var ajaxRequest;
if (jQuery.browser.msie) {
- var activexmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
- for (var i = 0; i < activexmodes.length; i++) {
- try {
- ajaxRequest = new ActiveXObject(activexmodes[i]);
- } catch(e) {
- }
- }
-
- } else if (window.XMLHttpRequest) {
- ajaxRequest = new XMLHttpRequest();
+ if (typeof XMLHttpRequest == "undefined")
+ XMLHttpRequest = function () {
+ try { return new ActiveXObject("Msxml2.XMLHTTP.6.0"); }
+ catch (e) {}
+ try { return new ActiveXObject("Msxml2.XMLHTTP.3.0"); }
+ catch (e) {}
+ try { return new ActiveXObject("Microsoft.XMLHTTP"); }
+ catch (e) {}
+ //Microsoft.XMLHTTP points to Msxml2.XMLHTTP and is redundant
+ throw new Error("This browser does not support XMLHttpRequest.");
+ };
}
- return ajaxRequest;
+ return new XMLHttpRequest();
}
/**
@@ -793,6 +1308,11 @@ jQuery.atmosphere = function() {
return;
}
+ if (rq.transport == 'ajax') {
+ _ajax(request);
+ return;
+ }
+
if ((rq.transport == 'streaming') && (jQuery.browser.msie)) {
rq.enableXDR && window.XDomainRequest ? _ieXDR(rq) : _ieStreaming(rq);
return;
@@ -803,7 +1323,7 @@ jQuery.atmosphere = function() {
return;
}
- if (rq.requestCount++ < rq.maxRequest) {
+ if (rq.reconnect && rq.requestCount++ < rq.maxRequest) {
var ajaxRequest = _buildAjaxRequest();
_doRequest(ajaxRequest, rq, true);
@@ -815,20 +1335,21 @@ jQuery.atmosphere = function() {
_response.transport = rq.transport;
}
- var error = false;
if (!jQuery.browser.msie) {
ajaxRequest.onerror = function() {
- error = true;
try {
_response.status = XMLHttpRequest.status;
} catch(e) {
- _response.status = 404;
+ _response.status = 500;
+ }
+
+ if (!_response.status) {
+ _response.status = 500;
}
_response.state = "error";
_invokeCallback();
- ajaxRequest.abort();
- _activeRequest = null;
+ _reconnect(ajaxRequest, rq, true);
};
}
@@ -863,7 +1384,6 @@ jQuery.atmosphere = function() {
update = true;
clearTimeout(rq.id);
}
-
} else if (!jQuery.browser.msie && ajaxRequest.readyState == 3 && ajaxRequest.status == 200 && rq.transport != 'long-polling') {
update = true;
} else {
@@ -871,29 +1391,41 @@ jQuery.atmosphere = function() {
}
if (update) {
+ var responseText = ajaxRequest.responseText;
- var tempDate = ajaxRequest.getResponseHeader('X-Cache-Date');
- if (tempDate != null || tempDate != undefined) {
- _request.lastTimestamp = tempDate.split(" ").pop();
+ // MSIE status can be higher than 1000, Chrome can be 0
+ if (ajaxRequest.status >= 500 || ajaxRequest.status == 0) {
+ _onError();
+ return;
}
- var responseText = ajaxRequest.responseText;
- this.previousLastIndex = rq.lastIndex;
+ _readHeaders(ajaxRequest, _request);
+
if (rq.transport == 'streaming') {
var text = responseText.substring(rq.lastIndex, responseText.length);
_response.isJunkEnded = true;
- if (rq.lastIndex == 0 && text.indexOf("") == -1)) {
+ return;
+ }
+ _response.junkFull = true;
+
+ //if it's the start and we see the junk start
+ //fix for reconnecting on chrome - junk is comming in parts
+ if (rq.lastIndex == 0 && text.indexOf("") != -1) {
_response.isJunkEnded = false;
}
if (!_response.isJunkEnded) {
var endOfJunk = "";
- var endOfJunkLenght = endOfJunk.length;
- var junkEnd = text.indexOf(endOfJunk) + endOfJunkLenght;
+ var endOfJunkLength = endOfJunk.length;
+ var junkEnd = text.indexOf(endOfJunk) + endOfJunkLength;
- if (junkEnd > endOfJunkLenght && junkEnd != text.length) {
+ if (junkEnd > endOfJunkLength && junkEnd != text.length) {
_response.responseBody = text.substring(junkEnd);
+ //fix cached messages
+ skipCallbackInvocation = _trackMessageSize(_response.responseBody, rq, _response);
} else {
skipCallbackInvocation = true;
}
@@ -910,15 +1442,7 @@ jQuery.atmosphere = function() {
_response.status = ajaxRequest.status;
_response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders());
- // HOTFIX for firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=608735
- if (_request.headers) {
- jQuery.each(_request.headers, function(name) {
- var v = ajaxRequest.getResponseHeader(name);
- if (v) {
- _response.headers[name] = v;
- }
- });
- }
+ _readHeaders(ajaxRequest, _request);
}
catch(e) {
_response.status = 404;
@@ -949,15 +1473,7 @@ jQuery.atmosphere = function() {
_response.status = ajaxRequest.status;
_response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders());
- // HOTFIX for firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=608735
- if (_request.headers) {
- jQuery.each(_request.headers, function(name) {
- var v = ajaxRequest.getResponseHeader(name);
- if (v) {
- _response.headers[name] = v;
- }
- });
- }
+ _readHeaders(ajaxRequest, rq);
} catch(e) {
_response.status = 404;
}
@@ -986,8 +1502,6 @@ jQuery.atmosphere = function() {
// Close and reopen connection on large data received
ajaxRequest.abort();
_doRequest(ajaxRequest, rq, true);
- } else {
- _open('re-opening', rq.transport);
}
}
};
@@ -998,13 +1512,16 @@ jQuery.atmosphere = function() {
if (_subscribed) {
ajaxRequest.abort();
_subscribe(rq);
+ _execute();
}
}, rq.timeout);
}
_subscribed = true;
} else {
- jQuery.atmosphere.log(rq.logLevel, ["Max re-connection reached."]);
+ if (rq.logLevel == 'debug') {
+ jQuery.atmosphere.log(rq.logLevel, ["Max re-connection reached."]);
+ }
_onError();
}
}
@@ -1017,7 +1534,8 @@ jQuery.atmosphere = function() {
*/
function _doRequest(ajaxRequest, request, create) {
// Prevent Android to cache request
- var url = jQuery.atmosphere.prepareURL(request.url);
+ var url = _attachHeaders(request);
+ url = jQuery.atmosphere.prepareURL(url);
if (create) {
ajaxRequest.open(request.method, url, true);
@@ -1037,26 +1555,28 @@ jQuery.atmosphere = function() {
}
}
- ajaxRequest.setRequestHeader("X-Atmosphere-Framework", jQuery.atmosphere.version);
- ajaxRequest.setRequestHeader("X-Atmosphere-Transport", request.transport);
- if (request.lastTimestamp != undefined) {
- ajaxRequest.setRequestHeader("X-Cache-Date", request.lastTimestamp);
- } else {
- ajaxRequest.setRequestHeader("X-Cache-Date", 0);
- }
+ if (!_request.dropAtmosphereHeaders) {
+ ajaxRequest.setRequestHeader("X-Atmosphere-Framework", jQuery.atmosphere.version);
+ ajaxRequest.setRequestHeader("X-Atmosphere-Transport", request.transport);
+ if (request.lastTimestamp != undefined) {
+ ajaxRequest.setRequestHeader("X-Cache-Date", request.lastTimestamp);
+ } else {
+ ajaxRequest.setRequestHeader("X-Cache-Date", 0);
+ }
- if (request.trackMessageLength) {
- ajaxRequest.setRequestHeader("X-Atmosphere-TrackMessageSize", "true")
- }
+ if (request.trackMessageLength) {
+ ajaxRequest.setRequestHeader("X-Atmosphere-TrackMessageSize", "true")
+ }
- if (request.contentType != '') {
- ajaxRequest.setRequestHeader("Content-Type", request.contentType);
+ if (request.contentType != '') {
+ ajaxRequest.setRequestHeader("Content-Type", request.contentType);
+ }
+ ajaxRequest.setRequestHeader("X-Atmosphere-tracking-id", request.uuid);
}
- ajaxRequest.setRequestHeader("X-Atmosphere-tracking-id", _uuid);
jQuery.each(request.headers, function(name, value) {
var h = jQuery.isFunction(value) ? value.call(this, ajaxRequest, request, create, _response) : value;
- if (h) {
+ if (h != null) {
ajaxRequest.setRequestHeader(name, h);
}
});
@@ -1064,7 +1584,12 @@ jQuery.atmosphere = function() {
function _reconnect(ajaxRequest, request, force) {
if (force || (request.suspend && ajaxRequest.status == 200 && request.transport != 'streaming' && _subscribed)) {
- _executeRequest();
+ if (request.reconnect) {
+ _open('re-opening', request.transport, request);
+ request.id = setTimeout(function() {
+ _executeRequest();
+ }, request.reconnectInterval);
+ }
}
}
@@ -1081,10 +1606,8 @@ jQuery.atmosphere = function() {
rq = request;
}
- var lastMessage = "";
var transport = rq.transport;
var lastIndex = 0;
-
var xdrCallback = function (xdr) {
var responseBody = xdr.responseText;
var isJunkEnded = false;
@@ -1096,12 +1619,12 @@ jQuery.atmosphere = function() {
if (isJunkEnded) {
var endOfJunk = "";
var endOfJunkLenght = endOfJunk.length;
- var junkEnd = responseBody.indexOf(endOfJunk) + endOfJunkLenght;
-
- responseBody = responseBody.substring(junkEnd + lastIndex);
- lastIndex += responseBody.length;
+ var junkEnd = responseBody.indexOf(endOfJunk);
+ if (junkEnd !== -1) {
+ responseBody = responseBody.substring(junkEnd + endOfJunkLenght + lastIndex);
+ lastIndex += responseBody.length;
+ }
}
-
_prepareCallback(responseBody, "messageReceived", 200, transport);
};
@@ -1109,42 +1632,52 @@ jQuery.atmosphere = function() {
var rewriteURL = rq.rewriteURL || function(url) {
// Maintaining session by rewriting URL
// http://stackoverflow.com/questions/6453779/maintaining-session-by-rewriting-url
- var rewriters = {
- JSESSIONID: function(sid) {
- return url.replace(/;jsessionid=[^\?]*|(\?)|$/, ";jsessionid=" + sid + "$1");
- },
- PHPSESSID: function(sid) {
- return url.replace(/\?PHPSESSID=[^&]*&?|\?|$/, "?PHPSESSID=" + sid + "&").replace(/&$/, "");
- }
- };
+ var match = /(?:^|;\s*)(JSESSIONID|PHPSESSID)=([^;]*)/.exec(document.cookie);
- for (var name in rewriters) {
- // Finds session id from cookie
- var matcher = new RegExp("(?:^|;\\s*)" + encodeURIComponent(name) + "=([^;]*)").exec(document.cookie);
- if (matcher) {
- return rewriters[name](matcher[1]);
- }
+ switch (match && match[1]) {
+ case "JSESSIONID":
+ return url.replace(/;jsessionid=[^\?]*|(\?)|$/, ";jsessionid=" + match[2] + "$1");
+ case "PHPSESSID":
+ return url.replace(/\?PHPSESSID=[^&]*&?|\?|$/, "?PHPSESSID=" + match[2] + "&").replace(/&$/, "");
}
-
return url;
};
// Handles open and message event
xdr.onprogress = function() {
xdrCallback(xdr);
+ rq.lastMessage = xdr.responseText;
};
// Handles error event
xdr.onerror = function() {
- _prepareCallback(xdr.responseText, "error", 500, transport);
+ // If the server doesn't send anything back to XDR will fail with polling
+ if (rq.transport != 'polling') {
+ _prepareCallback(xdr.responseText, "error", 500, transport);
+ }
};
// Handles close event
- xdr.onload = function() {
- if (lastMessage != xdr.responseText) {
+ xdr.onload = function () {
+ // XDomain loop forever on itself without this.
+ // TODO: Clearly I need to come with something better than that solution
+ if (rq.lastMessage == xdr.responseText) return;
+
+ if (rq.executeCallbackBeforeReconnect) {
xdrCallback(xdr);
}
- if (rq.transport == "long-polling") {
- _executeRequest();
+
+ // window.XDomainRequest() cannot read response headers, hence X-Atmosphere-Tracking-ID
+ // and X-Cache-Date won't work.
+ // _readHeaders()
+
+ if (rq.transport == "long-polling" && rq.requestCount++ < rq.maxRequest) {
+ xdr.status = 200;
+ _reconnect(xdr, rq, false);
}
+
+ if (!rq.executeCallbackBeforeReconnect) {
+ xdrCallback(xdr);
+ }
+ rq.lastMessage = xdr.responseText;
};
return {
@@ -1154,7 +1687,7 @@ jQuery.atmosphere = function() {
}
var url = _attachHeaders(rq);
if (rq.method == 'POST') {
- url += "&X-Atmosphere-Post-Body=" + rq.data;
+ url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data);
}
xdr.open(rq.method, rewriteURL(url));
xdr.send();
@@ -1204,7 +1737,7 @@ jQuery.atmosphere = function() {
url = _attachHeaders(rq);
if (rq.data != '') {
- url += "&X-Atmosphere-Post-Body=" + rq.data;
+ url += "&X-Atmosphere-Post-Body=" + encodeURIComponent(rq.data);
}
// Finally attach a timestamp to prevent Android and IE caching.
@@ -1217,85 +1750,90 @@ jQuery.atmosphere = function() {
var cdoc = iframe.contentDocument || iframe.contentWindow.document;
stop = jQuery.atmosphere.iterate(function() {
- if (!cdoc.firstChild) {
- return;
- }
-
- // Detects connection failure
- if (cdoc.readyState === "complete") {
- try {
- jQuery.noop(cdoc.fileSize);
- } catch(e) {
- _prepareCallback("Connection Failure", "error", 500, rq.transport);
- return false;
- }
- }
-
- var res = cdoc.body ? cdoc.body.lastChild : cdoc;
- var readResponse = function() {
- // Clones the element not to disturb the original one
- var clone = res.cloneNode(true);
-
- // If the last character is a carriage return or a line feed, IE ignores it in the innerText property
- // therefore, we add another non-newline character to preserve it
- clone.appendChild(cdoc.createTextNode("."));
-
- var text = clone.innerText;
- var isJunkEnded = true;
-
- if (text.indexOf("";
- var endOfJunkLenght = endOfJunk.length;
- var junkEnd = text.indexOf(endOfJunk) + endOfJunkLenght;
-
- text = text.substring(junkEnd);
- }
- return text.substring(0, text.length - 1);
- };
-
- //To support text/html content type
- if (!jQuery.nodeName(res, "pre")) {
- // Injects a plaintext element which renders text without interpreting the HTML and cannot be stopped
- // it is deprecated in HTML5, but still works
- var head = cdoc.head || cdoc.getElementsByTagName("head")[0] || cdoc.documentElement || cdoc;
- var script = cdoc.createElement("script");
-
- script.text = "document.write('')";
-
- head.insertBefore(script, head.firstChild);
- head.removeChild(script);
-
- // The plaintext element will be the response container
- res = cdoc.body.lastChild;
- }
-
- // Handles open event
- _prepareCallback(readResponse(), "opening", 200, rq.transport);
-
- // Handles message and close event
- stop = jQuery.atmosphere.iterate(function() {
- var text = readResponse();
- if (text.length > rq.lastIndex) {
- _response.status = 200;
- _prepareCallback(text, "messageReceived", 200, rq.transport);
-
- // Empties response every time that it is handled
- res.innerText = "";
- rq.lastIndex = 0;
+ try {
+ if (!cdoc.firstChild) {
+ return;
}
+ // Detects connection failure
if (cdoc.readyState === "complete") {
- _prepareCallback("", "re-opening", 200, rq.transport);
- _ieStreaming(rq);
- return false;
+ try {
+ jQuery.noop(cdoc.fileSize);
+ } catch(e) {
+ _prepareCallback("Connection Failure", "error", 500, rq.transport);
+ return false;
+ }
}
- }, null);
- return false;
+ var res = cdoc.body ? cdoc.body.lastChild : cdoc;
+ var readResponse = function() {
+ // Clones the element not to disturb the original one
+ var clone = res.cloneNode(true);
+
+ // If the last character is a carriage return or a line feed, IE ignores it in the innerText property
+ // therefore, we add another non-newline character to preserve it
+ clone.appendChild(cdoc.createTextNode("."));
+
+ var text = clone.innerText;
+ var isJunkEnded = true;
+
+ if (text.indexOf("";
+ var endOfJunkLength = endOfJunk.length;
+ var junkEnd = text.indexOf(endOfJunk) + endOfJunkLength;
+
+ text = text.substring(junkEnd);
+ }
+ return text.substring(0, text.length - 1);
+ };
+
+ //To support text/html content type
+ if (!jQuery.nodeName(res, "pre")) {
+ // Injects a plaintext element which renders text without interpreting the HTML and cannot be stopped
+ // it is deprecated in HTML5, but still works
+ var head = cdoc.head || cdoc.getElementsByTagName("head")[0] || cdoc.documentElement || cdoc;
+ var script = cdoc.createElement("script");
+
+ script.text = "document.write('')";
+
+ head.insertBefore(script, head.firstChild);
+ head.removeChild(script);
+
+ // The plaintext element will be the response container
+ res = cdoc.body.lastChild;
+ }
+
+ // Handles open event
+ _prepareCallback(readResponse(), "opening", 200, rq.transport);
+
+ // Handles message and close event
+ stop = jQuery.atmosphere.iterate(function() {
+ var text = readResponse();
+ if (text.length > rq.lastIndex) {
+ _response.status = 200;
+
+ // Empties response every time that it is handled
+ res.innerText = "";
+ _prepareCallback(text, "messageReceived", 200, rq.transport);
+
+ rq.lastIndex = 0;
+ }
+
+ if (cdoc.readyState === "complete") {
+ _prepareCallback("", "re-opening", 200, rq.transport);
+ _ieStreaming(rq);
+ return false;
+ }
+ }, null);
+
+ return false;
+ } catch (err) {
+ jQuery.atmosphere.error(err);
+ }
});
},
@@ -1319,7 +1857,10 @@ jQuery.atmosphere = function() {
* @private
*/
function _push(message) {
- if (_activeRequest != null || _sse != null) {
+
+ if (_localStorageService != null) {
+ _pushLocal(message);
+ } else if (_activeRequest != null || _sse != null) {
_pushAjaxMessage(message);
} else if (_ieStream != null) {
_pushIE(message);
@@ -1330,6 +1871,25 @@ jQuery.atmosphere = function() {
}
}
+ function _pushLocal(message) {
+ _localStorageService.send(message);
+ }
+
+ function _intraPush(message) {
+ // IE 9 will crash if not.
+ if (message.length == 0) return;
+
+ try {
+ if (_localStorageService) {
+ _localStorageService.localSend(message);
+ } else {
+ _storageService.signal("localMessage", jQuery.stringifyJSON({id: guid , event: message}));
+ }
+ } catch (err) {
+ jQuery.atmosphere.error(err);
+ }
+ }
+
/**
* Send a message using currently opened ajax request (using
* http-streaming or long-polling).
@@ -1352,7 +1912,14 @@ jQuery.atmosphere = function() {
* @private
*/
function _pushIE(message) {
- _pushAjaxMessage(message);
+ if (_request.enableXDR && jQuery.atmosphere.checkCORSSupport()) {
+ var rq = _getPushRequest(message);
+ // Do not reconnect since we are pushing.
+ rq.reconnect = false;
+ _jsonp(rq);
+ } else {
+ _pushAjaxMessage(message);
+ }
}
/**
@@ -1391,21 +1958,22 @@ jQuery.atmosphere = function() {
url: _request.url,
contentType : _request.contentType,
headers: {},
- cache: true,
- async: true,
- ifModified: false,
+ reconnect : true,
callback: null,
- dataType: '',
data : msg,
suspend : false,
maxRequest : 60,
logLevel : 'info',
requestCount : 0,
- transport: 'polling'
+ withCredentials : _request.withCredentials,
+ transport: 'polling',
+ attachHeadersAsQueryString: true,
+ enableXDR: _request.enableXDR,
+ uuid : _request.uuid
};
if (typeof(message) == 'object') {
- rq = $.extend(rq, message);
+ rq = jQuery.extend(rq, message);
}
return rq;
@@ -1434,17 +2002,26 @@ jQuery.atmosphere = function() {
_websocket.send(data);
} catch (e) {
- jQuery.atmosphere.log(_request.logLevel, ["Websocket failed. Downgrading to Comet and resending " + data]);
-
_websocket.onclose = function(message) {
};
_websocket.close();
- _reconnectWithFallbackTransport();
+ _reconnectWithFallbackTransport("Websocket failed. Downgrading to Comet and resending " + data);
_pushAjaxMessage(message);
}
}
+ function _localMessage(message) {
+ var m = jQuery.parseJSON(message);
+ if (m.id != guid) {
+ if (typeof(_request.onLocalMessage) != 'undefined') {
+ _request.onLocalMessage(m.event);
+ } else if (typeof(jQuery.atmosphere.onLocalMessage) != 'undefined') {
+ jQuery.atmosphere.onLocalMessage(m.event);
+ }
+ }
+ }
+
function _prepareCallback(messageBody, state, errorCode, transport) {
if (state == "messageReceived") {
@@ -1453,16 +2030,38 @@ jQuery.atmosphere = function() {
_response.transport = transport;
_response.status = errorCode;
-
- // If not -1, we have buffered the message.
- if (_response.expectedBodySize == -1) {
- _response.responseBody = messageBody;
- }
_response.state = state;
_invokeCallback();
}
+ function _readHeaders(xdr, request) {
+ if (!request.readResponsesHeaders) return;
+
+ try {
+ var tempDate = xdr.getResponseHeader('X-Cache-Date');
+ if (tempDate && tempDate != null && tempDate.length > 0 ) {
+ request.lastTimestamp = tempDate.split(" ").pop();
+ }
+
+ var tempUUID = xdr.getResponseHeader('X-Atmosphere-tracking-id');
+ if (tempUUID && tempUUID != null) {
+ request.uuid = tempUUID.split(" ").pop();
+ }
+
+ // HOTFIX for firefox bug: https://bugzilla.mozilla.org/show_bug.cgi?id=608735
+ if (request.headers) {
+ jQuery.each(_request.headers, function (name) {
+ var v = xdr.getResponseHeader(name);
+ if (v) {
+ _response.headers[name] = v;
+ }
+ });
+ }
+ } catch (e) {
+ }
+ }
+
function _invokeFunction(response) {
_f(response, _request);
// Global
@@ -1486,6 +2085,7 @@ jQuery.atmosphere = function() {
case "re-opening" :
if (typeof(f.onReconnect) != 'undefined') f.onReconnect(_request, response);
break;
+ case "unsubscribe" :
case "closed" :
if (typeof(f.onClose) != 'undefined') f.onClose(response);
break;
@@ -1502,29 +2102,54 @@ jQuery.atmosphere = function() {
func(_response);
};
- _invokeFunction(_response);
+ if (_localStorageService == null && _localSocketF != null) {
+ _localSocketF(_response.responseBody);
+ }
- // Invoke global callbacks
- if (jQuery.atmosphere.callbacks.length > 0) {
- jQuery.atmosphere.debug("Invoking " + jQuery.atmosphere.callbacks.length + " global callbacks: " + _response.state);
- try {
- jQuery.each(jQuery.atmosphere.callbacks, call);
- } catch (e) {
- jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]);
+ var messages = typeof(_response.responseBody) == 'string' ? _response.responseBody.split(_request.messageDelimiter) : new Array(_response.responseBody);
+ for (var i = 0; i < messages.length; i++) {
+
+ if (messages.length > 1 && messages[i].length == 0) {
+ continue;
+ }
+ _response.responseBody = jQuery.trim(messages[i]);
+
+ // Ugly see issue 400.
+ if (_response.responseBody.length == 0 && _response.transport == 'streaming' && _response.state == "messageReceived") {
+ var ua = navigator.userAgent.toLowerCase();
+ var isAndroid = ua.indexOf("android") > -1;
+ if (isAndroid) {
+ continue;
+ }
+ }
+
+ _invokeFunction(_response);
+
+ // Invoke global callbacks
+ if (jQuery.atmosphere.callbacks.length > 0) {
+ if (_request.logLevel == 'debug') {
+ jQuery.atmosphere.debug("Invoking " + jQuery.atmosphere.callbacks.length + " global callbacks: " + _response.state);
+ }
+ try {
+ jQuery.each(jQuery.atmosphere.callbacks, call);
+ } catch (e) {
+ jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]);
+ }
+ }
+
+ // Invoke request callback
+ if (typeof(_request.callback) == 'function') {
+ if (_request.logLevel == 'debug') {
+ jQuery.atmosphere.debug("Invoking request callbacks");
+ }
+ try {
+ _request.callback(_response);
+ } catch (e) {
+ jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]);
+ }
}
}
- // Invoke request callback
- if (typeof(_request.callback) == 'function') {
- if (_request.logLevel == 'debug') {
- jQuery.atmosphere.debug("Invoking request callbacks");
- }
- try {
- _request.callback(_response);
- } catch (e) {
- jQuery.atmosphere.log(_request.logLevel, ["Callback exception" + e]);
- }
- }
}
/**
@@ -1533,6 +2158,8 @@ jQuery.atmosphere = function() {
* @private
*/
function _close() {
+ _request.reconnect = false;
+ _response.request = _request;
_subscribed = false;
_abordingConnection = true;
_response.state = 'unsubscribe';
@@ -1540,28 +2167,41 @@ jQuery.atmosphere = function() {
_response.status = 408;
_invokeCallback();
+ _clearState();
+
+ // Stop sharing a connection
+ if (_storageService != null) {
+ // Clears trace timer
+ clearInterval(_traceTimer);
+ // Removes the trace
+ document.cookie = encodeURIComponent("atmosphere-" + _request.url) + "=; expires=Thu, 01 Jan 1970 00:00:00 GMT";
+ // The heir is the parent unless unloading
+ _storageService.signal("close", {reason: "", heir: !_abordingConnection ? guid : (_storageService.get("children") || [])[0]});
+ _storageService.close();
+ }
+ if (_localStorageService != null) {
+ _localStorageService.close();
+ }
+ }
+
+ function _clearState() {
if (_ieStream != null) {
_ieStream.close();
_ieStream = null;
- _abordingConnection = false;
}
if (_jqxhr != null) {
_jqxhr.abort();
_jqxhr = null;
- _abordingConnection = false;
}
if (_activeRequest != null) {
_activeRequest.abort();
_activeRequest = null;
- _abordingConnection = false;
}
if (_websocket != null) {
- _closingWebSocket = true;
_websocket.close();
_websocket = null;
}
if (_sse != null) {
- _closingSSE = true;
_sse.close();
_sse = null;
}
@@ -1569,6 +2209,7 @@ jQuery.atmosphere = function() {
this.subscribe = function(options) {
_subscribe(options);
+ _execute();
};
this.execute = function() {
@@ -1589,7 +2230,11 @@ jQuery.atmosphere = function() {
this.push = function(message) {
_push(message);
- }
+ };
+
+ this.pushLocal = function(message) {
+ _intraPush(message);
+ };
this.response = _response;
},
@@ -1606,6 +2251,8 @@ jQuery.atmosphere = function() {
}
var rq = new jQuery.atmosphere.AtmosphereRequest(request);
+ rq.execute();
+
jQuery.atmosphere.requests[jQuery.atmosphere.requests.length] = rq;
return rq;
},
@@ -1668,7 +2315,7 @@ jQuery.atmosphere = function() {
checkCORSSupport : function() {
if (jQuery.browser.msie && !window.XDomainRequest) {
return true;
- } else if (jQuery.browser.opera) {
+ } else if (jQuery.browser.opera && jQuery.browser.version < 12.0) {
return true;
}
@@ -1703,6 +2350,21 @@ jQuery.atmosphere = function() {
return jQuery.param(data, jQuery.ajaxSettings.traditional);
},
+ supportStorage : function() {
+ var storage = window.localStorage;
+ if (storage) {
+ try {
+ storage.setItem("t", "t");
+ storage.removeItem("t");
+ // The storage event of Internet Explorer and Firefox 3 works strangely
+ return window.StorageEvent && !jQuery.browser.msie && !(jQuery.browser.mozilla && jQuery.browser.version.split(".")[0] === "1");
+ } catch (e) {
+ }
+ }
+
+ return false;
+ },
+
iterate : function (fn, interval) {
var timeoutId;
@@ -1725,84 +2387,6 @@ jQuery.atmosphere = function() {
};
},
- parseUri : function(baseUrl, uri) {
- var protocol = window.location.protocol;
- var host = window.location.host;
- var path = window.location.pathname;
- var parameters = {};
- var anchor = '';
- var pos;
-
- if ((pos = uri.search(/\:/)) >= 0) {
- protocol = uri.substring(0, pos + 1);
- uri = uri.substring(pos + 1);
- }
-
- if ((pos = uri.search(/\#/)) >= 0) {
- anchor = uri.substring(pos + 1);
- uri = uri.substring(0, pos);
- }
-
- if ((pos = uri.search(/\?/)) >= 0) {
- var paramsStr = uri.substring(pos + 1) + '&;';
- uri = uri.substring(0, pos);
- while ((pos = paramsStr.search(/\&/)) >= 0) {
- var paramStr = paramsStr.substring(0, pos);
- paramsStr = paramsStr.substring(pos + 1);
-
- if (paramStr.length) {
- var equPos = paramStr.search(/\=/);
- if (equPos < 0) {
- parameters[paramStr] = '';
- } else {
- parameters[paramStr.substring(0, equPos)] =
- decodeURIComponent(paramStr.substring(equPos + 1));
- }
- }
- }
- }
-
- if (uri.search(/\/\//) == 0) {
- uri = uri.substring(2);
- if ((pos = uri.search(/\//)) >= 0) {
- host = uri.substring(0, pos);
- path = uri.substring(pos);
- } else {
- host = uri;
- path = '/';
- }
- } else if (uri.search(/\//) == 0) {
- path = uri;
- }
-
- else // relative to directory
- {
- var p = path.lastIndexOf('/');
- if (p < 0) {
- path = '/';
- } else if (p < path.length - 1) {
- path = path.substring(0, p + 1);
- }
-
- while (uri.search(/\.\.\//) == 0) {
- p = path.lastIndexOf('/', path.lastIndexOf('/') - 1);
- if (p >= 0) {
- path = path.substring(0, p + 1);
- }
- uri = uri.substring(3);
- }
- path = path + uri;
- }
-
- var formattedUri = protocol + '//' + host + path;
- var div = '?';
- for (var key in parameters) {
- formattedUri += div + key + '=' + encodeURIComponent(parameters[key]);
- div = '&';
- }
- return formattedUri;
- },
-
log: function (level, args) {
if (window.console) {
var logger = window.console[level];
@@ -1839,7 +2423,7 @@ jQuery.atmosphere = function() {
* http://www.apache.org/licenses/LICENSE-2.0
*/
// This plugin is heavily based on Douglas Crockford's reference implementation
-(function($) {
+(function(jQuery) {
var escapable = /[\\\"\x00-\x1f\x7f-\x9f\u00ad\u0600-\u0604\u070f\u17b4\u17b5\u200c-\u200f\u2028-\u202f\u2060-\u206f\ufeff\ufff0-\uffff]/g, meta = {
'\b' : '\\b',
@@ -1910,7 +2494,7 @@ jQuery.atmosphere = function() {
}
}
- $.stringifyJSON = function(value) {
+ jQuery.stringifyJSON = function(value) {
if (window.JSON && window.JSON.stringify) {
return window.JSON.stringify(value);
}
diff --git a/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/serverpush.js b/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/serverpush.js
index 27e1015328..2efeaaa2bc 100644
--- a/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/serverpush.js
+++ b/org.adempiere.ui.zk/WEB-INF/src/web/js/jawwa/atmosphere/serverpush.js
@@ -24,9 +24,8 @@
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);
+ if (this.failures < 100) {
+ setTimeout(this.proxy(this._send), this.delay);
} else {
this.stop();
}
@@ -34,45 +33,36 @@
_send: function() {
if (!this.active)
return;
-
+
var me = this;
- var socket = $.atmosphere;
- var request = {
- url: zk.ajaxURI("/comet", {
- au: true
- }),
- logLevel : 'debug',
- transport : 'long-polling',
- fallbackTransport: 'streaming',
- method: "GET",
- cache: false,
- async: true,
- timeout: me.timeout,
- onError: function(response) {
- if (typeof console == "object") {
- console.error(response);
- }
- me.failures += 1;
- if (me.failures < 10) {
- if (response.transport == 'long-polling') {
- me._schedule();
- }
- } else {
- me.stop();
- }
- },
- onMessage: function(response) {
- zAu.cmd0.echo(me.desktop);
- me.failures = 0;
- if (response.transport == 'long-polling') {
- me._schedule();
- }
- }
- };
-
- request.url = request.url+'?dtid='+me.desktop.id;
- socket.unsubscribe();
- socket.subscribe(request);
+ 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",
+ timeout: me.timeout,
+ error: function(jqxhr, textStatus, errorThrown) {
+ if (typeof console == "object") {
+ console.error(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;
@@ -88,4 +78,4 @@
}
}
});
-})();
+})();
\ No newline at end of file
diff --git a/org.adempiere.ui.zk/build.properties b/org.adempiere.ui.zk/build.properties
index 3ac783b6fc..f63d711a63 100644
--- a/org.adempiere.ui.zk/build.properties
+++ b/org.adempiere.ui.zk/build.properties
@@ -11,10 +11,6 @@ bin.includes = META-INF/,\
timeout.zul,\
plugin.xml,\
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,\
metainfo/,\
WEB-INF/lib/calendar.jar,\
calendar.css,\
@@ -29,7 +25,11 @@ bin.includes = META-INF/,\
OSGI-INF/jrviewerprovider.xml,\
OSGI-INF/resourcefinder.xml,\
OSGI-INF/defaultpaymentformfactory.xml,\
- OSGI-INF/processfactory.xml
+ OSGI-INF/processfactory.xml,\
+ WEB-INF/lib/atmosphere-compat-jbossweb-1.0.4.jar,\
+ WEB-INF/lib/atmosphere-compat-tomcat-1.0.4.jar,\
+ WEB-INF/lib/atmosphere-compat-tomcat7-1.0.4.jar,\
+ WEB-INF/lib/atmosphere-runtime-1.0.4.jar
src.includes = WEB-INF/classes/,\
WEB-INF/tld/,\
WEB-INF/web.xml,\