You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@mina.apache.org by ng...@apache.org on 2010/08/12 18:57:19 UTC

svn commit: r984863 - in /mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src: main/java/org/apache/vysper/xmpp/extension/xep0124/ test/java/org/apache/vysper/xmpp/extension/xep0124/

Author: ngn
Date: Thu Aug 12 16:57:19 2010
New Revision: 984863

URL: http://svn.apache.org/viewvc?rev=984863&view=rev
Log:
Implementation of the ack attribute (VYSPER-232, by Bogdan Pistol)

Added:
    mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshRequest.java
Modified:
    mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContext.java
    mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandler.java
    mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContextTest.java
    mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandlerTest.java

Modified: mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContext.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContext.java?rev=984863&r1=984862&r2=984863&view=diff
==============================================================================
--- mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContext.java (original)
+++ mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContext.java Thu Aug 12 16:57:19 2010
@@ -21,8 +21,8 @@ package org.apache.vysper.xmpp.extension
 
 import java.util.LinkedList;
 import java.util.Queue;
-
-import javax.servlet.http.HttpServletRequest;
+import java.util.SortedMap;
+import java.util.TreeMap;
 
 import org.apache.vysper.xml.fragment.Renderer;
 import org.apache.vysper.xmpp.protocol.SessionStateHolder;
@@ -61,12 +61,18 @@ public class BoshBackedSessionContext ex
     private int wait = 60;
 
     private int hold = 1;
-
+    
+    private Long highestAcknowledgedRid = null;
+    
+    private Long currentProcessingRequest = null;
+    
     /*
      * Keeps the suspended HTTP requests (does not respond to them) until the server has an asynchronous message
      * to send to the client. (Comet HTTP Long Polling technique - described in XEP-0124)
+     * 
+     * The BOSH requests sorted by their RIDs.
      */
-    private Queue<HttpServletRequest> requestQueue;
+    private SortedMap<Long, BoshRequest> requestsWindow;
 
     /*
      * Keeps the asynchronous messages sent from server that cannot be delivered to the client because there are
@@ -86,9 +92,18 @@ public class BoshBackedSessionContext ex
         sessionStateHolder.setState(SessionState.ENCRYPTED);
 
         this.boshHandler = boshHandler;
-        requestQueue = new LinkedList<HttpServletRequest>();
+        requestsWindow = new TreeMap<Long, BoshRequest>();
         delayedResponseQueue = new LinkedList<Stanza>();
     }
+    
+    /**
+     * Returns the highest RID that is received in a continuous (uninterrupted) sequence of RIDs.
+     * Higher RIDs can exist with gaps separating them from the highestAcknowledgedRid.
+     * @return the highest continuous RID received so far
+     */
+    public long getHighestAcknowledgedRid() {
+        return highestAcknowledgedRid;
+    }
 
     public SessionStateHolder getStateHolder() {
         return sessionStateHolder;
@@ -119,16 +134,18 @@ public class BoshBackedSessionContext ex
      * @param response The BOSH response to write
      */
     void write0(Stanza response) {
-        HttpServletRequest req = requestQueue.poll();
-        if (req == null) {
+        BoshRequest req;
+        if (requestsWindow.isEmpty() || requestsWindow.firstKey() > highestAcknowledgedRid) {
             delayedResponseQueue.offer(response);
             return;
+        } else {
+            req = requestsWindow.remove(requestsWindow.firstKey());
         }
-        BoshResponse boshResponse = getBoshResponse(response);
+        BoshResponse boshResponse = getBoshResponse(response, req.getRid().equals(highestAcknowledgedRid) ? null : highestAcknowledgedRid);
         if (LOGGER.isDebugEnabled()) {
             LOGGER.debug("BOSH writing response: {}", new String(boshResponse.getContent()));
         }
-        Continuation continuation = ContinuationSupport.getContinuation(req);
+        Continuation continuation = ContinuationSupport.getContinuation(req.getHttpServletRequest());
         continuation.setAttribute("response", boshResponse);
         continuation.resume();
     }
@@ -138,7 +155,7 @@ public class BoshBackedSessionContext ex
      */
     public void close() {
         // respond to all the queued HTTP requests with empty responses
-        while (!requestQueue.isEmpty()) {
+        while (!requestsWindow.isEmpty()) {
             write0(boshHandler.getEmptyResponse());
         }
         
@@ -267,21 +284,13 @@ public class BoshBackedSessionContext ex
      * expirations for the BOSH client while the current request expires.
      */
     synchronized private void requestExpired(Continuation continuation) {
-        HttpServletRequest req = (HttpServletRequest) continuation.getAttribute("request");
+        BoshRequest req = (BoshRequest) continuation.getAttribute("request");
         if (req == null) {
             LOGGER.warn("Continuation expired without having an associated request!");
             return;
         }
-        continuation.setAttribute("response", getBoshResponse(boshHandler.getEmptyResponse()));
-        for (;;) {
-            HttpServletRequest r = requestQueue.peek();
-            if (r == null) {
-                break;
-            }
+        while (!requestsWindow.isEmpty() && requestsWindow.firstKey() <= req.getRid()) {
             write0(boshHandler.getEmptyResponse());
-            if (r == req) {
-                break;
-            }
         }
     }
 
@@ -291,12 +300,28 @@ public class BoshBackedSessionContext ex
      * 
      * @param req the HTTP request
      */
-    public void addRequest(HttpServletRequest req) {
-        Continuation continuation = ContinuationSupport.getContinuation(req);
+    public void insertRequest(BoshRequest br) {
+        if (highestAcknowledgedRid != null && br.getRid() <= highestAcknowledgedRid || requestsWindow.containsKey(br.getRid())) {
+            // TODO: return the old response
+            return;
+        }
+        Continuation continuation = ContinuationSupport.getContinuation(br.getHttpServletRequest());
         continuation.setTimeout(wait * 1000);
         continuation.suspend();
-        continuation.setAttribute("request", req);
-        requestQueue.offer(req);
+        continuation.setAttribute("request", br);
+        requestsWindow.put(br.getRid(), br);
+        if (highestAcknowledgedRid == null) {
+            highestAcknowledgedRid = br.getRid();
+        }
+        for (;;) {
+            // update the highestAcknowledgedRid to the latest value
+            // it is possible to have higher RIDs than the highestAcknowledgedRid with a gap between them (e.g. lost client request)
+            if (requestsWindow.containsKey(highestAcknowledgedRid + 1)) {
+                highestAcknowledgedRid++;
+            } else {
+                break;
+            }
+        }
 
         // listen the continuation to be notified when the request expires
         continuation.addContinuationListener(new ContinuationListener() {
@@ -325,14 +350,37 @@ public class BoshBackedSessionContext ex
 
         // If there are more suspended enqueued requests than it is allowed by the BOSH 'hold' parameter,
         // than we release the oldest one by sending an empty response.
-        if (requestQueue.size() > hold) {
+        if (requestsWindow.size() > hold) {
             write0(boshHandler.getEmptyResponse());
         }
     }
 
-    private BoshResponse getBoshResponse(Stanza stanza) {
+    private BoshResponse getBoshResponse(Stanza stanza, Long ack) {
+        if (ack != null) {
+            stanza = boshHandler.addAck(stanza, ack);
+        }
         byte[] content = new Renderer(stanza).getComplete().getBytes();
         return new BoshResponse(contentType, content);
     }
 
+    /**
+     * Returns the next BOSH body to process.
+     * It is possible to have more than one BOSH body to process in the case where a lost request is resent by the client.
+     * @return the next (by RID order) body to process
+     */
+    public BoshRequest getNextRequest() {
+        if (requestsWindow.isEmpty()) {
+            return null;
+        }
+        if (currentProcessingRequest == null || currentProcessingRequest < requestsWindow.firstKey()) {
+            currentProcessingRequest = requestsWindow.firstKey();
+        }
+        if (currentProcessingRequest > highestAcknowledgedRid) {
+            return null;
+        } else {
+            currentProcessingRequest++;
+            return requestsWindow.get(currentProcessingRequest - 1);
+        }
+    }
+
 }

Modified: mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandler.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandler.java?rev=984863&r1=984862&r2=984863&view=diff
==============================================================================
--- mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandler.java (original)
+++ mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandler.java Thu Aug 12 16:57:19 2010
@@ -25,6 +25,7 @@ import java.util.concurrent.ConcurrentHa
 
 import javax.servlet.http.HttpServletRequest;
 
+import org.apache.vysper.xml.fragment.Attribute;
 import org.apache.vysper.xml.fragment.XMLElement;
 import org.apache.vysper.xmpp.protocol.NamespaceURIs;
 import org.apache.vysper.xmpp.server.ServerRuntimeContext;
@@ -77,67 +78,76 @@ public class BoshHandler {
     /**
      * Processes BOSH requests
      * @param httpRequest the HTTP request
-     * @param boshRequest the decoded BOSH request
+     * @param body the decoded BOSH request
      */
-    public void process(HttpServletRequest httpRequest, Stanza boshRequest) {
-        if (!boshRequest.getNamespaceURI().equalsIgnoreCase(NamespaceURIs.XEP0124_BOSH)) {
-            LOGGER.error("Invalid namespace for body wrapper '{}', must be '{}'!", boshRequest.getNamespaceURI(),
+    public void process(HttpServletRequest httpRequest, Stanza body) {
+        if (!body.getNamespaceURI().equalsIgnoreCase(NamespaceURIs.XEP0124_BOSH)) {
+            LOGGER.error("Invalid namespace for body wrapper '{}', must be '{}'!", body.getNamespaceURI(),
                     NamespaceURIs.XEP0124_BOSH);
             return;
         }
-        if (!boshRequest.getName().equalsIgnoreCase("body")) {
-            LOGGER.error("Invalid body wrapper '{}'!", boshRequest.getName());
+        if (!body.getName().equalsIgnoreCase("body")) {
+            LOGGER.error("Invalid body wrapper '{}'!", body.getName());
             return;
         }
-        if (boshRequest.getAttribute("rid") == null) {
+        if (body.getAttribute("rid") == null) {
             LOGGER.error("Invalid request that does not have a request identifier (rid) attribute!");
             return;
         }
-
-        if (boshRequest.getAttribute("sid") == null) {
+        BoshRequest br = new BoshRequest(httpRequest, body, Long.parseLong(body.getAttributeValue("rid")));
+        if (body.getAttribute("sid") == null) {
             // the session creation request (first request) does not have a "sid" attribute
             try {
-                createSession(httpRequest, boshRequest);
+                createSession(br);
             } catch (IOException e) {
                 LOGGER.error("Exception thrown while processing the session creation request", e);
                 return;
             }
         } else {
-            BoshBackedSessionContext session = sessions.get(boshRequest.getAttributeValue("sid"));
+            BoshBackedSessionContext session = sessions.get(body.getAttributeValue("sid"));
             if (session == null) {
                 LOGGER.warn("Received an invalid 'sid'!");
                 return;
             }
             synchronized (session) {
-                session.addRequest(httpRequest);
-                processSession(session, boshRequest);
+                session.insertRequest(br);
+                for (;;) {
+                    // When a request from the user comes in, it is possible that the request fills a gap
+                    // created by previous lost request, and it could be possible to process more than the current request
+                    // continuing with all the adjacent requests.
+                    br = session.getNextRequest();
+                    if (br == null) {
+                        break;
+                    }
+                    processSession(session, br);
+                }
             }
         }
     }
     
-    private void processSession(BoshBackedSessionContext session, Stanza boshRequest) {
+    private void processSession(BoshBackedSessionContext session, BoshRequest br) {
         if (session.getState() == SessionState.ENCRYPTED) {
-            if (boshRequest.getInnerElements().isEmpty()) {
+            if (br.getBody().getInnerElements().isEmpty()) {
                 // session needs authentication
                 return;
             }
-            for (XMLElement element : boshRequest.getInnerElements()) {
+            for (XMLElement element : br.getBody().getInnerElements()) {
                 if (element.getNamespaceURI().equals(NamespaceURIs.URN_IETF_PARAMS_XML_NS_XMPP_SASL)) {
                     processStanza(session, element);
                 }
             }
         } else if (session.getState() == SessionState.AUTHENTICATED) {
-            if ("true".equals(boshRequest.getAttributeValue(NamespaceURIs.URN_XMPP_XBOSH, "restart"))) {
+            if ("true".equals(br.getBody().getAttributeValue(NamespaceURIs.URN_XMPP_XBOSH, "restart"))) {
                 // restart request
                 session.write0(getRestartResponse());
             } else {
                 // any other request
-                for (XMLElement element : boshRequest.getInnerElements()) {
+                for (XMLElement element : br.getBody().getInnerElements()) {
                     processStanza(session, element);
                 }
                 
                 // if the client solicited the session termination
-                if ("terminate".equals(boshRequest.getAttributeValue("type"))) {
+                if ("terminate".equals(br.getBody().getAttributeValue("type"))) {
                     terminateSession(session);
                 }
             }
@@ -162,28 +172,28 @@ public class BoshHandler {
                 session.getStateHolder());
     }
 
-    private void createSession(HttpServletRequest httpRequest, Stanza boshRequest) throws IOException {
+    private void createSession(BoshRequest br) throws IOException {
         BoshBackedSessionContext session = new BoshBackedSessionContext(this, serverRuntimeContext);
-        if (boshRequest.getAttribute("content") != null) {
-            session.setContentType(boshRequest.getAttributeValue("content"));
+        if (br.getBody().getAttribute("content") != null) {
+            session.setContentType(br.getBody().getAttributeValue("content"));
         }
-        if (boshRequest.getAttribute("wait") != null) {
-            int wait = Integer.parseInt(boshRequest.getAttributeValue("wait"));
+        if (br.getBody().getAttribute("wait") != null) {
+            int wait = Integer.parseInt(br.getBody().getAttributeValue("wait"));
             session.setWait(wait);
         }
-        if (boshRequest.getAttribute("hold") != null) {
-            int hold = Integer.parseInt(boshRequest.getAttributeValue("hold"));
+        if (br.getBody().getAttribute("hold") != null) {
+            int hold = Integer.parseInt(br.getBody().getAttributeValue("hold"));
             session.setHold(hold);
         }
-        if (boshRequest.getAttribute("ver") != null) {
-            String ver = boshRequest.getAttributeValue("ver");
+        if (br.getBody().getAttribute("ver") != null) {
+            String ver = br.getBody().getAttributeValue("ver");
             session.setBoshVersion(ver);
         }
-        if (boshRequest.getAttribute(NamespaceURIs.XML, "lang") != null) {
-            String lang = boshRequest.getAttributeValue(NamespaceURIs.XML, "lang");
+        if (br.getBody().getAttribute(NamespaceURIs.XML, "lang") != null) {
+            String lang = br.getBody().getAttributeValue(NamespaceURIs.XML, "lang");
             session.setXMLLang(lang);
         }
-        session.addRequest(httpRequest);
+        session.insertRequest(br);
         sessions.put(session.getSessionId(), session);
 
         session.write0(getSessionCreationResponse(session));
@@ -200,6 +210,10 @@ public class BoshHandler {
         body.addAttribute("ver", session.getBoshVersion());
         body.addAttribute("from", session.getServerJID().getFullQualifiedName());
         body.addAttribute("secure", "true");
+        
+        // adding the ack attribute here is needed because when responding to o request with the same RID (as is the case here)
+        // the ack would not be included on BoshBackedSessionContext#write0, but this first ack is required.
+        body.addAttribute("ack", Long.toString(session.getHighestAcknowledgedRid()));
 
         Stanza features = new ServerResponses().getFeaturesForAuthentication(serverRuntimeContext.getServerFeatures()
                 .getAuthenticationMethods());
@@ -266,5 +280,17 @@ public class BoshHandler {
         stanzaBuilder.addAttribute("type", "terminate");
         return stanzaBuilder.build();
     }
+    
+    public Stanza addAck(Stanza stanza, Long ack) {
+        StanzaBuilder stanzaBuilder = new StanzaBuilder("body", NamespaceURIs.XEP0124_BOSH);
+        for (Attribute attr : stanza.getAttributes()) {
+            stanzaBuilder.addAttribute(attr);
+        }
+        stanzaBuilder.addAttribute("ack", ack.toString());
+        for (XMLElement element : stanza.getInnerElements()) {
+            stanzaBuilder.addPreparedElement(element);
+        }
+        return stanzaBuilder.build();
+    }
 
 }

Added: mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshRequest.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshRequest.java?rev=984863&view=auto
==============================================================================
--- mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshRequest.java (added)
+++ mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/main/java/org/apache/vysper/xmpp/extension/xep0124/BoshRequest.java Thu Aug 12 16:57:19 2010
@@ -0,0 +1,83 @@
+/*
+ *  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
+ *
+ *  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.apache.vysper.xmpp.extension.xep0124;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.vysper.xmpp.stanza.Stanza;
+
+/**
+ * Wraps an HTTP request with its XML BOSH body.
+ * 
+ * @author The Apache MINA Project (dev@mina.apache.org)
+ */
+public class BoshRequest implements Comparable<BoshRequest> {
+
+    private final HttpServletRequest httpServletRequest;
+
+    private final Stanza body;
+
+    private final Long rid;
+
+    public BoshRequest(HttpServletRequest httpServletRequest, Stanza body, Long rid) {
+        this.httpServletRequest = httpServletRequest;
+        this.body = body;
+        this.rid = rid;
+    }
+
+    public HttpServletRequest getHttpServletRequest() {
+        return httpServletRequest;
+    }
+
+    public Stanza getBody() {
+        return body;
+    }
+
+    public Long getRid() {
+        return rid;
+    }
+
+    public int compareTo(BoshRequest br) {
+        return rid.compareTo(br.rid);
+    }
+
+    @Override
+    public int hashCode() {
+        return rid.hashCode();
+    }
+
+    @Override
+    public boolean equals(Object obj) {
+        if (this == obj)
+            return true;
+        if (obj == null)
+            return false;
+        if (getClass() != obj.getClass())
+            return false;
+        BoshRequest other = (BoshRequest) obj;
+        if (rid == null) {
+            if (other.rid != null)
+                return false;
+        } else if (!rid.equals(other.rid))
+            return false;
+        return true;
+    }
+
+}
\ No newline at end of file

Modified: mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContextTest.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContextTest.java?rev=984863&r1=984862&r2=984863&view=diff
==============================================================================
--- mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContextTest.java (original)
+++ mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshBackedSessionContextTest.java Thu Aug 12 16:57:19 2010
@@ -51,8 +51,6 @@ public class BoshBackedSessionContextTes
 
     private ServerRuntimeContext serverRuntimeContext;
 
-    private BoshBackedSessionContext boshBackedSessionContext;
-
     @Before
     public void setUp() throws Exception {
         mocksControl = createControl();
@@ -74,7 +72,7 @@ public class BoshBackedSessionContextTes
         expect(httpServletRequest.getAttribute(Continuation.ATTRIBUTE)).andReturn(continuation);
         expectLastCall().atLeastOnce();
         continuation.setTimeout(anyLong());
-        continuation.setAttribute("request", httpServletRequest);
+        continuation.setAttribute(eq("request"), EasyMock.<BoshRequest> notNull());
         continuation.suspend();
         continuation.resume();
         continuation.addContinuationListener(EasyMock.<ContinuationListener> anyObject());
@@ -82,12 +80,12 @@ public class BoshBackedSessionContextTes
         continuation.setAttribute(eq("response"), EasyMock.<BoshResponse> capture(captured));
         mocksControl.replay();
 
-        boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
-        boshBackedSessionContext.addRequest(httpServletRequest);
+        BoshBackedSessionContext boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
         Stanza body = new StanzaBuilder("body", NamespaceURIs.XEP0124_BOSH).build();
+        boshBackedSessionContext.insertRequest(new BoshRequest(httpServletRequest, body, 1L));
         boshBackedSessionContext.write0(body);
         mocksControl.verify();
-
+        
         BoshResponse boshResponse = captured.getValue();
         assertEquals(BoshServlet.XML_CONTENT_TYPE, boshResponse.getContentType());
         assertEquals(new Renderer(body).getComplete(), new String(boshResponse.getContent()));
@@ -96,17 +94,19 @@ public class BoshBackedSessionContextTes
     @Test
     public void testSetBoshVersion1() {
         mocksControl.replay();
-        boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
+        BoshBackedSessionContext boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
         boshBackedSessionContext.setBoshVersion("1.8");
         assertEquals("1.8", boshBackedSessionContext.getBoshVersion());
+        mocksControl.verify();
     }
 
     @Test
     public void testSetBoshVersion2() {
         mocksControl.replay();
-        boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
+        BoshBackedSessionContext boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
         boshBackedSessionContext.setBoshVersion("2.0");
         assertEquals("1.9", boshBackedSessionContext.getBoshVersion());
+        mocksControl.verify();
     }
 
     @Test
@@ -120,13 +120,15 @@ public class BoshBackedSessionContextTes
         expectLastCall().atLeastOnce();
         continuation.setTimeout(anyLong());
         continuation.suspend();
-        continuation.setAttribute("request", httpServletRequest);
+        continuation.setAttribute(eq("request"), EasyMock.<BoshRequest> notNull());
 
         Capture<ContinuationListener> listenerCaptured = new Capture<ContinuationListener>();
         continuation.addContinuationListener(EasyMock.<ContinuationListener> capture(listenerCaptured));
+        
+        BoshRequest br = new BoshRequest(httpServletRequest, body, 1L);
 
         // requestExpired
-        expect(continuation.getAttribute("request")).andReturn(httpServletRequest);
+        expect(continuation.getAttribute("request")).andReturn(br);
         Capture<BoshResponse> responseCaptured = new Capture<BoshResponse>();
         continuation.setAttribute(eq("response"), EasyMock.<BoshResponse> capture(responseCaptured));
 
@@ -134,12 +136,12 @@ public class BoshBackedSessionContextTes
         expectLastCall().atLeastOnce();
 
         // write0
-        continuation.setAttribute(eq("response"), EasyMock.<BoshResponse> anyObject());
         continuation.resume();
 
         mocksControl.replay();
-        boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
-        boshBackedSessionContext.addRequest(httpServletRequest);
+        BoshBackedSessionContext boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
+        
+        boshBackedSessionContext.insertRequest(br);
         listenerCaptured.getValue().onTimeout(continuation);
         mocksControl.verify();
 
@@ -160,12 +162,17 @@ public class BoshBackedSessionContextTes
         expectLastCall().atLeastOnce();
         continuation1.setTimeout(anyLong());
         continuation1.suspend();
-        continuation1.setAttribute("request", httpServletRequest1);
+        Capture<BoshRequest> br1 = new Capture<BoshRequest>();
+        continuation1.setAttribute(eq("request"), EasyMock.<BoshRequest> capture(br1));
         continuation2.setTimeout(anyLong());
         continuation2.suspend();
-        continuation2.setAttribute("request", httpServletRequest2);
+        Capture<BoshRequest> br2 = new Capture<BoshRequest>();
+        continuation2.setAttribute(eq("request"), EasyMock.<BoshRequest> capture(br2));
         continuation1.addContinuationListener(EasyMock.<ContinuationListener> anyObject());
         continuation2.addContinuationListener(EasyMock.<ContinuationListener> anyObject());
+        
+        Stanza body = new StanzaBuilder("body", NamespaceURIs.XEP0124_BOSH).build();
+        expect(boshHandler.addAck(eq(body), EasyMock.anyLong())).andReturn(body);
 
         // write0
         Capture<BoshResponse> captured = new Capture<BoshResponse>();
@@ -173,14 +180,17 @@ public class BoshBackedSessionContextTes
         continuation1.resume();
 
         mocksControl.replay();
-        boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
+        BoshBackedSessionContext boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
 
         boshBackedSessionContext.setHold(2);
-        boshBackedSessionContext.addRequest(httpServletRequest1);
-        boshBackedSessionContext.addRequest(httpServletRequest2);
-        Stanza body = new StanzaBuilder("body", NamespaceURIs.XEP0124_BOSH).build();
+        // consecutive writes with RID 1 and 2
+        boshBackedSessionContext.insertRequest(new BoshRequest(httpServletRequest1, body, 1L));
+        boshBackedSessionContext.insertRequest(new BoshRequest(httpServletRequest2, body, 2L));
         boshBackedSessionContext.write0(body);
         mocksControl.verify();
+        
+        assertEquals(httpServletRequest1, br1.getValue().getHttpServletRequest());
+        assertEquals(httpServletRequest2, br2.getValue().getHttpServletRequest());
 
         assertEquals(new Renderer(body).getComplete(), new String(captured.getValue().getContent()));
         assertEquals(BoshServlet.XML_CONTENT_TYPE, captured.getValue().getContentType());
@@ -194,7 +204,7 @@ public class BoshBackedSessionContextTes
         expectLastCall().atLeastOnce();
         continuation.setTimeout(anyLong());
         continuation.suspend();
-        continuation.setAttribute("request", httpServletRequest);
+        continuation.setAttribute(eq("request"), EasyMock.<BoshRequest> notNull());
 
         continuation.addContinuationListener(EasyMock.<ContinuationListener> anyObject());
 
@@ -209,10 +219,10 @@ public class BoshBackedSessionContextTes
 
         mocksControl.replay();
 
-        boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
+        BoshBackedSessionContext boshBackedSessionContext = new BoshBackedSessionContext(boshHandler, serverRuntimeContext);
         boshBackedSessionContext.write0(body1);
         boshBackedSessionContext.write0(body2);
-        boshBackedSessionContext.addRequest(httpServletRequest);
+        boshBackedSessionContext.insertRequest(new BoshRequest(httpServletRequest, body1, 1L));
         mocksControl.verify();
     }
 

Modified: mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandlerTest.java
URL: http://svn.apache.org/viewvc/mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandlerTest.java?rev=984863&r1=984862&r2=984863&view=diff
==============================================================================
--- mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandlerTest.java (original)
+++ mina/vysper/trunk/server/extensions/xep0124-xep0206-bosh/src/test/java/org/apache/vysper/xmpp/extension/xep0124/BoshHandlerTest.java Thu Aug 12 16:57:19 2010
@@ -32,7 +32,6 @@ import java.util.Collections;
 
 import javax.servlet.http.HttpServletRequest;
 
-import org.apache.vysper.xml.fragment.Renderer;
 import org.apache.vysper.xml.fragment.XMLElement;
 import org.apache.vysper.xmpp.addressing.EntityImpl;
 import org.apache.vysper.xmpp.authorization.SASLMechanism;
@@ -85,7 +84,8 @@ public class BoshHandlerTest {
         expect(httpServletRequest.getAttribute(Continuation.ATTRIBUTE)).andReturn(continuation);
         expectLastCall().atLeastOnce();
         continuation.setTimeout(anyLong());
-        continuation.setAttribute("request", httpServletRequest);
+        Capture<BoshRequest> br = new Capture<BoshRequest>();
+        continuation.setAttribute(eq("request"), EasyMock.<BoshRequest> capture(br));
         continuation.addContinuationListener(EasyMock.<ContinuationListener> anyObject());
         continuation.suspend();
 
@@ -101,6 +101,9 @@ public class BoshHandlerTest {
         Stanza boshRequest = createSessionRequest();
         boshHandler.process(httpServletRequest, boshRequest);
         mocksControl.verify();
+        
+        assertEquals(httpServletRequest, br.getValue().getHttpServletRequest());
+        assertEquals(boshRequest, br.getValue().getBody());
 
         Stanza response = new XMLUtil(new String(captured.getValue().getContent())).parse();
         assertNotNull(response);
@@ -126,7 +129,7 @@ public class BoshHandlerTest {
         expectLastCall().atLeastOnce();
         continuation.setTimeout(anyLong());
         continuation.suspend();
-        continuation.setAttribute("request", httpServletRequest);
+        continuation.setAttribute(eq("request"), EasyMock.<BoshRequest> capture(br));
         continuation.addContinuationListener(EasyMock.<ContinuationListener> anyObject());
         StanzaProcessor stanzaProcessor = mocksControl.createMock(StanzaProcessor.class);
         expect(serverRuntimeContext.getStanzaProcessor()).andReturn(stanzaProcessor);
@@ -137,6 +140,10 @@ public class BoshHandlerTest {
         boshRequest = createSaslRequest();
         boshHandler.process(httpServletRequest, boshRequest);
         mocksControl.verify();
+        
+        assertEquals(httpServletRequest, br.getValue().getHttpServletRequest());
+        assertEquals(boshRequest, br.getValue().getBody());
+        
         Stanza stanza = stanzaCaptured.getValue();
         assertNotNull(stanza);
         assertEquals("auth", stanza.getName());