tuscany-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From fm...@apache.org
Subject svn commit: r1127080 - in /tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main: java/org/apache/tuscany/sca/binding/comet/runtime/ java/org/apache/tuscany/sca/binding/comet/runtime/callback/ java/org/apache/tuscany/sca/binding/comet/runti...
Date Tue, 24 May 2011 14:27:46 GMT
Author: fmoga
Date: Tue May 24 14:27:45 2011
New Revision: 1127080

URL: http://svn.apache.org/viewvc?rev=1127080&view=rev
Log:
Improve comet support. Add status to callback return type to determine when browser client
has closed the page. Upgrade to atmosphere-jquery-0.7.1. Add support for multiple tabs. Fix
and improve reliability of long polling technique.

Added:
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/Status.java
Removed:
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometMessageContext.java
Modified:
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometComponentContext.java
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometInvoker.java
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometServiceBindingProvider.java
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/CometCallback.java
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/handler/CometBindingHandler.java
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/cometComponentContext.js
    tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/jquery.atmosphere.js

Modified: tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometComponentContext.java
URL: http://svn.apache.org/viewvc/tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometComponentContext.java?rev=1127080&r1=1127079&r2=1127080&view=diff
==============================================================================
--- tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometComponentContext.java
(original)
+++ tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometComponentContext.java
Tue May 24 14:27:45 2011
@@ -24,9 +24,14 @@ import java.util.concurrent.ConcurrentHa
 
 import org.apache.tuscany.sca.interfacedef.Operation;
 import org.apache.tuscany.sca.runtime.RuntimeEndpoint;
+import org.atmosphere.cpr.Broadcaster;
+
+import com.google.gson.Gson;
 
 public class CometComponentContext {
 
+	public static Map<String, Broadcaster> broadcasters = new ConcurrentHashMap<String,
Broadcaster>();
+	public static Gson gson = new Gson();
 	private Map<String, RuntimeEndpoint> endpoints;
 	private Map<String, Operation> operations;
 

Modified: tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometInvoker.java
URL: http://svn.apache.org/viewvc/tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometInvoker.java?rev=1127080&r1=1127079&r2=1127080&view=diff
==============================================================================
--- tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometInvoker.java
(original)
+++ tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometInvoker.java
Tue May 24 14:27:45 2011
@@ -20,11 +20,13 @@
 package org.apache.tuscany.sca.binding.comet.runtime;
 
 import org.apache.tuscany.sca.assembly.EndpointReference;
-import org.apache.tuscany.sca.binding.comet.runtime.handler.CometBindingHandler;
+import org.apache.tuscany.sca.binding.comet.runtime.callback.Status;
+import org.apache.tuscany.sca.core.invocation.Constants;
 import org.apache.tuscany.sca.core.invocation.impl.MessageImpl;
 import org.apache.tuscany.sca.interfacedef.Operation;
 import org.apache.tuscany.sca.invocation.Invoker;
 import org.apache.tuscany.sca.invocation.Message;
+import org.atmosphere.cpr.Broadcaster;
 
 public class CometInvoker implements Invoker {
 
@@ -38,18 +40,28 @@ public class CometInvoker implements Inv
 
 	@Override
 	public Message invoke(final Message msg) {
-		String operation = msg.getOperation().getName();
-		CometMessageContext context = msg.getBindingContext();
-		CometBindingHandler handler = context.getCometHandler();
-		Message message = new MessageImpl();
-		if (operation.equals("sendResponse")) {
-			String callbackMethod = context.getCallbackMethod();
+		return handleSendMessage(msg);
+	}
+
+	private Message handleSendMessage(Message msg) {
+		String sessionId = (String) msg.getHeaders().get(Constants.RELATES_TO);
+		Broadcaster broadcaster = CometComponentContext.broadcasters.get(sessionId);
+		Message response = new MessageImpl();
+		if (broadcaster == null) {
+			System.out.println("Broadcaster already removed.");
+			response.setBody(Status.CLIENT_DISCONNECTED);
+		} else if (broadcaster.getAtmosphereResources().isEmpty()) {
+			System.out.println("Removing broadcaster " + sessionId + "...");
+			CometComponentContext.broadcasters.remove(sessionId);
+			response.setBody(Status.CLIENT_DISCONNECTED);
+		} else {
+			System.out.println("Using broadcaster " + sessionId + "...");
+			String callbackMethod = msg.getTo().getURI();
 			Object[] body = msg.getBody();
-			handler.respondToClient(callbackMethod, body[0]);
-		} else if (operation.equals("isClientConnected")) {
-			message.setBody(handler.isClientConnected());
+			broadcaster.broadcast(callbackMethod + "($.secureEvalJSON('" + CometComponentContext.gson.toJson(body[0])
+					+ "'))");
+			response.setBody(Status.OK);
 		}
-		return message;
+		return response;
 	}
-
 }

Modified: tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometServiceBindingProvider.java
URL: http://svn.apache.org/viewvc/tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometServiceBindingProvider.java?rev=1127080&r1=1127079&r2=1127080&view=diff
==============================================================================
--- tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometServiceBindingProvider.java
(original)
+++ tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/CometServiceBindingProvider.java
Tue May 24 14:27:45 2011
@@ -33,59 +33,63 @@ import org.apache.tuscany.sca.runtime.Ru
  */
 public class CometServiceBindingProvider implements ServiceBindingProvider {
 
-    /**
-     * Service's endpoint.
-     */
-    private final RuntimeEndpoint endpoint;
-
-    /**
-     * The underlying servlet host.
-     */
-    private final ServletHost servletHost;
-
-    /**
-     * Constructor.
-     * 
-     * @param endpoint the given endpoint
-     * @param servletHost the given servlet host
-     */
-    public CometServiceBindingProvider(final RuntimeEndpoint endpoint, final ServletHost
servletHost) {
-        this.endpoint = endpoint;
-        this.servletHost = servletHost;
-    }
-
-    /**
-     * This method is used to start the provider.
-     */
-    @Override
-    public void start() {
-        String deployedURI = ServletFactory.registerServlet(this.servletHost);
-        endpoint.setDeployedURI(deployedURI);
-        final ComponentService service = this.endpoint.getService();
-        final Interface serviceInterface = service.getInterfaceContract().getInterface();
-        JavascriptGenerator.generateServiceProxy(service);
-        for (final Operation operation : serviceInterface.getOperations()) {
-            JavascriptGenerator.generateMethodProxy(service, operation);
-            ServletFactory.registerOperation(this.endpoint, operation);
-        }
-    }
-
-    /**
-     * This method is used to stop the provider.
-     */
-    @Override
-    public void stop() {
-        ServletFactory.unregisterServlet(this.servletHost);
-    }
-
-    @Override
-    public InterfaceContract getBindingInterfaceContract() {
-        return endpoint.getService().getInterfaceContract();
-    }
-
-    @Override
-    public boolean supportsOneWayInvocation() {
-        return true;
-    }
+	/**
+	 * Service's endpoint.
+	 */
+	private final RuntimeEndpoint endpoint;
+
+	/**
+	 * The underlying servlet host.
+	 */
+	private final ServletHost servletHost;
+
+	/**
+	 * Constructor.
+	 * 
+	 * @param endpoint
+	 *            the given endpoint
+	 * @param servletHost
+	 *            the given servlet host
+	 */
+	public CometServiceBindingProvider(final RuntimeEndpoint endpoint, final ServletHost servletHost)
{
+		this.endpoint = endpoint;
+		this.servletHost = servletHost;
+	}
+
+	/**
+	 * This method is used to start the provider.
+	 */
+	@Override
+	public void start() {
+		String deployedURI = ServletFactory.registerServlet(this.servletHost);
+		endpoint.setDeployedURI(deployedURI);
+		final ComponentService service = this.endpoint.getService();
+		final Interface serviceInterface = service.getInterfaceContract().getInterface();
+		JavascriptGenerator.generateServiceProxy(service);
+		for (final Operation operation : serviceInterface.getOperations()) {
+			JavascriptGenerator.generateMethodProxy(service, operation);
+			ServletFactory.registerOperation(this.endpoint, operation);
+		}
+	}
+
+	/**
+	 * This method is used to stop the provider.
+	 */
+	@Override
+	public void stop() {
+		ServletFactory.unregisterServlet(this.servletHost);
+	}
+
+	@Override
+	public InterfaceContract getBindingInterfaceContract() {
+		return endpoint.getService().getInterfaceContract();
+	}
+
+	@Override
+	public boolean supportsOneWayInvocation() {
+		// set to false so the runtime will add a nonBlocking interceptor to
+		// handle @OneWay calls
+		return false;
+	}
 
 }

Modified: tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/CometCallback.java
URL: http://svn.apache.org/viewvc/tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/CometCallback.java?rev=1127080&r1=1127079&r2=1127080&view=diff
==============================================================================
--- tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/CometCallback.java
(original)
+++ tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/CometCallback.java
Tue May 24 14:27:45 2011
@@ -24,8 +24,6 @@ import org.oasisopen.sca.annotation.Remo
 @Remotable
 public interface CometCallback {
 
-	void sendResponse(Object response);
-
-	boolean isClientConnected();
+	Status sendMessage(Object message);
 
 }

Added: tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/Status.java
URL: http://svn.apache.org/viewvc/tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/Status.java?rev=1127080&view=auto
==============================================================================
--- tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/Status.java
(added)
+++ tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/callback/Status.java
Tue May 24 14:27:45 2011
@@ -0,0 +1,5 @@
+package org.apache.tuscany.sca.binding.comet.runtime.callback;
+
+public enum Status {
+	OK, CLIENT_DISCONNECTED
+}

Modified: tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/handler/CometBindingHandler.java
URL: http://svn.apache.org/viewvc/tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/handler/CometBindingHandler.java?rev=1127080&r1=1127079&r2=1127080&view=diff
==============================================================================
--- tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/handler/CometBindingHandler.java
(original)
+++ tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/java/org/apache/tuscany/sca/binding/comet/runtime/handler/CometBindingHandler.java
Tue May 24 14:27:45 2011
@@ -32,24 +32,27 @@ import javax.ws.rs.Path;
 import javax.ws.rs.PathParam;
 import javax.ws.rs.Produces;
 import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MediaType;
 
 import org.apache.tuscany.sca.assembly.EndpointReference;
 import org.apache.tuscany.sca.binding.comet.runtime.CometComponentContext;
-import org.apache.tuscany.sca.binding.comet.runtime.CometMessageContext;
 import org.apache.tuscany.sca.binding.comet.runtime.ServletFactory;
 import org.apache.tuscany.sca.core.assembly.impl.RuntimeEndpointImpl;
 import org.apache.tuscany.sca.core.assembly.impl.RuntimeEndpointReferenceImpl;
+import org.apache.tuscany.sca.core.invocation.Constants;
 import org.apache.tuscany.sca.core.invocation.impl.MessageImpl;
 import org.apache.tuscany.sca.interfacedef.DataType;
 import org.apache.tuscany.sca.interfacedef.Operation;
 import org.apache.tuscany.sca.invocation.Message;
 import org.apache.tuscany.sca.runtime.RuntimeEndpoint;
-import org.atmosphere.annotation.Broadcast;
+import org.atmosphere.cache.SessionBroadcasterCache;
 import org.atmosphere.cpr.Broadcaster;
 import org.atmosphere.cpr.DefaultBroadcaster;
+import org.atmosphere.cpr.DefaultBroadcasterFactory;
+import org.atmosphere.jersey.JerseyBroadcaster;
 import org.atmosphere.jersey.SuspendResponse;
+import org.atmosphere.jersey.util.JerseyBroadcasterUtil;
 
-import com.google.gson.Gson;
 import com.sun.jersey.spi.container.servlet.PerSession;
 
 /**
@@ -67,11 +70,6 @@ public class CometBindingHandler {
 	private Broadcaster broadcaster;
 
 	/**
-	 * JSON converter.
-	 */
-	private Gson gson = new Gson();
-
-	/**
 	 * The underlying servlet context.
 	 */
 	@Context
@@ -82,6 +80,15 @@ public class CometBindingHandler {
 
 	private CometComponentContext context;
 
+	@GET
+	@Path("/sessionId")
+	@Produces(MediaType.TEXT_PLAIN)
+	public String establishSessionId() {
+		request.getSession().invalidate();
+		request.getSession(true);
+		return "OK";
+	}
+
 	/**
 	 * Method called at comet connect time. This suspends the response and keeps
 	 * the connection opened.
@@ -89,12 +96,14 @@ public class CometBindingHandler {
 	 * @return the suspended response
 	 */
 	@GET
+	@Path("/connect")
 	public SuspendResponse<String> connect() {
 		System.out.println("-- connect -- Session Id: " + request.getSession().getId());
 		if (broadcaster == null) {
-			broadcaster = new DefaultBroadcaster();
+			broadcaster = new JerseyBroadcaster();
 			context = (CometComponentContext) sc.getAttribute(ServletFactory.COMET_COMPONENT_CONTEXT_KEY);
 		}
+		CometComponentContext.broadcasters.put(request.getSession().getId(), broadcaster);
 		return new SuspendResponse.SuspendResponseBuilder<String>().broadcaster(this.broadcaster).outputComments(true)
 				.build();
 	}
@@ -117,7 +126,6 @@ public class CometBindingHandler {
 	 */
 	@POST
 	@Path("/{service}/{method}")
-	@Broadcast
 	public void handleRequest(@PathParam("service") final String service, @PathParam("method")
final String method,
 			@FormParam("callback") final String callbackMethod, @FormParam("params") final String
jsonData)
 			throws InvocationTargetException {
@@ -128,6 +136,7 @@ public class CometBindingHandler {
 
 		final Object[] args = decodeJsonDataForOperation(jsonData, operation);
 		Message msg = createMessageWithMockedCometReference(args, callbackMethod);
+		System.out.println("CometBindingHandler thread id: " + Thread.currentThread().getId());
 		wire.invoke(operation, msg);
 	}
 
@@ -144,7 +153,7 @@ public class CometBindingHandler {
 		int index = 0;
 		// convert each argument to the corresponding class
 		for (final DataType<?> dataType : operation.getInputType().getLogical()) {
-			args[index] = this.gson.fromJson(json[index], dataType.getPhysical());
+			args[index] = CometComponentContext.gson.fromJson(json[index], dataType.getPhysical());
 			index++;
 		}
 		return args;
@@ -161,11 +170,12 @@ public class CometBindingHandler {
 	 */
 	private Message createMessageWithMockedCometReference(Object[] args, String callbackMethod)
{
 		Message msg = new MessageImpl();
+		msg.getHeaders().put(Constants.MESSAGE_ID, request.getSession().getId());
 		msg.setBody(args);
-		CometMessageContext messageContext = new CometMessageContext(this, callbackMethod);
-		msg.setBindingContext(messageContext);
 		EndpointReference re = new RuntimeEndpointReferenceImpl();
-		re.setCallbackEndpoint(new RuntimeEndpointImpl());
+		RuntimeEndpointImpl callbackEndpoint = new RuntimeEndpointImpl();
+		callbackEndpoint.setURI(callbackMethod);
+		re.setCallbackEndpoint(callbackEndpoint);
 		msg.setFrom(re);
 		return msg;
 	}
@@ -211,12 +221,4 @@ public class CometBindingHandler {
 		return objects.toArray(new String[] {});
 	}
 
-	public void respondToClient(String callbackMethod, Object response) {
-		broadcaster.broadcast(callbackMethod + "($.secureEvalJSON('" + this.gson.toJson(response)
+ "'))");
-	}
-
-	public boolean isClientConnected() {
-		return !broadcaster.getAtmosphereResources().isEmpty();
-	}
-
 }

Modified: tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/cometComponentContext.js
URL: http://svn.apache.org/viewvc/tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/cometComponentContext.js?rev=1127080&r1=1127079&r2=1127080&view=diff
==============================================================================
--- tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/cometComponentContext.js
(original)
+++ tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/cometComponentContext.js
Tue May 24 14:27:45 2011
@@ -1,21 +1,19 @@
 
 /**
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you 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
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you 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
+ * 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.    
+ * 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.
  */
 
 var SCA = new function() {
@@ -24,10 +22,11 @@ this.TuscanyComet = {
 	appUrl: 'tuscany-comet',
 	connectedEndpoint : null,
 	connect : function(transport) {
-		$.atmosphere.subscribe(document.location.toString() + this.appUrl,
+		$.atmosphere.subscribe(document.location.toString() + this.appUrl + "/connect",
 				this.callback, 
 				$.atmosphere.request = {
-					transport : transport
+					transport : transport,
+					maxRequest: 1000000000
 				});
 		this.connectedEndpoint = $.atmosphere.response;
 	},
@@ -45,4 +44,11 @@ this.TuscanyComet = {
 	}
 };
 
+
+$.ajax({
+	url: document.location.toString() + this.TuscanyComet.appUrl + '/sessionId',
+	type: 'GET',
+	async: false,
+});
+
 this.CometComponentContext = new Object();

Modified: tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/jquery.atmosphere.js
URL: http://svn.apache.org/viewvc/tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/jquery.atmosphere.js?rev=1127080&r1=1127079&r2=1127080&view=diff
==============================================================================
--- tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/jquery.atmosphere.js
(original)
+++ tuscany/sca-java-2.x/trunk/modules/binding-comet-runtime/src/main/resources/jquery.atmosphere.js
Tue May 24 14:27:45 2011
@@ -3,7 +3,7 @@
  * 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
+ * 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,
@@ -14,10 +14,18 @@
 jQuery.atmosphere = function()
 {
     var activeRequest;
-    $(window).unload(function()
+    jQuery(window).unload(function()
     {
-        if (activeRequest)
+        if (activeRequest){
             activeRequest.abort();
+        }
+
+        if( !(typeof(transferDoc) == 'undefined') ){
+            if(transferDoc != null){
+                transferDoc = null;
+                CollectGarbage();
+            }
+        }
     });
 
     return {
@@ -34,6 +42,7 @@ jQuery.atmosphere = function()
         },
 
         request : {},
+        abordingConnection: false,
         logLevel : 'info',
         callbacks: [],
         activeTransport : null,
@@ -46,7 +55,7 @@ jQuery.atmosphere = function()
                 timeout: 300000,
                 method: 'GET',
                 headers: {},
-                contentType : "text/html;charset=ISO-8859-1",
+                contentType : '',
                 cache: true,
                 async: true,
                 ifModified: false,
@@ -57,10 +66,11 @@ jQuery.atmosphere = function()
                 suspend : true,
                 maxRequest : 60,
                 lastIndex : 0,
-                logLevel :  'info',
+                logLevel : 'info',
                 requestCount : 0,
                 fallbackTransport : 'streaming',
-                transport : 'long-polling'
+                transport : 'long-polling',
+                webSocketImpl: null
 
             }, request);
 
@@ -78,9 +88,10 @@ jQuery.atmosphere = function()
             if (jQuery.atmosphere.request.transport != 'websocket') {
                 jQuery.atmosphere.executeRequest();
             } else if (jQuery.atmosphere.request.transport == 'websocket') {
-                if (!window.WebSocket) {
+                if (jQuery.atmosphere.request.webSocketImpl == null && !window.WebSocket)
{
                     jQuery.atmosphere.log(logLevel, ["Websocket is not supported, using request.fallbackTransport"]);
                     jQuery.atmosphere.request.transport = jQuery.atmosphere.request.fallbackTransport;
+                    jQuery.atmosphere.response.transport = jQuery.atmosphere.request.fallbackTransport;
                     jQuery.atmosphere.executeRequest();
                 }
                 else {
@@ -93,6 +104,7 @@ jQuery.atmosphere = function()
          * Always make sure one transport is used, not two at the same time except for Websocket.
          */
         closeSuspendedConnection : function () {
+            jQuery.atmosphere.abordingConnection = true;
             if (activeRequest != null) {
                 activeRequest.abort();
             }
@@ -101,16 +113,24 @@ jQuery.atmosphere = function()
                 jQuery.atmosphere.websocket.close();
                 jQuery.atmosphere.websocket = null;
             }
+            jQuery.atmosphere.abordingConnection = false;
+
+            if (!(typeof(transferDoc) == 'undefined')) {
+                if (transferDoc != null) {
+                    transferDoc = null;
+                    CollectGarbage();
+                }
+            }
         },
 
         executeRequest: function()
         {
 
             if (jQuery.atmosphere.request.transport == 'streaming') {
-                if ($.browser.msie) {
+                if (jQuery.browser.msie) {
                     jQuery.atmosphere.ieStreaming();
                     return;
-                } else if ((typeof window.addEventStream) == 'function') {
+                } else if (jQuery.browser.opera) {
                     jQuery.atmosphere.operaStreaming();
                     return;
                 }
@@ -131,8 +151,8 @@ jQuery.atmosphere = function()
 
                 var ajaxRequest;
                 var error = false;
-                if ($.browser.msie) {
-                    var activexmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"]
+                if (jQuery.browser.msie) {
+                    var activexmodes = ["Msxml2.XMLHTTP", "Microsoft.XMLHTTP"];
                     for (var i = 0; i < activexmodes.length; i++) {
                         try {
                             ajaxRequest = new ActiveXObject(activexmodes[i])
@@ -151,9 +171,17 @@ jQuery.atmosphere = function()
                 ajaxRequest.open(request.method, request.url, true);
                 ajaxRequest.setRequestHeader("X-Atmosphere-Framework", jQuery.atmosphere.version);
                 ajaxRequest.setRequestHeader("X-Atmosphere-Transport", request.transport);
-                ajaxRequest.setRequestHeader("X-Cache-Date", new Date());
+                ajaxRequest.setRequestHeader("X-Cache-Date", new Date().getTime());
+
+                if (jQuery.atmosphere.request.contentType != '') {
+                    ajaxRequest.setRequestHeader("Content-Type", jQuery.atmosphere.request.contentType);
+                }
 
-                if (!$.browser.msie) {
+                for(var x in request.headers) {
+                    ajaxRequest.setRequestHeader(x, request.headers[x]);
+                }
+
+                if (!jQuery.browser.msie) {
                     ajaxRequest.onerror = function()
                     {
                         error = true;
@@ -173,6 +201,8 @@ jQuery.atmosphere = function()
 
                 ajaxRequest.onreadystatechange = function()
                 {
+                    if (jQuery.atmosphere.abordingConnection) return;
+
                     var junkForWebkit = false;
                     var update = false;
                     if (ajaxRequest.readyState == 4) {
@@ -181,10 +211,10 @@ jQuery.atmosphere = function()
                             jQuery.atmosphere.executeRequest();
                         }
 
-                        if ($.browser.msie) {
+                        if (jQuery.browser.msie) {
                             update = true;
                         }
-                    } else if (!$.browser.msie && ajaxRequest.readyState == 3 &&
ajaxRequest.status == 200) {
+                    } else if (!jQuery.browser.msie && ajaxRequest.readyState ==
3 && ajaxRequest.status == 200) {
                         update = true;
                     } else {
                         clearTimeout(request.id);
@@ -193,24 +223,24 @@ jQuery.atmosphere = function()
                     if (update) {
                         if (request.transport == 'streaming') {
                             response.responseBody = ajaxRequest.responseText.substring(request.lastIndex,
ajaxRequest.responseText.length);
-                            request.lastIndex = ajaxRequest.responseText.length;
 
-                            if (response.responseBody.indexOf("<!--") != -1) {
-                                junkForWebkit = true;
+                            if (request.lastIndex == 0 && response.responseBody.indexOf("<!--
Welcome to the Atmosphere Framework.") != -1) {
+                                var endOfJunk = "<!-- EOD -->";
+                                var endOfJunkLenght = "<!-- EOD -->".length;
+                                var junkEnd = response.responseBody.indexOf(endOfJunk) +
endOfJunkLenght;
+
+                                if (junkEnd != ajaxRequest.responseText.length) {
+                                    response.responseBody = response.responseBody.substring(junkEnd);
+                                } else {
+                                    junkForWebkit = true;
+                                }
                             }
-
+                            request.lastIndex = ajaxRequest.responseText.length;
+                            if (junkForWebkit) return;
                         } else {
                             response.responseBody = ajaxRequest.responseText;
                         }
 
-                        if (response.responseBody.indexOf("parent.callback") != -1) {
-                            var start = response.responseBody.indexOf("('") + 2;
-                            var end = response.responseBody.indexOf("')");
-                            response.responseBody = response.responseBody.substring(start,
end);
-                        }
-
-                        if (junkForWebkit) return;
-
                         try {
                             response.status = ajaxRequest.status;
                             response.headers = ajaxRequest.getAllResponseHeaders();
@@ -224,9 +254,22 @@ jQuery.atmosphere = function()
                         } else {
                             response.state = "messagePublished";
                         }
-                        jQuery.atmosphere.invokeCallback(response);
+
+                        if (response.responseBody.indexOf("parent.callback") != -1) {
+                            var index = 0;
+                            var responseBody = response.responseBody;
+                            while ( responseBody.indexOf("('", index) != -1) {
+                              var start = responseBody.indexOf("('", index) + 2;
+                              var end = responseBody.indexOf("')", index);
+                              response.responseBody = responseBody.substring(start, end);
+                              index = end + 2;
+                              jQuery.atmosphere.invokeCallback(response);
+                            }
+                        } else {
+                            jQuery.atmosphere.invokeCallback(response);
+                        }
                     }
-                }
+                };
                 ajaxRequest.send(request.data);
 
                 if (request.suspend) {
@@ -245,51 +288,54 @@ jQuery.atmosphere = function()
         operaStreaming: function()
         {
 
-            var url = jQuery.atmosphere.request.url;
-            var es = document.createElement('event-source');
-            var response = jQuery.atmosphere.response;
+            jQuery.atmosphere.closeSuspendedConnection();
 
+            var url = jQuery.atmosphere.request.url;
+            var callback = jQuery.atmosphere.request.callback;
             jQuery.atmosphere.response.push = function (url)
             {
                 jQuery.atmosphere.request.transport = 'polling';
                 jQuery.atmosphere.request.callback = null;
                 jQuery.atmosphere.publish(url, null, jQuery.atmosphere.request);
             };
-            
-            es.setAttribute('src', url);
-            // without this check opera 9.5 would make two connections.
-            if (opera.version() < 9.5) {
-                document.body.appendChild(es);
-            }
-
-            var operaCallback = function (event) {
-                if (event.data) {
-                    var junkForWebkit = false;
-                    
-                    response.responseBody = event.data;
-                    if (event.data.indexOf("<!--") != -1) {
-                        junkForWebkit = true;
-                    }
-
-                    if (response.responseBody.indexOf("parent.callback") != -1) {
-                        var start = response.responseBody.indexOf("('") + 2;
-                        var end = response.responseBody.indexOf("')");
-                        response.responseBody = response.responseBody.substring(start, end);
-                    }
-
-                    if (junkForWebkit) return;
 
-                    response.state = "messageReceived";
-                    jQuery.atmosphere.invokeCallback(response);
-                }
-            };
+            function init()
+            {
+                var iframe = document.createElement("iframe");
+                iframe.style.width = "0px";
+                iframe.style.height = "0px";
+                iframe.style.border = "0px";
+                iframe.id = "__atmosphere";
+                document.body.appendChild(iframe);
+                var d;
+                if (iframe.contentWindow) {
+                    d = iframe.contentWindow.document;
+                } else if (iframe.document) {
+                    d = iframe.document;
+                } else if (iframe.contentDocument) {
+                    d = iframe.contentDocument;
+                }
+
+                if (/\?/i.test(url)) url += "&";
+                else url += "?";
+                url += "callback=jquery.atmosphere.streamingCallback";
+                iframe.src = url;
+            }
 
-            es.addEventListener('payload', operaCallback, false);
+            init();
 
         },
 
         ieStreaming : function()
         {
+
+            if (!(typeof(transferDoc) == 'undefined')) {
+                if (transferDoc != null) {
+                    transferDoc = null;
+                    CollectGarbage();
+                }
+            }
+
             var url = jQuery.atmosphere.request.url;
             jQuery.atmosphere.response.push = function (url)
             {
@@ -298,6 +344,7 @@ jQuery.atmosphere = function()
                 jQuery.atmosphere.publish(url, null, jQuery.atmosphere.request);
             };
 
+            //Must not use var here to avoid IE from disconnecting
             transferDoc = new ActiveXObject("htmlfile");
             transferDoc.open();
             transferDoc.close();
@@ -323,13 +370,24 @@ jQuery.atmosphere = function()
         executeWebSocket : function()
         {
             var request = jQuery.atmosphere.request;
+            var success = false;
             jQuery.atmosphere.log(logLevel, ["Invoking executeWebSocket"]);
             jQuery.atmosphere.response.transport = "websocket";
             var url = jQuery.atmosphere.request.url;
             var callback = jQuery.atmosphere.request.callback;
+
+            if (url.indexOf("http") == -1 && url.indexOf("ws") == -1) {
+                url = jQuery.atmosphere.parseUri(document.location, url);
+            }
             var location = url.replace('http:', 'ws:').replace('https:', 'wss:');
 
-            var websocket = new WebSocket(location);
+            var websocket = null;
+            if (jQuery.atmosphere.request.webSocketImpl != null) {
+                websocket = jQuery.atmosphere.request.webSocketImpl;
+            } else {
+                websocket = new WebSocket(location);
+            }
+
             jQuery.atmosphere.websocket = websocket;
 
             jQuery.atmosphere.response.push = function (url)
@@ -343,25 +401,19 @@ jQuery.atmosphere = function()
                     jQuery.atmosphere.log(logLevel, ["Websocket failed. Downgrading to Comet
and resending " + data]);
                     // Websocket is not supported, reconnect using the fallback transport.
                     request.transport = request.fallbackTransport;
+                    jQuery.atmosphere.response.transport = request.fallbackTransport;
                     jQuery.atmosphere.request = request;
                     jQuery.atmosphere.executeRequest();
 
-                    // Repost the data.
-                    jQuery.atmosphere.request.suspend = false;
-                    jQuery.atmosphere.request.method = 'POST';
-                    jQuery.atmosphere.request.data = data;
-                    jQuery.atmosphere.response.state = 'messageReceived';
-                    jQuery.atmosphere.response.transport = request.fallbackTransport;
-                    jQuery.atmosphere.publish(url, null, jQuery.atmosphere.request);
-
                     ws.onclose = function(message) {
-                    }
+                    };
                     ws.close();
                 }
             };
 
             websocket.onopen = function(message)
             {
+                success = true;
                 jQuery.atmosphere.response.state = 'openning';
                 jQuery.atmosphere.invokeCallback(jQuery.atmosphere.response);
             };
@@ -388,8 +440,19 @@ jQuery.atmosphere = function()
 
             websocket.onclose = function(message)
             {
-                jQuery.atmosphere.response.state = 'closed';
-                jQuery.atmosphere.invokeCallback(jQuery.atmosphere.response);
+                if (!success) {
+                    var data = jQuery.atmosphere.request.data;
+                    jQuery.atmosphere.log(logLevel, ["Websocket failed. Downgrading to Comet
and resending " + data]);
+                    // Websocket is not supported, reconnect using the fallback transport.
+                    request.transport = request.fallbackTransport;
+                    jQuery.atmosphere.response.transport = request.fallbackTransport;
+
+                    jQuery.atmosphere.request = request;
+                    jQuery.atmosphere.executeRequest();
+                } else {
+                    jQuery.atmosphere.response.state = 'closed';
+                    jQuery.atmosphere.invokeCallback(jQuery.atmosphere.response);
+                }
             };
         }
         ,
@@ -430,6 +493,7 @@ jQuery.atmosphere = function()
                 connected: false,
                 timeout: 60000,
                 method: 'POST',
+                contentType : '',
                 headers: {},
                 cache: true,
                 async: true,
@@ -440,7 +504,7 @@ jQuery.atmosphere = function()
                 data : '',
                 suspend : false,
                 maxRequest : 60,
-                logLevel :  'info',
+                logLevel : 'info',
                 requestCount : 0,
                 transport: 'polling'
             }, request);
@@ -528,6 +592,111 @@ jQuery.atmosphere = function()
                 log('debug', arguments);
             }
         }
+        ,
+
+        close : function()
+        {
+            jQuery.atmosphere.closeSuspendedConnection();
+        },
+
+
+        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 )
+                {
+                    var p = path.lastIndexOf( '/', path.lastIndexOf( '/' ) - 1 );
+                    if ( p>= 0 )
+                    {
+                        path = path.substring( 0, p + 1 );
+                    }
+                    uri = uri.substring( 3 );
+                }
+                path = path + uri;
+            }
+
+            var uri = protocol + '//' + host + path;
+            var div = '?';
+            for ( var key in parameters )
+            {
+                uri += div + key + '=' + encodeURIComponent( parameters[key] );
+                div = '&';
+            }
+            return uri;
+        }
+
     }
 
 }



Mime
View raw message