From f8fb8b7bfdbf4d8c22ca512e937fe812803369c6 Mon Sep 17 00:00:00 2001 From: =?utf8?q?Johannes=20Dahlstr=C3=B6m?= Date: Tue, 21 May 2013 10:18:22 +0300 Subject: [PATCH] Upgrade to Atmosphere 1.0.13 (#11861) Change-Id: Ie9281ff5e9805be89942bf3ca8259740f49e15ab --- .classpath | 1 - WebContent/VAADIN/jquery.atmosphere.js | 553 ++--- push/build.xml | 3 +- push/ivy.xml | 2 +- .../atmosphere/cpr/AtmosphereFramework.java | 1779 ----------------- server/src/com/vaadin/server/Constants.java | 4 +- .../shared/communication/PushConstants.java | 2 +- 7 files changed, 286 insertions(+), 2058 deletions(-) delete mode 100644 push/src/org/atmosphere/cpr/AtmosphereFramework.java diff --git a/.classpath b/.classpath index 381f0d280d..c6230a2c55 100644 --- a/.classpath +++ b/.classpath @@ -11,7 +11,6 @@ - diff --git a/WebContent/VAADIN/jquery.atmosphere.js b/WebContent/VAADIN/jquery.atmosphere.js index e9def6ae95..2b176668c1 100644 --- a/WebContent/VAADIN/jquery.atmosphere.js +++ b/WebContent/VAADIN/jquery.atmosphere.js @@ -49,7 +49,7 @@ jQuery.atmosphere = function() { }; return { - version : "1.0.12", + version : "1.0.13", requests : [], callbacks : [], @@ -138,13 +138,16 @@ jQuery.atmosphere = function() { */ var _response = { status: 200, + reasonPhrase : "OK", responseBody : '', + messages : [], headers : [], state : "messageReceived", transport : "polling", error: null, request : null, partialMessage : "", + errorHandled: false, id : 0 }; @@ -682,19 +685,21 @@ jQuery.atmosphere = function() { _close(); }; - _response.request = request; - var prevState = _response.state; - _response.state = state; - _response.status = 200; - var prevTransport = _response.transport; - _response.transport = transport; + if (_response.error == null) { + _response.request = request; + var prevState = _response.state; + _response.state = state; + _response.status = 200; + var prevTransport = _response.transport; + _response.transport = transport; - var _body = _response.responseBody; - _invokeCallback(); - _response.responseBody = _body; + var _body = _response.responseBody; + _invokeCallback(); + _response.responseBody = _body; - _response.state = prevState; - _response.transport = prevTransport; + _response.state = prevState; + _response.transport = prevTransport; + } } /** @@ -729,41 +734,43 @@ jQuery.atmosphere = function() { type : rq.method, dataType: "jsonp", error : function(jqXHR, textStatus, errorThrown) { - if (jqXHR.status < 300) { + _response.error = true; + if (jqXHR.status < 300 && rq.reconnect && _requestCount++ < rq.maxReconnectOnClose) { _reconnect(_jqxhr, rq); } else { - _prepareCallback(textStatus, "error", jqXHR.status, rq.transport); + _onError(jqXHR.status, errorThrown); } }, jsonp : "jsonpTransport", success: function(json) { + if (rq.reconnect) { + if (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest) { + _readHeaders(_jqxhr, rq); - if (rq.reconnect && (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) { - _readHeaders(_jqxhr, rq); - - if (!rq.executeCallbackBeforeReconnect) { - _reconnect(_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 + var msg = json.message; + if (msg != null && typeof msg != 'string') { + try { + msg = jQuery.stringifyJSON(msg); + } catch (err) { + // The message was partial + } } - } - if (_handleProtocol(rq, msg)) { - _prepareCallback(msg, "messageReceived", 200, rq.transport); - } + if (_handleProtocol(rq, msg)) { + _prepareCallback(msg, "messageReceived", 200, rq.transport); + } - if (rq.executeCallbackBeforeReconnect) { - _reconnect(_jqxhr, rq); + if (rq.executeCallbackBeforeReconnect) { + _reconnect(_jqxhr, rq); + } + } else { + jQuery.atmosphere.log(_request.logLevel, ["JSONP reconnect maximum try reached " + _request.requestCount]); + _onError(0, "maxRequest reached"); } - } else { - jQuery.atmosphere.log(_request.logLevel, ["JSONP reconnect maximum try reached " + _request.requestCount]); - _onError(); } }, data : rq.data, @@ -802,29 +809,32 @@ jQuery.atmosphere = function() { url : url, type : rq.method, error : function(jqXHR, textStatus, errorThrown) { + _response.error = true; if (jqXHR.status < 300) { _reconnect(_jqxhr, rq); } else { - _prepareCallback(textStatus, "error", jqXHR.status, rq.transport); + _onError(jqXHR.status, errorThrown); } }, success: function(data, textStatus, jqXHR) { - if (rq.reconnect && (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) { - if (!rq.executeCallbackBeforeReconnect) { - _reconnect(_jqxhr, rq); - } + if (rq.reconnect) { + if (rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest) { + if (!rq.executeCallbackBeforeReconnect) { + _reconnect(_jqxhr, rq); + } - if (_handleProtocol(rq, data)) { - _prepareCallback(data, "messageReceived", 200, rq.transport); - } + if (_handleProtocol(rq, data)) { + _prepareCallback(data, "messageReceived", 200, rq.transport); + } - if (rq.executeCallbackBeforeReconnect) { - _reconnect(_jqxhr, rq); + if (rq.executeCallbackBeforeReconnect) { + _reconnect(_jqxhr, rq); + } + } else { + jQuery.atmosphere.log(_request.logLevel, ["AJAX reconnect maximum try reached " + _request.requestCount]); + _onError(0, "maxRequest reached"); } - } else { - jQuery.atmosphere.log(_request.logLevel, ["AJAX reconnect maximum try reached " + _request.requestCount]); - _onError(); } }, beforeSend : function(jqXHR) { @@ -912,7 +922,14 @@ jQuery.atmosphere = function() { } return; } - _sse = new EventSource(location, {withCredentials: _request.withCredentials}); + + try { + _sse = new EventSource(location, {withCredentials: _request.withCredentials}); + } catch (e) { + _onError(0, e); + _reconnectWithFallbackTransport("SSE failed. Downgrading to fallback transport and resending"); + return; + } if (_request.connectTimeout > 0) { _request.id = setTimeout(function() { @@ -944,31 +961,25 @@ jQuery.atmosphere = function() { return; } - if (!_handleProtocol(_request, message.data)) return; + var data = message.data; + + if (!_handleProtocol(_request, data)) 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; - } - + var skipCallbackInvocation = _trackMessageSize(data, _request, _response); if (!skipCallbackInvocation) { _invokeCallback(); _response.responseBody = ''; + _response.messages = []; } }; _sse.onerror = function(message) { clearTimeout(_request.id); - _response.state = 'closed'; - _response.responseBody = ""; - _response.status = !sseOpened ? 501 : 200; - _invokeCallback(); + _invokeClose(sseOpened); _clearState(); if (_abordingConnection) { @@ -981,9 +992,10 @@ jQuery.atmosphere = function() { _executeSSE(true); }, _request.reconnectInterval); _response.responseBody = ""; + _response.messages = []; } else { jQuery.atmosphere.log(_request.logLevel, ["SSE reconnect maximum try reached " + _requestCount]); - _onError(); + _onError(0, "maxReconnectOnClose reached"); } } }; @@ -1025,7 +1037,6 @@ jQuery.atmosphere = function() { } _websocket = _getWebSocket(location); - if (_request.connectTimeout > 0) { _request.id = setTimeout(function() { if (!webSocketOpened) { @@ -1062,6 +1073,7 @@ jQuery.atmosphere = function() { } webSocketOpened = true; + _websocket.webSocketOpened = webSocketOpened; if (_request.method == 'POST') { _response.state = "messageReceived"; @@ -1078,17 +1090,18 @@ jQuery.atmosphere = function() { }, _request.reconnectInterval) }, _request.timeout); - if (!_handleProtocol(_request, message.data)) return; + var data = message.data; + + if (!_handleProtocol(_request, data)) return; _response.state = 'messageReceived'; _response.status = 200; - var message = message.data; - var skipCallbackInvocation = _trackMessageSize(message, _request, _response); - + var skipCallbackInvocation = _trackMessageSize(data, _request, _response); if (!skipCallbackInvocation) { _invokeCallback(); _response.responseBody = ''; + _response.messages = []; } }; @@ -1098,6 +1111,7 @@ jQuery.atmosphere = function() { _websocket.onclose = function(message) { if (closed) return + clearTimeout(_request.id); var reason = message.reason; if (reason === "") { @@ -1133,12 +1147,7 @@ jQuery.atmosphere = function() { jQuery.atmosphere.warn("Websocket closed, reason: " + reason); jQuery.atmosphere.warn("Websocket closed, wasClean: " + message.wasClean); - _response.state = 'closed'; - _response.responseBody = ""; - _response.status = !webSocketOpened ? 501 : 200; - _invokeCallback(); - clearTimeout(_request.id); - + _invokeClose(webSocketOpened); closed = true; if (_abordingConnection) { @@ -1151,12 +1160,13 @@ jQuery.atmosphere = function() { if (_request.reconnect && _requestCount++ < _request.maxReconnectOnClose) { _request.id = setTimeout(function() { _response.responseBody = ""; + _response.messages = []; _executeWebSocket(true); }, _request.reconnectInterval); } else { jQuery.atmosphere.log(_request.logLevel, ["Websocket reconnect maximum try reached " + _requestCount]); jQuery.atmosphere.warn("Websocket error, reason: " + message.reason); - _onError(); + _onError(0, "maxReconnectOnClose reached"); } } }; @@ -1164,22 +1174,25 @@ jQuery.atmosphere = function() { function _handleProtocol(request, message) { // The first messages is always the uuid. - if (request.enableProtocol && request.firstMessage) { + if (jQuery.trim(message) != 0 && request.enableProtocol && request.firstMessage) { request.firstMessage = false; var messages = message.split(request.messageDelimiter); - request.uuid = messages[0]; - request.stime = messages[1]; + var pos = messages.length == 2 ? 0 : 1; + request.uuid = jQuery.trim(messages[pos]); + request.stime = jQuery.trim(messages[pos + 1]); return false; } return true; } - function _onError() { + function _onError(code, reason) { _clearState(); _response.state = 'error'; + _response.reasonPhrase = reason; _response.responseBody = ""; - _response.status = 500; + _response.messages = []; + _response.status = code; _invokeCallback(); } @@ -1203,7 +1216,7 @@ jQuery.atmosphere = function() { var messageLength = 0; var messageStart = message.indexOf(request.messageDelimiter); while (messageStart != -1) { - messageLength = message.substring(messageLength, messageStart); + messageLength = jQuery.trim(message.substring(messageLength, messageStart)); message = message.substring(messageStart + request.messageDelimiter.length, message.length); if (message.length == 0 || message.length < messageLength) break; @@ -1220,8 +1233,11 @@ jQuery.atmosphere = function() { if (messages.length != 0) { response.responseBody = messages.join(request.messageDelimiter); + response.messages = messages; return false; } else { + response.responseBody = ""; + response.messages = []; return true; } } else { @@ -1246,15 +1262,16 @@ jQuery.atmosphere = function() { } _request.transport = _request.fallbackTransport; - var reconnect = _request.reconnect && _requestCount++ < _request.maxReconnectOnClose; - if (reconnect && _request.transport != 'none' || _request.transport == null) { + var reconnectInterval = _request.connectTimeout == -1 ? 0 : _request.connectTimeout; + if (_request.reconnect && _request.transport != 'none' || _request.transport == null) { _request.method = _request.fallbackMethod; _response.transport = _request.fallbackTransport; + _request.fallbackTransport = 'none'; _request.id = setTimeout(function() { _execute(); - }, _request.reconnectInterval); - } else if (!reconnect) { - _onError(); + }, reconnectInterval); + } else { + _onError(500, "Unable to reconnect with fallback transport"); } } @@ -1357,6 +1374,9 @@ jQuery.atmosphere = function() { rq = request; } + rq.lastIndex = 0; + rq.readyState = 0; + // CORS fake using JSONP if ((rq.transport == 'jsonp') || ((rq.enableXDR) && (jQuery.atmosphere.checkCORSSupport()))) { _jsonp(rq); @@ -1380,6 +1400,14 @@ jQuery.atmosphere = function() { } } + var reconnectF = function() { + if (rq.reconnect && _requestCount++ < rq.maxReconnectOnClose) { + _reconnect(ajaxRequest, rq, true); + } else { + _onError(0, "maxReconnectOnClose reached"); + } + }; + if (rq.reconnect && ( rq.maxRequest == -1 || rq.requestCount++ < rq.maxRequest)) { var ajaxRequest = _buildAjaxRequest(); _doRequest(ajaxRequest, rq, true); @@ -1392,162 +1420,129 @@ jQuery.atmosphere = function() { _response.transport = rq.transport; } - if (!jQuery.browser.msie) { - ajaxRequest.onerror = function() { - try { - _response.status = XMLHttpRequest.status; - } catch(e) { - _response.status = 500; - } + ajaxRequest.onabort = function () { + _invokeClose(true); + }; - if (!_response.status) { - _response.status = 500; - } - _clearState(); + ajaxRequest.onerror = function() { + _response.error = true; + try { + _response.status = XMLHttpRequest.status; + } catch(e) { + _response.status = 500; + } - if (rq.reconnect) { - _reconnect(ajaxRequest, rq, true); - } else { - _onError(); - } - }; - } + if (!_response.status) { + _response.status = 500; + } + _clearState(); + if (!_response.errorHandled) { + reconnectF(); + } + }; ajaxRequest.onreadystatechange = function() { if (_abordingConnection) { return; } - + _response.error = null; var skipCallbackInvocation = false; var update = false; - // Remote server disconnected us, reconnect. - if (rq.transport == 'streaming' + + // Opera doesn't call onerror if the server disconnect. + if (jQuery.browser.opera + && rq.transport == 'streaming' && rq.readyState > 2 && ajaxRequest.readyState == 4) { rq.readyState = 0; rq.lastIndex = 0; - _reconnect(ajaxRequest, rq, true); + reconnectF(); return; } rq.readyState = ajaxRequest.readyState; - if (ajaxRequest.readyState == 4) { - if (jQuery.browser.msie) { - update = true; - } else if (rq.transport == 'streaming') { - update = true; - } else if (rq.transport == 'long-polling') { - update = true; - clearTimeout(rq.id); - } - } else if (rq.transport == 'streaming' && jQuery.browser.msie && ajaxRequest.readyState >= 3) { + if (rq.transport == 'streaming' && ajaxRequest.readyState >= 3) { update = true; - } else if (!jQuery.browser.msie && ajaxRequest.readyState == 3 && ajaxRequest.status == 200 && rq.transport != 'long-polling') { + } else if (rq.transport == 'long-polling' && ajaxRequest.readyState === 4) { update = true; - } else { - clearTimeout(rq.id); } + clearTimeout(rq.id); if (update) { + // MSIE 9 and lower status can be higher than 1000, Chrome can be 0 + var status = 0; + if (ajaxRequest.readyState != 0) { + status = ajaxRequest.status > 1000 ? 0 : ajaxRequest.status; + } + + if (status >= 300 || status == 0) { + // Prevent onerror callback to be called + _response.errorHandled = true; + _clearState(); + reconnectF(); + return; + } var responseText = ajaxRequest.responseText; - // MSIE status can be higher than 1000, Chrome can be 0 - if (ajaxRequest.status >= 500 || ajaxRequest.status == 0) { - if (rq.reconnect) { - _reconnect(ajaxRequest, rq, true); - } else { - _onError(); + if (jQuery.trim(responseText.length) == 0 && rq.transport == 'long-polling') { + // For browser that aren't support onabort + if (!ajaxRequest.hasData) { + reconnectF(); + } else { + ajaxRequest.hasData = false; } return; } + ajaxRequest.hasData = true; _readHeaders(ajaxRequest, _request); if (rq.transport == 'streaming') { - var text = responseText.substring(rq.lastIndex, responseText.length); - _response.isJunkEnded = true; - - //fix junk is comming in parts - if (!_response.junkFull && (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 endOfJunkLength = endOfJunk.length; - var junkEnd = text.indexOf(endOfJunk) + endOfJunkLength; - - if (junkEnd > endOfJunkLength && junkEnd != text.length) { - _response.responseBody = text.substring(junkEnd); - rq.lastIndex = responseText.length; - if (!_handleProtocol( _request, _response.responseBody)) { - return; - } - skipCallbackInvocation = _trackMessageSize(_response.responseBody, rq, _response); - } else { - skipCallbackInvocation = true; - } - } else { + if (!jQuery.browser.opera) { var message = responseText.substring(rq.lastIndex, responseText.length); rq.lastIndex = responseText.length; - if (!_handleProtocol( _request, message)) { + if (!_handleProtocol(_request, message)) { return; } skipCallbackInvocation = _trackMessageSize(message, rq, _response); - } - rq.lastIndex = responseText.length; - - if (jQuery.browser.opera) { - jQuery.atmosphere.iterate(function() { - if (ajaxRequest.responseText.length > rq.lastIndex) { + } else { + jQuery.atmosphere.iterate(function () { + if (_response.status != 500 && ajaxRequest.responseText.length > rq.lastIndex) { try { _response.status = ajaxRequest.status; - _response.headers = parseHeaders(ajaxRequest.getAllResponseHeaders()); - - _readHeaders(ajaxRequest, _request); } - catch(e) { + catch (e) { _response.status = 404; } _response.state = "messageReceived"; - _response.responseBody = ajaxRequest.responseText.substring(rq.lastIndex); + + var message = ajaxRequest.responseText.substring(rq.lastIndex); rq.lastIndex = ajaxRequest.responseText.length; + if (_handleProtocol(_request, message)) { + skipCallbackInvocation = _trackMessageSize(message, rq, _response); + if (!skipCallbackInvocation) { + _invokeCallback(); + } - if (!_handleProtocol( _request, _response.responseBody)) { - _reconnect(ajaxRequest, rq, false); - return; - } - _invokeCallback(); - if ((rq.transport == 'streaming') && (ajaxRequest.responseText.length > rq.maxStreamingLength)) { - // Close and reopen connection on large data received - _clearState(); - _doRequest(_buildAjaxRequest(), rq, true); + _verifyStreamingLength(ajaxRequest, rq); } + } else if (_response.status > 400){ + rq.lastIndex = ajaxRequest.responseText.length; + return false; } }, 0); } - - if (skipCallbackInvocation) { - return; - } } else { if (!_handleProtocol( _request, responseText)) { _reconnect(ajaxRequest, rq, false); return; } - _trackMessageSize(responseText, rq, _response); + skipCallbackInvocation = _trackMessageSize(responseText, rq, _response); rq.lastIndex = responseText.length; } @@ -1575,17 +1570,15 @@ jQuery.atmosphere = function() { jQuery.atmosphere.log(rq.logLevel, ["parent.callback no longer supported with 0.8 version and up. Please upgrade"]); } - _invokeCallback(); + if (!skipCallbackInvocation) { + _invokeCallback(); + } if (rq.executeCallbackBeforeReconnect) { _reconnect(ajaxRequest, rq, false); } - if ((rq.transport == 'streaming') && (responseText.length > rq.maxStreamingLength)) { - // Close and reopen connection on large data received - _clearState(); - _doRequest(_buildAjaxRequest(), rq, true); - } + _verifyStreamingLength(ajaxRequest, rq); } }; ajaxRequest.send(rq.data); @@ -1606,7 +1599,7 @@ jQuery.atmosphere = function() { if (rq.logLevel == 'debug') { jQuery.atmosphere.log(rq.logLevel, ["Max re-connection reached."]); } - _onError(); + _onError(0, "maxRequest reached"); } } @@ -1651,13 +1644,13 @@ jQuery.atmosphere = function() { if (request.trackMessageLength) { ajaxRequest.setRequestHeader("X-Atmosphere-TrackMessageSize", "true") } - - if (request.contentType != '') { - ajaxRequest.setRequestHeader("Content-Type", request.contentType); - } ajaxRequest.setRequestHeader("X-Atmosphere-tracking-id", request.uuid); } + if (request.contentType != '') { + ajaxRequest.setRequestHeader("Content-Type", request.contentType); + } + jQuery.each(request.headers, function(name, value) { var h = jQuery.isFunction(value) ? value.call(this, ajaxRequest, request, create, _response) : value; if (h != null) { @@ -1667,17 +1660,26 @@ jQuery.atmosphere = function() { } function _reconnect(ajaxRequest, request, force) { - var reconnect = request.reconnect && _requestCount++ < request.maxReconnectOnClose; + if (force || request.transport != 'streaming') { + if ( request.reconnect || (request.suspend && _subscribed)) { + var status = 0; + if (ajaxRequest.readyState != 0) { + status = ajaxRequest.status > 1000 ? 0 : ajaxRequest.status; + } + _response.status = status == 0 ? 204 : status; + _response.reason = status == 0 ? "Server resumed the connection or down." : "OK"; - if (reconnect && force || (request.suspend && ajaxRequest.status == 200 && request.transport != 'streaming' && _subscribed)) { - if (request.reconnect) { - _open('re-opening', request.transport, request); - request.id = setTimeout(function() { - _executeRequest(); - }, request.reconnectInterval); + var reconnectInterval = (request.connectTimeout == -1) ? 0 : request.connectTimeout; + + // Reconnect immedialtely + if (!force) { + request.id = setTimeout(function () { + _executeRequest(request); + }, reconnectInterval); + } else { + _executeRequest(request); + } } - } else if (!reconnect) { - _onError(); } } @@ -1702,21 +1704,6 @@ jQuery.atmosphere = function() { var lastIndex = 0; var xdrCallback = function (xdr) { var responseBody = xdr.responseText; - var isJunkEnded = false; - - if (responseBody.indexOf(""; - var endOfJunkLenght = endOfJunk.length; - var junkEnd = responseBody.indexOf(endOfJunk); - if (junkEnd !== -1) { - responseBody = responseBody.substring(junkEnd + endOfJunkLenght + lastIndex); - lastIndex += responseBody.length; - } - } if (!_handleProtocol(request, responseBody)) return; @@ -1747,10 +1734,8 @@ jQuery.atmosphere = function() { xdr.onerror = function() { // If the server doesn't send anything back to XDR will fail with polling if (rq.transport != 'polling') { - _prepareCallback(xdr.responseText, "error", 500, transport); + _reconnect(xdr, rq, false); } - - _reconnect(xdr, rq, false); }; // Handles close event @@ -1873,23 +1858,7 @@ jQuery.atmosphere = function() { 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); - } - text = text.substring(0, text.length - 1); - - _handleProtocol(rq, text); return text; }; @@ -1918,11 +1887,14 @@ jQuery.atmosphere = function() { var text = readResponse(); if (text.length > rq.lastIndex) { _response.status = 200; + _response.error = null; - // Empties response every time that it is handled - res.innerText = ""; - _prepareCallback(text, "messageReceived", 200, rq.transport); - + var message = text; + if (message.length != 0 && _handleProtocol(rq, message)) { + // Empties response every time that it is handled + res.innerText = ""; + _prepareCallback(message, "messageReceived", 200, rq.transport); + } rq.lastIndex = 0; } @@ -1938,12 +1910,13 @@ jQuery.atmosphere = function() { return false; } catch (err) { + _response.error = true; if (_requestCount++ < rq.maxReconnectOnClose) { rq.id = setTimeout(function() { _ieStreaming(rq); }, rq.reconnectInterval); } else { - _onError(); + _onError(0, "maxReconnectOnClose reached"); } doc.execCommand("Stop"); doc.close(); @@ -2095,6 +2068,7 @@ jQuery.atmosphere = function() { attachHeadersAsQueryString: true, enableXDR: _request.enableXDR, uuid : _request.uuid, + messageDelimiter : '|', enableProtocol : false, maxReconnectOnClose : _request.maxReconnectOnClose }; @@ -2147,11 +2121,11 @@ jQuery.atmosphere = function() { } function _prepareCallback(messageBody, state, errorCode, transport) { - _response.responseBody = messageBody; + if (state == "messageReceived") { - if (_trackMessageSize(messageBody, _request, _response)) { - return; - } + if (_trackMessageSize(messageBody, _request, _response)) return; + } else { + _response.responseBody = messageBody; } _response.transport = transport; @@ -2225,6 +2199,14 @@ jQuery.atmosphere = function() { } } + function _invokeClose(wasOpen) { + _response.state = 'closed'; + _response.responseBody = ""; + _response.messages = []; + _response.status = !wasOpen ? 501 : 200; + _invokeCallback(); + } + /** * Invoke request callbacks. * @@ -2242,7 +2224,7 @@ jQuery.atmosphere = function() { _request.reconnect = _request.mrequest; var messages = (typeof(_response.responseBody) == 'string' && _request.trackMessageLength) ? - _response.responseBody.split(_request.messageDelimiter) : new Array(_response.responseBody); + (_response.messages.length>0 ? _response.messages : ['']) : new Array(_response.responseBody); for (var i = 0; i < messages.length; i++) { if (messages.length > 1 && messages[i].length == 0) { @@ -2250,13 +2232,8 @@ jQuery.atmosphere = function() { } _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; - } + if (_response.responseBody.length == 0 && _response.state == "messageReceived") { + continue; } _invokeFunction(_response); @@ -2288,17 +2265,53 @@ jQuery.atmosphere = function() { } + /** + * + * @private + */ + function _verifyStreamingLength(ajaxRequest, rq){ + // Wait to be sure we have the full message before closing. + if (_response.partialMessage == "" && + (rq.transport == 'streaming') && + (ajaxRequest.responseText.length > rq.maxStreamingLength)) { + _response.messages = []; + _invokeClose(true); + _disconnect(); + _clearState(); + _reconnect(ajaxRequest, rq, true); + } + } + + /** + * Disconnect + * @private + */ + function _disconnect() { + if (_request.enableProtocol) { + var query = "X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=" + _request.uuid; + var url = _request.url.replace(/([?&])_=[^&]*/, query); + url = url + (url === _request.url ? (/\?/.test(_request.url) ? "&" : "?") + query : ""); + + if (_request.connectTimeout > -1) { + jQuery.ajax({url: url, async: false, timeout: _request.connectTimeout}); + } else { + jQuery.ajax({url: url, async: false}); + } + } + } + /** * Close request. * * @private */ function _close() { - _abordingConnection = true; _request.reconnect = false; + _abordingConnection = true; _response.request = _request; _response.state = 'unsubscribe'; _response.responseBody = ""; + _response.messages = []; _response.status = 408; _invokeCallback(); @@ -2319,7 +2332,9 @@ jQuery.atmosphere = function() { _activeRequest = null; } if (_websocket != null) { - _websocket.close(); + if (_websocket.webSocketOpened) { + _websocket.close(); + } _websocket = null; } if (_sse != null) { @@ -2343,7 +2358,7 @@ jQuery.atmosphere = function() { if (_localStorageService != null) { _localStorageService.close(); } - } + }; this.subscribe = function(options) { _subscribe(options); @@ -2362,6 +2377,10 @@ jQuery.atmosphere = function() { _close(); }; + this.disconnect = function () { + _disconnect(); + }; + this.getUrl = function() { return _request.url; }; @@ -2421,10 +2440,8 @@ jQuery.atmosphere = function() { var requestsClone = [].concat(jQuery.atmosphere.requests); for (var i = 0; i < requestsClone.length; i++) { var rq = requestsClone[i]; + rq.disconnect(); rq.close(); - if (rq.enableProtocol()) { - jQuery.ajax({url: this._closeUrl(rq), async:false}); - } clearTimeout(rq.response.request.id); } } @@ -2432,12 +2449,6 @@ jQuery.atmosphere = function() { jQuery.atmosphere.callbacks = []; }, - _closeUrl : function(rq) { - var query = "X-Atmosphere-Transport=close&X-Atmosphere-tracking-id=" + rq.getUUID(); - var url = rq.getUrl().replace(/([?&])_=[^&]*/, query); - return url + (url === rq.getUrl() ? (/\?/.test(rq.getUrl()) ? "&" : "?") + query : ""); - }, - unsubscribeUrl: function(url) { var idx = -1; if (jQuery.atmosphere.requests.length > 0) { @@ -2446,10 +2457,8 @@ jQuery.atmosphere = function() { // Suppose you can subscribe once to an url if (rq.getUrl() == url) { + rq.disconnect(); rq.close(); - if (rq.enableProtocol()) { - jQuery.ajax({url :this._closeUrl(rq), async:false}); - } clearTimeout(rq.response.request.id); idx = i; break; @@ -2643,8 +2652,8 @@ jQuery.atmosphere = function() { /* * jQuery stringifyJSON * http://github.com/flowersinthesand/jquery-stringifyJSON - * - * Copyright 2011, Donghwan Kim + * + * Copyright 2011, Donghwan Kim * Licensed under the Apache License, Version 2.0 * http://www.apache.org/licenses/LICENSE-2.0 */ @@ -2728,4 +2737,4 @@ jQuery.atmosphere = function() { return str("", {"": value}); }; -}(jQuery)); \ No newline at end of file +}(jQuery)); diff --git a/push/build.xml b/push/build.xml index d06b946dd1..953416cf2d 100644 --- a/push/build.xml +++ b/push/build.xml @@ -13,7 +13,7 @@ - + @@ -62,7 +62,6 @@ - diff --git a/push/ivy.xml b/push/ivy.xml index a5ca79d4a3..4834342f00 100644 --- a/push/ivy.xml +++ b/push/ivy.xml @@ -28,7 +28,7 @@ - diff --git a/push/src/org/atmosphere/cpr/AtmosphereFramework.java b/push/src/org/atmosphere/cpr/AtmosphereFramework.java deleted file mode 100644 index 62f867fc94..0000000000 --- a/push/src/org/atmosphere/cpr/AtmosphereFramework.java +++ /dev/null @@ -1,1779 +0,0 @@ -/* - * Copyright 2013 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 - * - * 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 org.atmosphere.cpr; - -import org.atmosphere.cache.UUIDBroadcasterCache; -import org.atmosphere.config.ApplicationConfiguration; -import org.atmosphere.config.AtmosphereHandlerConfig; -import org.atmosphere.config.AtmosphereHandlerProperty; -import org.atmosphere.config.FrameworkConfiguration; -import org.atmosphere.container.BlockingIOCometSupport; -import org.atmosphere.container.Tomcat7BIOSupportWithWebSocket; -import org.atmosphere.di.InjectorProvider; -import org.atmosphere.di.ServletContextHolder; -import org.atmosphere.di.ServletContextProvider; -import org.atmosphere.handler.AbstractReflectorAtmosphereHandler; -import org.atmosphere.handler.ReflectorServletProcessor; -import org.atmosphere.interceptor.AndroidAtmosphereInterceptor; -import org.atmosphere.interceptor.JSONPAtmosphereInterceptor; -import org.atmosphere.interceptor.JavaScriptProtocol; -import org.atmosphere.interceptor.OnDisconnectInterceptor; -import org.atmosphere.interceptor.SSEAtmosphereInterceptor; -import org.atmosphere.util.AtmosphereConfigReader; -import org.atmosphere.util.IntrospectionUtils; -import org.atmosphere.util.Version; -import org.atmosphere.websocket.DefaultWebSocketProcessor; -import org.atmosphere.websocket.WebSocket; -import org.atmosphere.websocket.WebSocketProtocol; -import org.atmosphere.websocket.protocol.SimpleHttpProtocol; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; - -import javax.servlet.Servlet; -import javax.servlet.ServletConfig; -import javax.servlet.ServletContext; -import javax.servlet.ServletException; -import java.io.File; -import java.io.FilenameFilter; -import java.io.IOException; -import java.io.InputStream; -import java.lang.reflect.InvocationHandler; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.lang.reflect.Proxy; -import java.net.MalformedURLException; -import java.net.URISyntaxException; -import java.net.URL; -import java.net.URLClassLoader; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.UUID; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; - -import static org.atmosphere.cpr.ApplicationConfig.ALLOW_QUERYSTRING_AS_REQUEST; -import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER; -import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER_MAPPING; -import static org.atmosphere.cpr.ApplicationConfig.ATMOSPHERE_HANDLER_PATH; -import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_CACHE; -import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_CLASS; -import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_FACTORY; -import static org.atmosphere.cpr.ApplicationConfig.BROADCASTER_LIFECYCLE_POLICY; -import static org.atmosphere.cpr.ApplicationConfig.BROADCAST_FILTER_CLASSES; -import static org.atmosphere.cpr.ApplicationConfig.DISABLE_ONSTATE_EVENT; -import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_ATMOSPHERE_XML; -import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_BLOCKING_COMETSUPPORT; -import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_COMET_SUPPORT; -import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_NATIVE_COMETSUPPORT; -import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_SERVLET_MAPPING; -import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_SESSION_SUPPORT; -import static org.atmosphere.cpr.ApplicationConfig.PROPERTY_USE_STREAM; -import static org.atmosphere.cpr.ApplicationConfig.RESUME_AND_KEEPALIVE; -import static org.atmosphere.cpr.ApplicationConfig.SUSPENDED_ATMOSPHERE_RESOURCE_UUID; -import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_PROCESSOR; -import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_PROTOCOL; -import static org.atmosphere.cpr.ApplicationConfig.WEBSOCKET_SUPPORT; -import static org.atmosphere.cpr.FrameworkConfig.ATMOSPHERE_CONFIG; -import static org.atmosphere.cpr.FrameworkConfig.HAZELCAST_BROADCASTER; -import static org.atmosphere.cpr.FrameworkConfig.JERSEY_BROADCASTER; -import static org.atmosphere.cpr.FrameworkConfig.JERSEY_CONTAINER; -import static org.atmosphere.cpr.FrameworkConfig.JGROUPS_BROADCASTER; -import static org.atmosphere.cpr.FrameworkConfig.JMS_BROADCASTER; -import static org.atmosphere.cpr.FrameworkConfig.REDIS_BROADCASTER; -import static org.atmosphere.cpr.FrameworkConfig.WRITE_HEADERS; -import static org.atmosphere.cpr.FrameworkConfig.XMPP_BROADCASTER; -import static org.atmosphere.cpr.HeaderConfig.ATMOSPHERE_POST_BODY; -import static org.atmosphere.cpr.HeaderConfig.X_ATMOSPHERE_TRACKING_ID; -import static org.atmosphere.websocket.WebSocket.WEBSOCKET_SUSPEND; - -/** - * The {@link AtmosphereFramework} is the entry point for the framework. This class can be used to from Servlet/filter - * to dispatch {@link AtmosphereRequest} and {@link AtmosphereResponse}. The framework can also be configured using - * the setXXX method. The life cycle of this class is - *
- * AtmosphereFramework f = new AtmosphereFramework();
- * f.init();
- * f.doCometSupport(AtmosphereRequest, AtmosphereResource);
- * f.destroy();
- * 
- * - * @author Jeanfrancois Arcand - */ -public class AtmosphereFramework implements ServletContextProvider { - public static final String DEFAULT_ATMOSPHERE_CONFIG_PATH = "/META-INF/atmosphere.xml"; - public static final String DEFAULT_LIB_PATH = "/WEB-INF/lib/"; - public static final String MAPPING_REGEX = "[a-zA-Z0-9-&.*=@~;\\?]+"; - - protected static final Logger logger = LoggerFactory.getLogger(AtmosphereFramework.class); - - protected final List broadcasterFilters = new ArrayList(); - protected final List asyncSupportListeners = new ArrayList(); - protected final ArrayList possibleComponentsCandidate = new ArrayList(); - protected final HashMap initParams = new HashMap(); - protected final AtmosphereConfig config; - protected final AtomicBoolean isCometSupportConfigured = new AtomicBoolean(false); - protected final boolean isFilter; - protected final Map atmosphereHandlers = new ConcurrentHashMap(); - protected final ConcurrentLinkedQueue broadcasterTypes = new ConcurrentLinkedQueue(); - - protected boolean useNativeImplementation = false; - protected boolean useBlockingImplementation = false; - protected boolean useStreamForFlushingComments = false; - protected AsyncSupport asyncSupport; - protected String broadcasterClassName = DefaultBroadcaster.class.getName(); - protected boolean isCometSupportSpecified = false; - protected boolean isBroadcasterSpecified = false; - protected boolean isSessionSupportSpecified = false; - protected BroadcasterFactory broadcasterFactory; - protected String broadcasterFactoryClassName; - protected String broadcasterCacheClassName; - protected boolean webSocketEnabled = true; - protected String broadcasterLifeCyclePolicy = "NEVER"; - protected String webSocketProtocolClassName = SimpleHttpProtocol.class.getName(); - protected WebSocketProtocol webSocketProtocol; - protected String handlersPath = "/WEB-INF/classes/"; - protected ServletConfig servletConfig; - protected boolean autoDetectHandlers = true; - private boolean hasNewWebSocketProtocol = false; - protected String atmosphereDotXmlPath = DEFAULT_ATMOSPHERE_CONFIG_PATH; - protected final LinkedList interceptors = new LinkedList(); - protected boolean scanDone = false; - protected String annotationProcessorClassName = "org.atmosphere.cpr.DefaultAnnotationProcessor"; - protected final List broadcasterListeners = new ArrayList(); - protected String webSocketProcessorClassName = DefaultWebSocketProcessor.class.getName(); - protected String libPath = DEFAULT_LIB_PATH; - protected boolean isInit; - protected boolean sharedThreadPools = true; - - public static final class AtmosphereHandlerWrapper { - - public final AtmosphereHandler atmosphereHandler; - public Broadcaster broadcaster; - public String mapping; - public List interceptors = Collections.emptyList(); - - public AtmosphereHandlerWrapper(BroadcasterFactory broadcasterFactory, AtmosphereHandler atmosphereHandler, String mapping) { - this.atmosphereHandler = atmosphereHandler; - try { - if (broadcasterFactory != null) { - this.broadcaster = broadcasterFactory.lookup(mapping, true); - } else { - this.mapping = mapping; - } - } catch (Exception t) { - throw new RuntimeException(t); - } - } - - public AtmosphereHandlerWrapper(AtmosphereHandler atmosphereHandler, Broadcaster broadcaster) { - this.atmosphereHandler = atmosphereHandler; - this.broadcaster = broadcaster; - } - - @Override - public String toString() { - return "AtmosphereHandlerWrapper{ atmosphereHandler=" + atmosphereHandler + ", broadcaster=" + - broadcaster + " }"; - } - } - - /** - * Create an AtmosphereFramework. - */ - public AtmosphereFramework() { - this(false, true); - } - - /** - * Create an AtmosphereFramework and initialize it via {@link AtmosphereFramework#init(javax.servlet.ServletConfig)} - */ - public AtmosphereFramework(ServletConfig sc) throws ServletException { - this(false, true); - init(sc); - } - - /** - * Create an AtmosphereFramework. - * - * @param isFilter true if this instance is used as an {@link AtmosphereFilter} - */ - public AtmosphereFramework(boolean isFilter, boolean autoDetectHandlers) { - this.isFilter = isFilter; - this.autoDetectHandlers = autoDetectHandlers; - readSystemProperties(); - populateBroadcasterType(); - config = new AtmosphereConfig(this); - } - - /** - * The order of addition is quite important here. - */ - private void populateBroadcasterType() { - broadcasterTypes.add(HAZELCAST_BROADCASTER); - broadcasterTypes.add(XMPP_BROADCASTER); - broadcasterTypes.add(REDIS_BROADCASTER); - broadcasterTypes.add(JGROUPS_BROADCASTER); - broadcasterTypes.add(JMS_BROADCASTER); - } - - /** - * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} - * This API is exposed to allow embedding an Atmosphere application. - * - * @param mapping The servlet mapping (servlet path) - * @param h implementation of an {@link AtmosphereHandler} - * @param l An attay of {@link AtmosphereInterceptor} - */ - public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, List l) { - if (!mapping.startsWith("/")) { - mapping = "/" + mapping; - } - - AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(broadcasterFactory, h, mapping); - w.interceptors = l; - addMapping(mapping, w); - - logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", h.getClass().getName(), mapping); - if (l.size() > 0) { - logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, h.getClass().getName()); - } - return this; - } - - /** - * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} - * This API is exposed to allow embedding an Atmosphere application. - * - * @param mapping The servlet mapping (servlet path) - * @param h implementation of an {@link AtmosphereHandler} - */ - public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h) { - addAtmosphereHandler(mapping, h, Collections.emptyList()); - return this; - } - - private AtmosphereFramework addMapping(String path, AtmosphereHandlerWrapper w) { - // We are using JAXRS mapping algorithm. - if (path.contains("*")) { - path = path.replace("*", MAPPING_REGEX); - } - - if (path.endsWith("/")) { - path = path + MAPPING_REGEX; - } - - InjectorProvider.getInjector().inject(w.atmosphereHandler); - atmosphereHandlers.put(path, w); - return this; - } - - /** - * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} - * This API is exposed to allow embedding an Atmosphere application. - * - * @param mapping The servlet mapping (servlet path) - * @param h implementation of an {@link AtmosphereHandler} - * @param broadcasterId The {@link Broadcaster#getID} value. - * @param l An attay of {@link AtmosphereInterceptor} - */ - public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, String broadcasterId, List l) { - if (!mapping.startsWith("/")) { - mapping = "/" + mapping; - } - - AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(broadcasterFactory, h, mapping); - w.broadcaster.setID(broadcasterId); - w.interceptors = l; - addMapping(mapping, w); - logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", h.getClass().getName(), mapping); - if (l.size() > 0) { - logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, h.getClass().getName()); - } - return this; - } - - /** - * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} - * This API is exposed to allow embedding an Atmosphere application. - * - * @param mapping The servlet mapping (servlet path) - * @param h implementation of an {@link AtmosphereHandler} - * @param broadcasterId The {@link Broadcaster#getID} value. - */ - public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, String broadcasterId) { - addAtmosphereHandler(mapping, h, broadcasterId, Collections.emptyList()); - return this; - } - - /** - * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} - * This API is exposed to allow embedding an Atmosphere application. - * - * @param mapping The servlet mapping (servlet path) - * @param h implementation of an {@link AtmosphereHandler} - * @param broadcaster The {@link Broadcaster} associated with AtmosphereHandler. - * @param l An attay of {@link AtmosphereInterceptor} - */ - public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, Broadcaster broadcaster, List l) { - if (!mapping.startsWith("/")) { - mapping = "/" + mapping; - } - - AtmosphereHandlerWrapper w = new AtmosphereHandlerWrapper(h, broadcaster); - w.interceptors = l; - - addMapping(mapping, w); - logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", h.getClass().getName(), mapping); - if (l.size() > 0) { - logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, h.getClass().getName()); - } - return this; - } - - /** - * Add an {@link AtmosphereHandler} serviced by the {@link Servlet} - * This API is exposed to allow embedding an Atmosphere application. - * - * @param mapping The servlet mapping (servlet path) - * @param h implementation of an {@link AtmosphereHandler} - * @param broadcaster The {@link Broadcaster} associated with AtmosphereHandler. - */ - public AtmosphereFramework addAtmosphereHandler(String mapping, AtmosphereHandler h, Broadcaster broadcaster) { - addAtmosphereHandler(mapping, h, broadcaster, Collections.emptyList()); - return this; - } - - /** - * Remove an {@link AtmosphereHandler} - * - * @param mapping the mapping used when invoking {@link #addAtmosphereHandler(String, AtmosphereHandler)}; - * @return true if removed - */ - public AtmosphereFramework removeAtmosphereHandler(String mapping) { - - if (mapping.endsWith("/")) { - mapping += MAPPING_REGEX; - } - - atmosphereHandlers.remove(mapping); - return this; - } - - /** - * Remove all {@link AtmosphereHandler} - */ - public AtmosphereFramework removeAllAtmosphereHandler() { - atmosphereHandlers.clear(); - return this; - } - - /** - * Remove all init parameters. - */ - public AtmosphereFramework removeAllInitParams() { - initParams.clear(); - return this; - } - - /** - * Add init-param like if they were defined in web.xml - * - * @param name The name - * @param value The value - */ - public AtmosphereFramework addInitParameter(String name, String value) { - initParams.put(name, value); - return this; - } - - protected void readSystemProperties() { - if (System.getProperty(PROPERTY_NATIVE_COMETSUPPORT) != null) { - useNativeImplementation = Boolean - .parseBoolean(System.getProperty(PROPERTY_NATIVE_COMETSUPPORT)); - isCometSupportSpecified = true; - } - - if (System.getProperty(PROPERTY_BLOCKING_COMETSUPPORT) != null) { - useBlockingImplementation = Boolean - .parseBoolean(System.getProperty(PROPERTY_BLOCKING_COMETSUPPORT)); - isCometSupportSpecified = true; - } - atmosphereDotXmlPath = System.getProperty(PROPERTY_ATMOSPHERE_XML, atmosphereDotXmlPath); - - if (System.getProperty(DISABLE_ONSTATE_EVENT) != null) { - initParams.put(DISABLE_ONSTATE_EVENT, System.getProperty(DISABLE_ONSTATE_EVENT)); - } - } - - /** - * Path specific container using their own property. - */ - public void patchContainer() { - System.setProperty("org.apache.catalina.STRICT_SERVLET_COMPLIANCE", "false"); - } - - /** - * Initialize the AtmosphereFramework. Invoke that method after having properly configured this class using the setter. - */ - public AtmosphereFramework init() { - try { - init(new ServletConfig() { - - @Override - public String getServletName() { - return "AtmosphereFramework"; - } - - @Override - public ServletContext getServletContext() { - return (ServletContext) Proxy.newProxyInstance(getClass().getClassLoader(), new Class[]{ServletContext.class}, - new InvocationHandler() { - @Override - public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { - logger.trace("Method {} not supported", method.getName()); - return null; - } - }); - } - - @Override - public String getInitParameter(String name) { - return initParams.get(name); - } - - @Override - public Enumeration getInitParameterNames() { - return Collections.enumeration(initParams.values()); - } - }); - } catch (ServletException e) { - logger.error("", e); - } - return this; - } - - /** - * Initialize the AtmosphereFramework using the {@link ServletContext} - * - * @param sc the {@link ServletContext} - */ - public AtmosphereFramework init(final ServletConfig sc) throws ServletException { - - if (isInit) return this; - - try { - ServletContextHolder.register(this); - - ServletConfig scFacade = new ServletConfig() { - - public String getServletName() { - return sc.getServletName(); - } - - public ServletContext getServletContext() { - return sc.getServletContext(); - } - - public String getInitParameter(String name) { - String param = initParams.get(name); - if (param == null) { - return sc.getInitParameter(name); - } - return param; - } - - public Enumeration getInitParameterNames() { - Enumeration en = sc.getInitParameterNames(); - while (en.hasMoreElements()) { - String name = (String) en.nextElement(); - if (!initParams.containsKey(name)) { - initParams.put(name, sc.getInitParameter(name)); - } - } - return Collections.enumeration(initParams.keySet()); - } - }; - this.servletConfig = scFacade; - asyncSupportListener(new AsyncSupportListenerAdapter()); - - autoConfigureService(scFacade.getServletContext()); - patchContainer(); - doInitParams(scFacade); - doInitParamsForWebSocket(scFacade); - configureBroadcaster(); - loadConfiguration(scFacade); - initWebSocket(); - - autoDetectContainer(); - configureWebDotXmlAtmosphereHandler(sc); - asyncSupport.init(scFacade); - initAtmosphereHandler(scFacade); - configureAtmosphereInterceptor(sc); - - if (broadcasterCacheClassName == null) { - logger.warn("No BroadcasterCache configured. Broadcasted message between client reconnection will be LOST. " + - "It is recommended to configure the {}", UUIDBroadcasterCache.class.getName()); - } else { - logger.info("Using BroadcasterCache: {}", broadcasterCacheClassName); - } - - // http://java.net/jira/browse/ATMOSPHERE-157 - if (sc.getServletContext() != null) { - sc.getServletContext().setAttribute(BroadcasterFactory.class.getName(), broadcasterFactory); - } - - for (String i : broadcasterFilters) { - logger.info("Using BroadcastFilter: {}", i); - } - - String s = config.getInitParameter(ApplicationConfig.BROADCASTER_SHARABLE_THREAD_POOLS); - if (s != null) { - sharedThreadPools = Boolean.parseBoolean(s); - } - - logger.info("Shared ExecutorService supported: {}", sharedThreadPools); - logger.info("HttpSession supported: {}", config.isSupportSession()); - logger.info("Using BroadcasterFactory: {}", broadcasterFactory.getClass().getName()); - logger.info("Using WebSocketProcessor: {}", webSocketProcessorClassName); - logger.info("Using Broadcaster: {}", broadcasterClassName); - logger.info("Atmosphere Framework {} started.", Version.getRawVersion()); - - String showSupportMessage = config.getInitParameter("org.atmosphere.cpr.showSupportMessage"); - if (showSupportMessage == null || Boolean.parseBoolean(showSupportMessage)) { - logger.info("\n\n\tFor Commercial Support, visit \n\t{} " + - "or send an email to {}\n", "http://www.async-io.org/", "support@async-io.org"); - } - } catch (Throwable t) { - logger.error("Failed to initialize Atmosphere Framework", t); - - if (t instanceof ServletException) { - throw (ServletException) t; - } - - throw new ServletException(t); - } - isInit = true; - return this; - } - - /** - * Configure the list of {@link AtmosphereInterceptor}. - * - * @param sc a ServletConfig - */ - protected void configureAtmosphereInterceptor(ServletConfig sc) { - String s = sc.getInitParameter(ApplicationConfig.ATMOSPHERE_INTERCEPTORS); - if (s != null) { - String[] list = s.split(","); - for (String a : list) { - try { - AtmosphereInterceptor ai = (AtmosphereInterceptor) Thread.currentThread().getContextClassLoader() - .loadClass(a.trim()).newInstance(); - ai.configure(config); - interceptor(ai); - } catch (InstantiationException e) { - logger.warn("", e); - } catch (IllegalAccessException e) { - logger.warn("", e); - } catch (ClassNotFoundException e) { - logger.warn("", e); - } - } - } - - s = sc.getInitParameter(ApplicationConfig.DISABLE_ATMOSPHEREINTERCEPTOR); - if (s == null) { - // OnDisconnect - interceptors.addFirst(newAInterceptor(OnDisconnectInterceptor.class)); - // ADD Tracking ID Handshake - interceptors.addFirst(newAInterceptor(JavaScriptProtocol.class)); - // ADD JSONP support - interceptors.addFirst(newAInterceptor(JSONPAtmosphereInterceptor.class)); - // Add SSE support - interceptors.addFirst(newAInterceptor(SSEAtmosphereInterceptor.class)); - // Android 2.3.x streaming support - interceptors.addFirst(newAInterceptor(AndroidAtmosphereInterceptor.class)); - logger.info("Installed Default AtmosphereInterceptor {}. " + - "Set org.atmosphere.cpr.AtmosphereInterceptor.disableDefaults in your xml to disable them.", interceptors); - } - } - - protected AtmosphereInterceptor newAInterceptor(Class a) { - AtmosphereInterceptor ai = null; - try { - ai = (AtmosphereInterceptor) getClass().getClassLoader().loadClass(a.getName()).newInstance(); - ai.configure(config); - } catch (Exception ex) { - logger.warn("", ex); - } - return ai; - } - - protected void configureWebDotXmlAtmosphereHandler(ServletConfig sc) { - String s = sc.getInitParameter(ATMOSPHERE_HANDLER); - if (s != null) { - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - try { - - String mapping = sc.getInitParameter(ATMOSPHERE_HANDLER_MAPPING); - if (mapping == null) { - mapping = "/*"; - } - addAtmosphereHandler(mapping, (AtmosphereHandler) cl.loadClass(s).newInstance()); - } catch (Exception ex) { - logger.warn("Unable to load WebSocketHandle instance", ex); - } - } - } - - protected void configureBroadcaster() { - - try { - // Check auto supported one - if (isBroadcasterSpecified == false) { - broadcasterClassName = lookupDefaultBroadcasterType(broadcasterClassName); - } - - Class bc = - (Class) Thread.currentThread().getContextClassLoader() - .loadClass(broadcasterClassName); - if (broadcasterFactoryClassName != null) { - broadcasterFactory = (BroadcasterFactory) Thread.currentThread().getContextClassLoader() - .loadClass(broadcasterFactoryClassName).newInstance(); - } - - if (broadcasterFactory == null) { - broadcasterFactory = new DefaultBroadcasterFactory(bc, broadcasterLifeCyclePolicy, config); - } - - for (BroadcasterListener b : broadcasterListeners) { - broadcasterFactory.addBroadcasterListener(b); - } - - BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config); - InjectorProvider.getInjector().inject(broadcasterFactory); - - Iterator> i = atmosphereHandlers.entrySet().iterator(); - AtmosphereHandlerWrapper w; - Entry e; - while (i.hasNext()) { - e = i.next(); - w = e.getValue(); - - if (w.broadcaster == null) { - w.broadcaster = broadcasterFactory.get(w.mapping); - } else { - if (broadcasterCacheClassName != null) { - BroadcasterCache cache = (BroadcasterCache) Thread.currentThread().getContextClassLoader() - .loadClass(broadcasterCacheClassName).newInstance(); - InjectorProvider.getInjector().inject(cache); - w.broadcaster.getBroadcasterConfig().setBroadcasterCache(cache); - } - } - } - } catch (Exception ex) { - logger.error("Unable to configure Broadcaster/Factory/Cache", ex); - } - } - - protected void doInitParamsForWebSocket(ServletConfig sc) { - String s = sc.getInitParameter(WEBSOCKET_SUPPORT); - if (s != null) { - webSocketEnabled = Boolean.parseBoolean(s); - sessionSupport(false); - } - s = sc.getInitParameter(WEBSOCKET_PROTOCOL); - if (s != null) { - webSocketProtocolClassName = s; - } - - s = sc.getInitParameter(WEBSOCKET_PROCESSOR); - if (s != null) { - webSocketProcessorClassName = s; - } - } - - /** - * Read init param from web.xml and apply them. - * - * @param sc {@link ServletConfig} - */ - protected void doInitParams(ServletConfig sc) { - String s = sc.getInitParameter(PROPERTY_NATIVE_COMETSUPPORT); - if (s != null) { - useNativeImplementation = Boolean.parseBoolean(s); - if (useNativeImplementation) isCometSupportSpecified = true; - } - s = sc.getInitParameter(PROPERTY_BLOCKING_COMETSUPPORT); - if (s != null) { - useBlockingImplementation = Boolean.parseBoolean(s); - if (useBlockingImplementation) isCometSupportSpecified = true; - } - s = sc.getInitParameter(PROPERTY_USE_STREAM); - if (s != null) { - useStreamForFlushingComments = Boolean.parseBoolean(s); - } - s = sc.getInitParameter(PROPERTY_COMET_SUPPORT); - if (s != null) { - asyncSupport = new DefaultAsyncSupportResolver(config).newCometSupport(s); - isCometSupportSpecified = true; - } - s = sc.getInitParameter(BROADCASTER_CLASS); - if (s != null) { - broadcasterClassName = s; - isBroadcasterSpecified = true; - } - s = sc.getInitParameter(BROADCASTER_CACHE); - if (s != null) { - broadcasterCacheClassName = s; - } - s = sc.getInitParameter(PROPERTY_SESSION_SUPPORT); - if (s != null) { - config.setSupportSession(Boolean.valueOf(s)); - isSessionSupportSpecified = true; - } - s = sc.getInitParameter(DISABLE_ONSTATE_EVENT); - if (s != null) { - initParams.put(DISABLE_ONSTATE_EVENT, s); - } else { - initParams.put(DISABLE_ONSTATE_EVENT, "false"); - } - s = sc.getInitParameter(RESUME_AND_KEEPALIVE); - if (s != null) { - initParams.put(RESUME_AND_KEEPALIVE, s); - } - s = sc.getInitParameter(BROADCAST_FILTER_CLASSES); - if (s != null) { - broadcasterFilters.addAll(Arrays.asList(s.split(","))); - logger.info("Installing BroadcastFilter class(es) {}", s); - } - s = sc.getInitParameter(BROADCASTER_LIFECYCLE_POLICY); - if (s != null) { - broadcasterLifeCyclePolicy = s; - } - s = sc.getInitParameter(BROADCASTER_FACTORY); - if (s != null) { - broadcasterFactoryClassName = s; - } - s = sc.getInitParameter(ATMOSPHERE_HANDLER_PATH); - if (s != null) { - handlersPath = s; - } - s = sc.getInitParameter(PROPERTY_ATMOSPHERE_XML); - if (s != null) { - atmosphereDotXmlPath = s; - } - } - - public void loadConfiguration(ServletConfig sc) throws ServletException { - - if (!autoDetectHandlers) return; - - try { - URL url = sc.getServletContext().getResource(handlersPath); - URLClassLoader urlC = new URLClassLoader(new URL[]{url}, - Thread.currentThread().getContextClassLoader()); - loadAtmosphereDotXml(sc.getServletContext(). - getResourceAsStream(atmosphereDotXmlPath), urlC); - - if (atmosphereHandlers.size() == 0) { - autoDetectAtmosphereHandlers(sc.getServletContext(), urlC); - - if (atmosphereHandlers.size() == 0) { - detectSupportedFramework(sc); - } - } - - autoDetectWebSocketHandler(sc.getServletContext(), urlC); - } catch (Throwable t) { - throw new ServletException(t); - } - } - - /** - * Auto-detect Jersey when no atmosphere.xml file are specified. - * - * @param sc {@link ServletConfig} - * @return true if Jersey classes are detected - * @throws ClassNotFoundException - */ - protected boolean detectSupportedFramework(ServletConfig sc) throws ClassNotFoundException, IllegalAccessException, - InstantiationException, NoSuchMethodException, InvocationTargetException { - - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - String broadcasterClassNameTmp = null; - - try { - cl.loadClass(JERSEY_CONTAINER); - - if (!isBroadcasterSpecified) { - broadcasterClassNameTmp = lookupDefaultBroadcasterType(JERSEY_BROADCASTER); - - cl.loadClass(broadcasterClassNameTmp); - } - useStreamForFlushingComments = true; - } catch (Throwable t) { - logger.trace("", t); - return false; - } - - logger.warn("Missing META-INF/atmosphere.xml but found the Jersey runtime. Starting Jersey"); - - // Jersey will handle itself the headers. - initParams.put(WRITE_HEADERS, "false"); - - ReflectorServletProcessor rsp = new ReflectorServletProcessor(); - if (broadcasterClassNameTmp != null) broadcasterClassName = broadcasterClassNameTmp; - rsp.setServletClassName(JERSEY_CONTAINER); - sessionSupport(false); - initParams.put(DISABLE_ONSTATE_EVENT, "true"); - - String mapping = sc.getInitParameter(PROPERTY_SERVLET_MAPPING); - if (mapping == null) { - mapping = "/*"; - } - Class bc = (Class) cl.loadClass(broadcasterClassName); - - - if (broadcasterFactory != null) { - broadcasterFactory.destroy(); - } - broadcasterFactory = new DefaultBroadcasterFactory(bc, broadcasterLifeCyclePolicy, config); - BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config); - - for (BroadcasterListener b : broadcasterListeners) { - broadcasterFactory.addBroadcasterListener(b); - } - - Broadcaster b; - - try { - b = broadcasterFactory.get(bc, mapping); - } catch (IllegalStateException ex) { - logger.warn("Two Broadcaster's named {}. Renaming the second one to {}", mapping, sc.getServletName() + mapping); - b = broadcasterFactory.get(bc, sc.getServletName() + mapping); - } - - addAtmosphereHandler(mapping, rsp, b); - return true; - } - - protected String lookupDefaultBroadcasterType(String defaultB) { - for (String b : broadcasterTypes) { - try { - Class.forName(b); - return b; - } catch (ClassNotFoundException e) { - } - } - return defaultB; - } - - protected void sessionSupport(boolean sessionSupport) { - if (!isSessionSupportSpecified) { - config.setSupportSession(sessionSupport); - } else if (!config.isSupportSession()) { - // Don't turn off session support. Once it's on, leave it on. - config.setSupportSession(sessionSupport); - } - } - - /** - * Initialize {@link AtmosphereServletProcessor} - * - * @param sc the {@link ServletConfig} - * @throws javax.servlet.ServletException - */ - public void initAtmosphereHandler(ServletConfig sc) throws ServletException { - AtmosphereHandler a; - AtmosphereHandlerWrapper w; - for (Entry h : atmosphereHandlers.entrySet()) { - w = h.getValue(); - a = w.atmosphereHandler; - if (a instanceof AtmosphereServletProcessor) { - ((AtmosphereServletProcessor) a).init(sc); - } - } - - if (atmosphereHandlers.size() == 0 && !SimpleHttpProtocol.class.isAssignableFrom(webSocketProtocol.getClass())) { - logger.debug("Adding a void AtmosphereHandler mapped to /* to allow WebSocket application only"); - addAtmosphereHandler("/*", new AbstractReflectorAtmosphereHandler() { - @Override - public void onRequest(AtmosphereResource r) throws IOException { - logger.debug("No AtmosphereHandler defined."); - } - - @Override - public void destroy() { - } - }); - } - } - - protected void initWebSocket() { - if (webSocketProtocol == null) { - try { - webSocketProtocol = (WebSocketProtocol) Thread.currentThread().getContextClassLoader() - .loadClass(webSocketProtocolClassName).newInstance(); - logger.info("Installed WebSocketProtocol {} ", webSocketProtocolClassName); - } catch (Exception ex) { - try { - webSocketProtocol = (WebSocketProtocol) AtmosphereFramework.class.getClassLoader() - .loadClass(webSocketProtocolClassName).newInstance(); - logger.info("Installed WebSocketProtocol {} ", webSocketProtocolClassName); - } catch (Exception ex2) { - logger.error("Cannot load the WebSocketProtocol {}", getWebSocketProtocolClassName(), ex); - webSocketProtocol = new SimpleHttpProtocol(); - } - } - } - webSocketProtocol.configure(config); - } - - public AtmosphereFramework destroy() { - if (asyncSupport != null && AsynchronousProcessor.class.isAssignableFrom(asyncSupport.getClass())) { - ((AsynchronousProcessor) asyncSupport).shutdown(); - } - - // We just need one bc to shutdown the shared thread pool - for (Entry entry : atmosphereHandlers.entrySet()) { - AtmosphereHandlerWrapper handlerWrapper = entry.getValue(); - handlerWrapper.atmosphereHandler.destroy(); - } - - BroadcasterFactory factory = broadcasterFactory; - if (factory != null) { - factory.destroy(); - BroadcasterFactory.factory = null; - } - WebSocketProcessorFactory.getDefault().destroy(); - return this; - } - - /** - * Load AtmosphereHandler defined under META-INF/atmosphere.xml - * - * @param stream The input stream we read from. - * @param c The classloader - */ - protected void loadAtmosphereDotXml(InputStream stream, URLClassLoader c) - throws IOException, ServletException { - - if (stream == null) { - return; - } - - AtmosphereConfigReader.getInstance().parse(config, stream); - for (AtmosphereHandlerConfig atmoHandler : config.getAtmosphereHandlerConfig()) { - try { - AtmosphereHandler handler; - - if (!ReflectorServletProcessor.class.getName().equals(atmoHandler.getClassName())) { - handler = (AtmosphereHandler) c.loadClass(atmoHandler.getClassName()).newInstance(); - } else { - handler = new ReflectorServletProcessor(); - } - - logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", handler, atmoHandler.getContextRoot()); - - for (ApplicationConfiguration a : atmoHandler.getApplicationConfig()) { - initParams.put(a.getParamName(), a.getParamValue()); - } - - for (FrameworkConfiguration a : atmoHandler.getFrameworkConfig()) { - initParams.put(a.getParamName(), a.getParamValue()); - } - - for (AtmosphereHandlerProperty handlerProperty : atmoHandler.getProperties()) { - - if (handlerProperty.getValue() != null && handlerProperty.getValue().indexOf("jersey") != -1) { - initParams.put(DISABLE_ONSTATE_EVENT, "true"); - useStreamForFlushingComments = true; - broadcasterClassName = lookupDefaultBroadcasterType(JERSEY_BROADCASTER); - broadcasterFactory = null; - configureBroadcaster(); - } - - IntrospectionUtils.setProperty(handler, handlerProperty.getName(), handlerProperty.getValue()); - IntrospectionUtils.addProperty(handler, handlerProperty.getName(), handlerProperty.getValue()); - } - - sessionSupport(Boolean.valueOf(atmoHandler.getSupportSession())); - - String broadcasterClass = atmoHandler.getBroadcaster(); - Broadcaster b; - /** - * If there is more than one AtmosphereHandler defined, their Broadcaster - * may clash each other with the BroadcasterFactory. In that case we will use the - * last one defined. - */ - if (broadcasterClass != null) { - broadcasterClassName = broadcasterClass; - ClassLoader cl = Thread.currentThread().getContextClassLoader(); - Class bc = (Class) cl.loadClass(broadcasterClassName); - broadcasterFactory = new DefaultBroadcasterFactory(bc, broadcasterLifeCyclePolicy, config); - BroadcasterFactory.setBroadcasterFactory(broadcasterFactory, config); - } - - b = broadcasterFactory.lookup(atmoHandler.getContextRoot(), true); - - AtmosphereHandlerWrapper wrapper = new AtmosphereHandlerWrapper(handler, b); - addMapping(atmoHandler.getContextRoot(), wrapper); - - String bc = atmoHandler.getBroadcasterCache(); - if (bc != null) { - broadcasterCacheClassName = bc; - } - - if (atmoHandler.getCometSupport() != null) { - asyncSupport = (AsyncSupport) c.loadClass(atmoHandler.getCometSupport()) - .getDeclaredConstructor(new Class[]{AtmosphereConfig.class}) - .newInstance(new Object[]{config}); - } - - if (atmoHandler.getBroadcastFilterClasses() != null) { - broadcasterFilters.addAll(atmoHandler.getBroadcastFilterClasses()); - } - - List l = new ArrayList(); - if (atmoHandler.getAtmosphereInterceptorClasses() != null) { - for (String a : atmoHandler.getAtmosphereInterceptorClasses()) { - try { - AtmosphereInterceptor ai = (AtmosphereInterceptor) c.loadClass(a).newInstance(); - ai.configure(config); - l.add(ai); - } catch (Throwable e) { - logger.warn("", e); - } - } - } - wrapper.interceptors = l; - if (l.size() > 0) { - logger.info("Installed AtmosphereInterceptor {} mapped to AtmosphereHandler {}", l, atmoHandler.getClassName()); - } - } catch (Throwable t) { - logger.warn("Unable to load AtmosphereHandler class: " + atmoHandler.getClassName(), t); - throw new ServletException(t); - } - - } - } - - /** - * Set the {@link AsyncSupport} implementation. Make sure you don't set - * an implementation that only works on some Container. See {@link BlockingIOCometSupport} - * for an example. - * - * @param asyncSupport - */ - public AtmosphereFramework setAsyncSupport(AsyncSupport asyncSupport) { - this.asyncSupport = asyncSupport; - return this; - } - - /** - * @param asyncSupport - * @return - * @Deprecated - Use {@link #setAsyncSupport(AsyncSupport)} - */ - public AtmosphereFramework setCometSupport(AsyncSupport asyncSupport) { - return setAsyncSupport(asyncSupport); - } - - /** - * Return the current {@link AsyncSupport} - * - * @return the current {@link AsyncSupport} - */ - public AsyncSupport getAsyncSupport() { - return asyncSupport; - } - - /** - * Return the current {@link AsyncSupport} - * - * @return the current {@link AsyncSupport} - * @deprecated Use getAsyncSupport - */ - public AsyncSupport getCometSupport() { - return asyncSupport; - } - - /** - * Returns an instance of AsyncSupportResolver {@link AsyncSupportResolver} - * - * @return CometSupportResolver - */ - protected AsyncSupportResolver createAsyncSupportResolver() { - return new DefaultAsyncSupportResolver(config); - } - - - /** - * Auto detect the underlying Servlet Container we are running on. - */ - protected void autoDetectContainer() { - // Was defined in atmosphere.xml - if (getAsyncSupport() == null) { - setAsyncSupport(createAsyncSupportResolver() - .resolve(useNativeImplementation, useBlockingImplementation, webSocketEnabled)); - } - - logger.info("Atmosphere is using async support: {} running under container: {}", - getAsyncSupport().getClass().getName(), asyncSupport.getContainerName()); - } - - /** - * Auto detect instance of {@link AtmosphereHandler} in case META-INF/atmosphere.xml - * is missing. - * - * @param servletContext {@link ServletContext} - * @param classloader {@link URLClassLoader} to load the class. - * @throws java.net.MalformedURLException - * @throws java.net.URISyntaxException - */ - public void autoDetectAtmosphereHandlers(ServletContext servletContext, URLClassLoader classloader) - throws MalformedURLException, URISyntaxException { - - // If Handler has been added - if (atmosphereHandlers.size() > 0) return; - - logger.info("Auto detecting atmosphere handlers {}", handlersPath); - - String realPath = servletContext.getRealPath(handlersPath); - - // Weblogic bug - if (realPath == null) { - URL u = servletContext.getResource(handlersPath); - if (u == null) return; - realPath = u.getPath(); - } - - loadAtmosphereHandlersFromPath(classloader, realPath); - } - - public void loadAtmosphereHandlersFromPath(URLClassLoader classloader, String realPath) { - File file = new File(realPath); - - if (file.isDirectory()) { - getFiles(file); - scanDone = true; - - for (String className : possibleComponentsCandidate) { - try { - className = className.replace('\\', '/'); - className = className.replaceFirst("^.*/(WEB-INF|target)(?:/scala-[^/]+)?/(test-)?classes/(.*)\\.class", "$3").replace("/", "."); - Class clazz = classloader.loadClass(className); - - if (AtmosphereHandler.class.isAssignableFrom(clazz)) { - AtmosphereHandler handler = (AtmosphereHandler) clazz.newInstance(); - InjectorProvider.getInjector().inject(handler); - addMapping("/" + handler.getClass().getSimpleName(), - new AtmosphereHandlerWrapper(broadcasterFactory, handler, "/" + handler.getClass().getSimpleName())); - logger.info("Installed AtmosphereHandler {} mapped to context-path: {}", handler, handler.getClass().getName()); - } - } catch (Throwable t) { - logger.trace("failed to load class as an AtmosphereHandler: " + className, t); - } - } - } - } - - /** - * Auto detect instance of {@link org.atmosphere.websocket.WebSocketHandler} in case META-INF/atmosphere.xml - * is missing. - * - * @param servletContext {@link ServletContext} - * @param classloader {@link URLClassLoader} to load the class. - * @throws java.net.MalformedURLException - * @throws java.net.URISyntaxException - */ - protected void autoDetectWebSocketHandler(ServletContext servletContext, URLClassLoader classloader) - throws MalformedURLException, URISyntaxException { - - if (hasNewWebSocketProtocol) return; - - logger.info("Auto detecting WebSocketHandler in {}", handlersPath); - - String realPath = servletContext.getRealPath(handlersPath); - - // Weblogic bug - if (realPath == null) { - URL u = servletContext.getResource(handlersPath); - if (u == null) return; - realPath = u.getPath(); - } - - loadWebSocketFromPath(classloader, realPath); - } - - protected void loadWebSocketFromPath(URLClassLoader classloader, String realPath) { - File file = new File(realPath); - - if (file.isDirectory()) { - getFiles(file); - scanDone = true; - - for (String className : possibleComponentsCandidate) { - try { - className = className.replace('\\', '/'); - className = className.replaceFirst("^.*/(WEB-INF|target)(?:/scala-[^/]+)?/(test-)?classes/(.*)\\.class", "$3").replace("/", "."); - Class clazz = classloader.loadClass(className); - - if (WebSocketProtocol.class.isAssignableFrom(clazz)) { - webSocketProtocol = (WebSocketProtocol) clazz.newInstance(); - InjectorProvider.getInjector().inject(webSocketProtocol); - logger.info("Installed WebSocketProtocol {}", webSocketProtocol); - } - } catch (Throwable t) { - logger.trace("failed to load class as an WebSocketProtocol: " + className, t); - } - } - } - } - - - /** - * Get the list of possible candidate to load as {@link AtmosphereHandler} - * - * @param f the real path {@link File} - */ - private void getFiles(File f) { - if (scanDone) return; - - File[] files = f.listFiles(); - for (File test : files) { - if (test.isDirectory()) { - getFiles(test); - } else { - String clazz = test.getAbsolutePath(); - if (clazz.endsWith(".class")) { - possibleComponentsCandidate.add(clazz); - } - } - } - } - - /** - * Invoke the proprietary {@link AsyncSupport} - * - * @param req - * @param res - * @return an {@link Action} - * @throws IOException - * @throws ServletException - */ - public Action doCometSupport(AtmosphereRequest req, AtmosphereResponse res) throws IOException, ServletException { - req.setAttribute(BROADCASTER_FACTORY, broadcasterFactory); - req.setAttribute(PROPERTY_USE_STREAM, useStreamForFlushingComments); - req.setAttribute(BROADCASTER_CLASS, broadcasterClassName); - req.setAttribute(ATMOSPHERE_CONFIG, config); - - Action a = null; - try { - boolean skip = true; - String s = config.getInitParameter(ALLOW_QUERYSTRING_AS_REQUEST); - if (s != null) { - skip = Boolean.valueOf(s); - } - if (!skip || req.getAttribute(WEBSOCKET_SUSPEND) == null) { - Map headers = configureQueryStringAsRequest(req); - String body = headers.remove(ATMOSPHERE_POST_BODY); - if (body != null && body.isEmpty()) { - body = null; - } - - req.headers(headers) - .method(body != null && req.getMethod().equalsIgnoreCase("GET") ? "POST" : req.getMethod()); - - if (body != null) { - req.body(body); - } - } - - s = req.getHeader(X_ATMOSPHERE_TRACKING_ID); - - // Lookup for websocket - if (s == null || s.equals("0")) { - String unique = config.getInitParameter(ApplicationConfig.UNIQUE_UUID_WEBSOCKET); - if (unique != null && Boolean.valueOf(unique)) { - s = (String) req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID); - } - } - - if (s == null || s.equals("0")) { - s = UUID.randomUUID().toString(); - res.setHeader(X_ATMOSPHERE_TRACKING_ID, s); - } else { - // This may breaks 1.0.0 application because the WebSocket's associated AtmosphereResource will - // all have the same UUID, and retrieving the original one for WebSocket, so we don't set it at all. - // Null means it is not an HTTP request. - if (req.resource() == null) { - res.setHeader(X_ATMOSPHERE_TRACKING_ID, s); - } else if (req.getAttribute(WebSocket.WEBSOCKET_INITIATED) == null) { - // WebSocket reconnect, in case an application manually set the header - // (impossible to retrieve the headers normally with WebSocket or SSE) - res.setHeader(X_ATMOSPHERE_TRACKING_ID, s); - } - } - - if (req.getAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID) == null) { - req.setAttribute(SUSPENDED_ATMOSPHERE_RESOURCE_UUID, s); - } - - a = asyncSupport.service(req, res); - } catch (IllegalStateException ex) { - if (ex.getMessage() != null && (ex.getMessage().startsWith("Tomcat failed") || ex.getMessage().startsWith("JBoss failed"))) { - if (!isFilter) { - logger.warn("Failed using comet support: {}, error: {} Is the Nio or Apr Connector enabled?", asyncSupport.getClass().getName(), - ex.getMessage()); - } - logger.trace(ex.getMessage(), ex); - - AsyncSupport current = asyncSupport; - asyncSupport = asyncSupport.supportWebSocket() ? new Tomcat7BIOSupportWithWebSocket(config) : new BlockingIOCometSupport(config); - if (current instanceof AsynchronousProcessor) { - ((AsynchronousProcessor) current).shutdown(); - } - - asyncSupport.init(config.getServletConfig()); - logger.warn("Using " + asyncSupport.getClass().getName()); - - a = asyncSupport.service(req, res); - } else { - logger.error("AtmosphereFramework exception", ex); - throw ex; - } - } finally { - if (a != null) { - notify(a.type(), req, res); - } - - if (req != null && a != null && a.type() != Action.TYPE.SUSPEND) { - req.destroy(); - res.destroy(); - notify(Action.TYPE.DESTROYED, req, res); - } - } - return a; - } - - /** - * Return the default {@link Broadcaster} class name. - * - * @return the broadcasterClassName - */ - public String getDefaultBroadcasterClassName() { - return broadcasterClassName; - } - - /** - * Set the default {@link Broadcaster} class name - * - * @param bccn the broadcasterClassName to set - */ - public AtmosphereFramework setDefaultBroadcasterClassName(String bccn) { - broadcasterClassName = bccn; - return this; - } - - /** - * true if Atmosphere uses {@link AtmosphereResponse#getOutputStream()} - * by default for write operation. - * - * @return the useStreamForFlushingComments - */ - public boolean isUseStreamForFlushingComments() { - return useStreamForFlushingComments; - } - - /** - * Set to true so Atmosphere uses {@link AtmosphereResponse#getOutputStream()} - * by default for write operation. Default is false. - * - * @param useStreamForFlushingComments the useStreamForFlushingComments to set - */ - public AtmosphereFramework setUseStreamForFlushingComments(boolean useStreamForFlushingComments) { - this.useStreamForFlushingComments = useStreamForFlushingComments; - return this; - } - - /** - * Get the {@link BroadcasterFactory} which is used by Atmosphere to construct - * {@link Broadcaster} - * - * @return {@link BroadcasterFactory} - */ - public BroadcasterFactory getBroadcasterFactory() { - return broadcasterFactory; - } - - /** - * Set the {@link BroadcasterFactory} which is used by Atmosphere to construct - * {@link Broadcaster} - * - * @return {@link BroadcasterFactory} - */ - public AtmosphereFramework setBroadcasterFactory(final BroadcasterFactory broadcasterFactory) { - this.broadcasterFactory = broadcasterFactory; - configureBroadcaster(); - return this; - } - - /** - * Return the {@link org.atmosphere.cpr.BroadcasterCache} class name. - * - * @return the {@link org.atmosphere.cpr.BroadcasterCache} class name. - */ - public String getBroadcasterCacheClassName() { - return broadcasterCacheClassName; - } - - /** - * Set the {@link org.atmosphere.cpr.BroadcasterCache} class name. - * - * @param broadcasterCacheClassName - */ - public void setBroadcasterCacheClassName(String broadcasterCacheClassName) { - this.broadcasterCacheClassName = broadcasterCacheClassName; - configureBroadcaster(); - } - - /** - * Add a new Broadcaster class name AtmosphereServlet can use when initializing requests, and when - * atmosphere.xml broadcaster element is unspecified. - * - * @param broadcasterTypeString - */ - public AtmosphereFramework addBroadcasterType(String broadcasterTypeString) { - broadcasterTypes.add(broadcasterTypeString); - return this; - } - - public String getWebSocketProtocolClassName() { - return webSocketProtocolClassName; - } - - public AtmosphereFramework setWebSocketProtocolClassName(String webSocketProtocolClassName) { - hasNewWebSocketProtocol = true; - this.webSocketProtocolClassName = webSocketProtocolClassName; - return this; - } - - public Map getAtmosphereHandlers() { - return atmosphereHandlers; - } - - protected Map configureQueryStringAsRequest(AtmosphereRequest request) { - Map headers = new HashMap(); - - Enumeration e = request.getParameterNames(); - String s; - while (e.hasMoreElements()) { - s = e.nextElement(); - if (s.equalsIgnoreCase("Content-Type")) { - // Use the one set by the user first. - if (request.getContentType() == null || - !request.getContentType().equalsIgnoreCase(request.getParameter(s))) { - request.contentType(request.getParameter(s)); - } - } - headers.put(s, request.getParameter(s)); - } - logger.trace("Query String translated to headers {}", headers); - return headers; - } - - protected boolean isIECandidate(AtmosphereRequest request) { - String userAgent = request.getHeader("User-Agent"); - if (userAgent == null) return false; - - if (userAgent.contains("MSIE") || userAgent.contains(".NET")) { - // Now check the header - String transport = request.getHeader(HeaderConfig.X_ATMOSPHERE_TRANSPORT); - if (transport != null) { - return false; - } else { - return true; - } - } - return false; - } - - public WebSocketProtocol getWebSocketProtocol() { - // TODO: Spagetthi code, needs to rework. - // Make sure we initialized the WebSocketProtocol - initWebSocket(); - return webSocketProtocol; - } - - public boolean isUseNativeImplementation() { - return useNativeImplementation; - } - - public AtmosphereFramework setUseNativeImplementation(boolean useNativeImplementation) { - this.useNativeImplementation = useNativeImplementation; - return this; - } - - public boolean isUseBlockingImplementation() { - return useBlockingImplementation; - } - - public AtmosphereFramework setUseBlockingImplementation(boolean useBlockingImplementation) { - this.useBlockingImplementation = useBlockingImplementation; - return this; - } - - public String getAtmosphereDotXmlPath() { - return atmosphereDotXmlPath; - } - - public AtmosphereFramework setAtmosphereDotXmlPath(String atmosphereDotXmlPath) { - this.atmosphereDotXmlPath = atmosphereDotXmlPath; - return this; - } - - public String getHandlersPath() { - return handlersPath; - } - - public AtmosphereFramework setHandlersPath(String handlersPath) { - this.handlersPath = handlersPath; - return this; - } - - /** - * Return the location of the jars containing the application classes. Default is WEB-INF/lib - * - * @return the location of the jars containing the application classes. Default is WEB-INF/lib - */ - public String getLibPath() { - return libPath; - } - - /** - * Set the location of the jars containing the application. - * - * @param libPath the location of the jars containing the application. - * @return this - */ - public AtmosphereFramework setLibPath(String libPath) { - this.libPath = libPath; - return this; - } - - public String getWebSocketProcessorClassName() { - return webSocketProcessorClassName; - } - - public AtmosphereFramework setWebsocketProcessorClassName(String webSocketProcessorClassName) { - this.webSocketProcessorClassName = webSocketProcessorClassName; - return this; - } - - /** - * Add an {@link AtmosphereInterceptor} implementation. The adding order or {@link AtmosphereInterceptor} will be used, e.g - * the first added {@link AtmosphereInterceptor} will always be called first. - * - * @param c {@link AtmosphereInterceptor} - * @return this - */ - public AtmosphereFramework interceptor(AtmosphereInterceptor c) { - boolean found = false; - for (AtmosphereInterceptor interceptor : interceptors) { - if (interceptor.getClass().equals(c.getClass())) { - found = true; - break; - } - } - - if (!found) { - interceptors.addLast(c); - logger.info("Installed AtmosphereInterceptor {}. ", c); - } - return this; - } - - /** - * Return the list of {@link AtmosphereInterceptor} - * - * @return the list of {@link AtmosphereInterceptor} - */ - public LinkedList interceptors() { - return interceptors; - } - - /** - * Set the {@link AnnotationProcessor} class name. - * - * @param annotationProcessorClassName the {@link AnnotationProcessor} class name. - * @return this - */ - public AtmosphereFramework annotationProcessorClassName(String annotationProcessorClassName) { - this.annotationProcessorClassName = annotationProcessorClassName; - return this; - } - - /** - * Add an {@link AsyncSupportListener} - * - * @param asyncSupportListener an {@link AsyncSupportListener} - * @return this; - */ - public AtmosphereFramework asyncSupportListener(AsyncSupportListener asyncSupportListener) { - asyncSupportListeners.add(asyncSupportListener); - return this; - } - - /** - * Return the list of an {@link AsyncSupportListener} - * - * @return - */ - public List asyncSupportListeners() { - return asyncSupportListeners; - } - - /** - * Add {@link BroadcasterListener} to all created {@link Broadcaster} - */ - public AtmosphereFramework addBroadcasterListener(BroadcasterListener b) { - if (isInit) { - broadcasterFactory.addBroadcasterListener(b); - } else { - broadcasterListeners.add(b); - } - return this; - } - - /** - * Return a configured instance of {@link AtmosphereConfig} - * - * @return a configured instance of {@link AtmosphereConfig} - */ - public AtmosphereConfig getAtmosphereConfig() { - return config; - } - - @Override - public ServletContext getServletContext() { - return servletConfig.getServletContext(); - } - - public ServletConfig getServletConfig() { - return servletConfig; - } - - /** - * Return the list of {@link BroadcastFilter} - * - * @return the list of {@link BroadcastFilter - */ - public List broadcasterFilters() { - return broadcasterFilters; - } - - /** - * Returns true if {@link java.util.concurrent.ExecutorService} shared amongst all components. - * - * @return true if {@link java.util.concurrent.ExecutorService} shared amongst all components. - */ - public boolean isShareExecutorServices() { - return sharedThreadPools; - } - - /** - * Set to true to have a {@link java.util.concurrent.ExecutorService} shared amongst all components. - * - * @param sharedThreadPools - * @return this - */ - public AtmosphereFramework shareExecutorServices(boolean sharedThreadPools) { - this.sharedThreadPools = sharedThreadPools; - return this; - } - - protected void autoConfigureService(ServletContext sc) throws IOException { - final ClassLoader cl = Thread.currentThread().getContextClassLoader(); - - String path = libPath != DEFAULT_LIB_PATH ? libPath : sc.getRealPath(handlersPath); - try { - AnnotationProcessor p = (AnnotationProcessor) cl.loadClass(annotationProcessorClassName).newInstance(); - logger.info("Atmosphere is using {} for processing annotation", annotationProcessorClassName); - - p.configure(this); - if (path != null) { - p.scan(new File(path)); - } - - String pathLibs = sc.getRealPath(DEFAULT_LIB_PATH); - if (pathLibs != null) { - File libFolder = new File(pathLibs); - File jars[] = libFolder.listFiles(new FilenameFilter() { - - @Override - public boolean accept(File arg0, String arg1) { - return arg1.endsWith(".jar"); - } - }); - - for (File file : jars) { - p.scan(file); - } - } - } catch (Throwable e) { - logger.debug("Atmosphere's Service Annotation Not Supported. Please add https://github.com/rmuller/infomas-asl as dependencies or your own AnnotationProcessor to support @Service"); - logger.trace("", e); - return; - } - } - - protected void notify(Action.TYPE type, AtmosphereRequest request, AtmosphereResponse response) { - for (AsyncSupportListener l : asyncSupportListeners()) { - try { - switch (type) { - case TIMEOUT: - l.onTimeout(request, response); - break; - case CANCELLED: - l.onClose(request, response); - break; - case SUSPEND: - l.onSuspend(request, response); - break; - case RESUME: - l.onSuspend(request, response); - break; - case DESTROYED: - l.onDestroyed(request, response); - break; - } - } catch (Throwable t) { - logger.warn("", t); - } - } - } -} \ No newline at end of file diff --git a/server/src/com/vaadin/server/Constants.java b/server/src/com/vaadin/server/Constants.java index f8d8105286..cf1031dab2 100644 --- a/server/src/com/vaadin/server/Constants.java +++ b/server/src/com/vaadin/server/Constants.java @@ -65,11 +65,11 @@ public interface Constants { + " Widgetset version: %s\n" + "================================================================="; - static final String REQUIRED_ATMOSPHERE_VERSION = "1.0.12"; + static final String REQUIRED_ATMOSPHERE_VERSION = "1.0.13"; static final String INVALID_ATMOSPHERE_VERSION_WARNING = "\n" + "=================================================================\n" - + "Vaadin depends on Atomsphere {0} but version {1} was found.\n" + + "Vaadin depends on Atmosphere {0} but version {1} was found.\n" + "This might cause compatibility problems if push is used.\n" + "================================================================="; diff --git a/shared/src/com/vaadin/shared/communication/PushConstants.java b/shared/src/com/vaadin/shared/communication/PushConstants.java index 2d63621c12..f16cbb7390 100644 --- a/shared/src/com/vaadin/shared/communication/PushConstants.java +++ b/shared/src/com/vaadin/shared/communication/PushConstants.java @@ -42,5 +42,5 @@ public class PushConstants implements Serializable { * The character used to mark message boundaries when messages may be split * into multiple fragments. */ - public static final char MESSAGE_DELIMITER = '\0'; + public static final char MESSAGE_DELIMITER = '|'; } -- 2.39.5