You are viewing a plain text version of this content. The canonical link for it is here.
Posted to scm@geronimo.apache.org by ri...@apache.org on 2007/12/12 17:13:35 UTC

svn commit: r603657 - in /geronimo/sandbox/AsyncHttpClient/src: main/java/org/apache/ahc/codec/SessionCache.java test/java/org/apache/ahc/ConnectionReuseTest.java

Author: rickmcguire
Date: Wed Dec 12 08:13:35 2007
New Revision: 603657

URL: http://svn.apache.org/viewvc?rev=603657&view=rev
Log:
GERONIMO-3686 AsyncHttpClient does not reuse connection even if connections are persistent

Missed adding the new files to the repository.   


Added:
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/SessionCache.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ConnectionReuseTest.java   (with props)

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/SessionCache.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/SessionCache.java?rev=603657&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/SessionCache.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/SessionCache.java Wed Dec 12 08:13:35 2007
@@ -0,0 +1,133 @@
+/*
+ *  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.ahc.codec;
+
+import java.net.InetSocketAddress;
+import java.util.Queue;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.ConcurrentLinkedQueue;
+import java.util.concurrent.ConcurrentMap;
+
+import org.apache.mina.common.IoSession;
+
+/**
+ * Class that provides access to cached sessions.  IoSessions are cached using
+ * the host and the port.  This class is thread safe.
+ */
+public final class SessionCache {
+    private static final SessionCache theInstance = new SessionCache();
+    
+    private final ConcurrentMap<String,Queue<IoSession>> cachedSessions = 
+            new ConcurrentHashMap<String,Queue<IoSession>>();
+    
+    /**
+     * Returns a singleton instance of the session cache.
+     */
+    public static SessionCache getInstance() {
+        return theInstance;
+    }
+    
+    private SessionCache() {}
+
+    /**
+     * Returns an IoSession that is connected and considered usable.  Note that
+     * this is still on a best-effort basis, and there is no guarantee that the
+     * connection can be used without errors, although it should be usually
+     * safe to use it.
+     * 
+     * @param msg the message for which to look up an active session.
+     * @throws IllegalArgumentException if a null request message was passed in.
+     * @return an active IoSession, or null if none are found.
+     */
+    public IoSession getActiveSession(HttpRequestMessage msg) {
+        if (msg == null) {
+            throw new IllegalArgumentException("null request was passed in");
+        }
+        
+        Queue<IoSession> queue = cachedSessions.get(getKey(msg));
+        if (queue == null) {
+            return null;
+        }
+        
+        IoSession cached = null;
+        while ((cached = queue.poll()) != null) {
+        	// see if the session is usable
+            if (cached.isConnected() && !cached.isClosing()) {
+                return cached;
+            }
+        }
+        return null;
+    }
+    
+    /**
+     * Caches the given session using its remote host and port information.
+     * 
+     * @param session IoSession to cache
+     * @throws IllegalArgumentException if a null session was passed in.
+     */
+    void cacheSession(IoSession session) {
+        if (session == null) {
+            throw new IllegalArgumentException("null session was passed in");
+        }
+        
+        String key = getKey((InetSocketAddress)session.getRemoteAddress());
+        Queue<IoSession> newQueue = new ConcurrentLinkedQueue<IoSession>();
+        Queue<IoSession> queue = cachedSessions.putIfAbsent(key, newQueue);
+        if (queue == null) {
+            // the value was previously empty
+            queue = newQueue;
+        }
+        // add it to the queue
+        queue.offer(session);
+    }
+    
+    /**
+     * Removes the given session from the cache if it is in the cache.
+     * 
+     * @param session IoSession to remove from the cache
+     * @throws IllegalArgumentException if a null session was passed in
+     */
+    void removeSession(IoSession session) {
+        if (session == null) {
+            throw new IllegalArgumentException("null session was passed in");
+        }
+        
+        String key = getKey((InetSocketAddress)session.getRemoteAddress());
+        Queue<IoSession> queue = cachedSessions.get(key);
+        if (queue != null) {
+            queue.remove(session);
+        }
+    }
+    
+    private String getKey(HttpRequestMessage msg) {
+        return getKey(msg.getHost(), msg.getPort());
+    }
+    
+    private String getKey(InetSocketAddress remote) {
+        return getKey(remote.getHostName(), remote.getPort());
+    }
+    
+    /**
+     * The key is of the form "host:port".
+     */
+    private String getKey(String host, int port) {
+        return new StringBuilder(host).append(':').append(port).toString();
+    }
+}

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/SessionCache.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/SessionCache.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/SessionCache.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ConnectionReuseTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ConnectionReuseTest.java?rev=603657&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ConnectionReuseTest.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ConnectionReuseTest.java Wed Dec 12 08:13:35 2007
@@ -0,0 +1,105 @@
+/*
+ *  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.ahc;
+
+import java.net.URL;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.apache.ahc.codec.HttpRequestMessage;
+import org.apache.ahc.codec.HttpResponseMessage;
+
+public class ConnectionReuseTest extends AbstractTest {
+    // variable that keeps count of session close's
+    private final AtomicInteger closeCount = new AtomicInteger(0);
+    
+    // It is important that this test case contains these methods in this order.
+    // It is because to test connection reuse we need to keep the embedded 
+    // server running while connections are reused.  However, AbstractTest 
+    // starts and tears down the server around each test method.
+    public void testConnectionReuse() throws Exception {
+        // reset the count
+        closeCount.set(0);
+        Future<HttpResponseMessage> future = 
+                submitRequest("http://localhost:8282/", true, new SessionCloseCounter());
+
+        HttpResponseMessage msg = future.get();
+        assertEquals("\nHello World!", msg.getStringContent());
+        
+        // do another request for the same host
+        future = submitRequest("http://localhost:8282/params.jsp", true, 
+                new SessionCloseCounter());
+
+        msg = future.get();
+        assertEquals("Test One Test Two", msg.getStringContent());
+        
+        // check that I got zero close at this point
+        assertEquals(0, closeCount.get());
+    }
+    
+    public void testConnectionClose() throws Exception {
+        // reset the count
+        closeCount.set(0);
+        Future<HttpResponseMessage> future = 
+                submitRequest("http://localhost:8282/", false, new SessionCloseCounter());
+
+        HttpResponseMessage msg = future.get();
+        assertEquals("\nHello World!", msg.getStringContent());
+        
+        // do another request for the same host
+        future = submitRequest("http://localhost:8282/params.jsp", false, 
+                new SessionCloseCounter());
+
+        msg = future.get();
+        assertEquals("Test One Test Two", msg.getStringContent());
+        
+        // give it a bit of time to catch up
+        Thread.sleep(500L);
+        
+        // check that I got close count of 2 at this point
+        assertEquals(2, closeCount.get());
+    }
+    
+    private Future<HttpResponseMessage> submitRequest(String url, 
+                                                      boolean reuseConnection,
+                                                      AsyncHttpClientCallback cb) 
+            throws Exception {
+        HttpRequestMessage request = new HttpRequestMessage(new URL(url), cb);
+
+        request.setParameter("TEST1", "Test One");
+        request.setParameter("TEST2", "Test Two");
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        ahc.setReuseConnection(reuseConnection);
+        ahc.setTcpNoDelay(true);
+        return ahc.sendRequest(request);
+    }
+    
+    private class SessionCloseCounter implements AsyncHttpClientCallback {
+        public void onClosed() {
+            System.out.println("onClosed()");
+            // increment the counter on every close
+            closeCount.incrementAndGet();
+        }
+
+        public void onException(Throwable cause) {}
+        public void onResponse(HttpResponseMessage message) {}
+        public void onTimeout() {}
+    }
+}

Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ConnectionReuseTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ConnectionReuseTest.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/ConnectionReuseTest.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain