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;
+        }
+
     }
 
 }


Reply via email to