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 2008/02/01 19:26:44 UTC

svn commit: r617593 [1/2] - in /geronimo/sandbox/async-http-client-mina2: ./ src/main/java/org/apache/ahc/ src/main/java/org/apache/ahc/auth/ src/main/java/org/apache/ahc/codec/ src/main/java/org/apache/ahc/proxy/ src/main/java/org/apache/ahc/util/ src...

Author: rickmcguire
Date: Fri Feb  1 10:26:11 2008
New Revision: 617593

URL: http://svn.apache.org/viewvc?rev=617593&view=rev
Log:
GERONIMO-3801 Merge Mina 1.1.5 AsyncHttpClient into Mina 2.0 based version.


Added:
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyFilter.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/CountingMonitor.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/EventDispatcher.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringEvent.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringListener.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/TimeMonitor.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/test/java/org/apache/ahc/MonitoringTest.java   (with props)
    geronimo/sandbox/async-http-client-mina2/src/test/java/org/apache/ahc/ProxyTest.java   (with props)
Modified:
    geronimo/sandbox/async-http-client-mina2/pom.xml
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/AsyncHttpClient.java
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpIoHandler.java
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/ResponseFuture.java
    geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/SessionCache.java
    geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/index.jsp
    geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/timeout.jsp
    geronimo/sandbox/async-http-client-mina2/src/test/java/org/apache/ahc/AsyncHttpClientTest.java
    geronimo/sandbox/async-http-client-mina2/src/test/java/org/apache/ahc/AsyncHttpClientWithFutureTest.java
    geronimo/sandbox/async-http-client-mina2/src/test/java/org/apache/ahc/AuthTest.java
    geronimo/sandbox/async-http-client-mina2/src/test/java/org/apache/ahc/ConnectionReuseTest.java
    geronimo/sandbox/async-http-client-mina2/src/test/java/org/apache/ahc/RetryTest.java

Modified: geronimo/sandbox/async-http-client-mina2/pom.xml
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/pom.xml?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/pom.xml (original)
+++ geronimo/sandbox/async-http-client-mina2/pom.xml Fri Feb  1 10:26:11 2008
@@ -125,6 +125,10 @@
                 <artifactId>maven-surefire-plugin</artifactId>
                 <configuration>
                     <forkMode>pertest</forkMode>
+                    <excludes>
+                        <exclude>**/ProxyTest.java</exclude>
+                        <exclude>**/AbstractTest.java</exclude>
+                    </excludes>
                 </configuration>
             </plugin>
             <plugin>

Modified: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/AsyncHttpClient.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/AsyncHttpClient.java?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/AsyncHttpClient.java (original)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/AsyncHttpClient.java Fri Feb  1 10:26:11 2008
@@ -20,6 +20,9 @@
 package org.apache.ahc;
 
 import java.net.InetSocketAddress;
+import java.net.MalformedURLException;
+import java.net.ProtocolException;
+import java.net.URL;
 import java.security.GeneralSecurityException;
 import java.util.concurrent.BlockingQueue;
 import java.util.concurrent.Executor;
@@ -33,8 +36,12 @@
 import org.apache.ahc.codec.HttpRequestMessage;
 import org.apache.ahc.codec.ResponseFuture;
 import org.apache.ahc.codec.SessionCache;
+import org.apache.ahc.proxy.ProxyFilter;
 import org.apache.ahc.ssl.TrustManagerFactoryImpl;
 import org.apache.ahc.util.AsyncHttpClientException;
+import org.apache.ahc.util.EventDispatcher;
+import org.apache.ahc.util.MonitoringEvent;
+import org.apache.ahc.util.MonitoringListener;
 import org.apache.mina.common.*;
 import org.apache.mina.filter.ssl.SslFilter;
 import org.apache.mina.filter.codec.ProtocolCodecFilter;
@@ -52,9 +59,6 @@
  */
 public class AsyncHttpClient {
 
-    /** The Constant DEFAULT_REUSE_CONNECTION. */
-    public static final boolean DEFAULT_REUSE_CONNECTION = false;
-    
     /** The Constant DEFAULT_CONNECTION_TIMEOUT. */
     public static final int DEFAULT_CONNECTION_TIMEOUT = 30000;
 
@@ -110,9 +114,9 @@
     /** The HttpIoHandler handler. */
     private final HttpIoHandler handler;
 
-    /** connection reuse option */
-    private volatile boolean reuseConnection = DEFAULT_REUSE_CONNECTION;
-    
+    /** The cache for session reuse */
+    private SessionCache sessionCache; 
+
     /** The Reuse Address Socket Parameter. */
     private boolean reuseAddress = DEFAULT_REUSE_ADDRESS;
 
@@ -140,24 +144,14 @@
     /** flag to make this as having been disposed of */
     private boolean destroyed = false; 
 
-    /**
-     * Returns if it reuses established connections for more requests.
-     * 
-     * @return true if it reuses connections
-     */
-    public boolean isReuseConnection() {
-        return reuseConnection;
-    }
-    
-    /**
-     * Sets if it should reuse established connections for more requests.
-     * 
-     * @param reuseConnection the new value
-     */
-    public void setReuseConnection(boolean reuseConnection) {
-        this.reuseConnection = reuseConnection;
-    }
+    /** a dispatcher for dispatching monitoring events */
+    private EventDispatcher eventDispatcher; 
     
+    public static final String SSL_FILTER = "SSL";
+    public static final String PROTOCOL_FILTER = "protocolFilter";
+    public static final String PROXY_FILTER = "proxyFilter";
+    public static final String EVENT_THREAD_POOL_FILTER = "eventThreadPoolFilter";
+
     /**
      * Checks if is reuse address.
      *
@@ -373,25 +367,48 @@
 
         threadPool = executor;
 
-        if (scheduler == null)
+        if (scheduler == null) {
             handler = new HttpIoHandler();
-        else
+        }
+        else {
             handler = new HttpIoHandler(scheduler);
+        }
 
-        if (threadPool == null)
+        if (threadPool == null) {
             connector = new NioSocketConnector();
-        else{
-        //    connector = new NioSocketConnector((Runtime.getRuntime().availableProcessors(), threadPool);
+        }
+        else {
             connector = new NioSocketConnector(threadPool, new SimpleIoProcessorPool(NioProcessor.class));
         }
-
         connector.setHandler(handler);
-
         applyConnectionTimeout();
-//        connector.setWorkerTimeout(1);
-
     }
 
+    
+    /**
+     * Set the session cache that should be used for 
+     * connection reuse.
+     * 
+     * @param cache  The new session cache.  If null, this will disable
+     *               future connection reuse.
+     */
+    public void setSessionCache(SessionCache cache) {
+        sessionCache = cache; 
+        // our I/O Handler instance needs to be fitted with the same 
+        // cache
+        handler.setSessionCache(cache); 
+    }
+    
+    /**
+     * Retrieve the session cache used for storing 
+     * connections for reuse. 
+     * 
+     * @return The current session cache for the client. 
+     */
+    public SessionCache getSessionCache() {
+        return sessionCache; 
+    }
+    
     /**
      * Sends a request.  The call is non-blocking, and returns a future object
      * with which the caller can synchronize on the completion of the request.
@@ -431,6 +448,11 @@
             throw new IllegalStateException("AsyncHttpClient has been destroyed and cannot be reused.");
         }
         
+        // set the request start time
+        message.setRequestStartTime();
+        // notify any interesting parties that this is starting 
+        notifyMonitoringListeners(MonitoringEvent.REQUEST_STARTED, message); 
+        
         // we need to provide a new result future and associate it with the
         // request unless it already exists (i.e. sendRequest() is called
         // multiple times for the request)
@@ -441,20 +463,33 @@
         // *IF* connection reuse is enabled, we should see if we have a cached 
         // connection first; if not, always open a new one
         ConnectFuture future = null;
-        if (reuseConnection) {
+        if (!message.isProxyEnabled()) {
+            SessionCache cache = getSessionCache(); 
+            if (cache != null) {
             future = getCachedConnection(message);
         } else {
             // add the Connection close header explicitly
             message.setHeader(HttpDecoder.CONNECTION, HttpDecoder.CLOSE);
         }
+        }
         
         // if no cached connection is found or keep-alive is disabled, force a
         // new connection
         if (future == null) {
+            // set the connect start time
+            message.setConnectStartTime();
+            // NB:  We broadcast this here rather than in open connection to avoid 
+            // having a connection retry result in both a CONNECTION_ATTEMPTED and 
+            // CONNECTION_RETRIED event getting dispatched. 
+            notifyMonitoringListeners(MonitoringEvent.CONNECTION_ATTEMPTED, message); 
             future = openConnection(message);
         }
         ResponseFuture response = message.getResponseFuture();
-        future.addListener(new FutureListener(message, response, connectionRetries));
+        FutureListener listener = 
+                message.isProxyEnabled() ? 
+                        new ProxyFutureListener(message, response) :
+                        new FutureListener(message, response);
+        future.addListener(listener);
         return response;
     }
     
@@ -468,21 +503,49 @@
      *                 attempt.  This should be one less than the count
      *                 used for the previous attempt.
      */
-    private void retryConnection(HttpRequestMessage message, ResponseFuture response, int retries) {
+    private void retryConnection(HttpRequestMessage message, ResponseFuture response, FutureListener listener) {
+        // set the connect start time again
+        message.setConnectStartTime();
+        notifyMonitoringListeners(MonitoringEvent.CONNECTION_RETRIED, message); 
         ConnectFuture future = openConnection(message);
-        future.addListener(new FutureListener(message, response, retries));
+        future.addListener(listener);
     }
     
+    /**
+     * Open the appropriate connection for this message.
+     * This will either open a direct connection or connect 
+     * to the configured proxy server.
+     * 
+     * @param message The message getting sent.  This defines the target
+     *                location and also holds the proxy configuration.
+     * 
+     * @return A ConnectFuture instance for managing the connection.
+     */
     private ConnectFuture openConnection(HttpRequestMessage message) {
-        return connector.connect((new InetSocketAddress(message.getHost(), message.getPort())));
+        InetSocketAddress remote = 
+                message.isProxyEnabled() ?
+                        message.getProxyConfiguration().getProxyAddress(message.getUrl()) :
+                        new InetSocketAddress(message.getHost(), message.getPort());
+        return connector.connect(remote);
     }
     
+    /**
+     * Attempt to get a connection from the session cache.
+     * 
+     * @param message The message we're sending.
+     * 
+     * @return A cached connection.  This returns null if there's
+     *         no available connection for the target location.
+     */
     private ConnectFuture getCachedConnection(HttpRequestMessage message) {
-        IoSession cached = SessionCache.getInstance().getActiveSession(message);
+        IoSession cached = sessionCache.getActiveSession(message);
         if (cached == null) {
             return null;
         }
 
+        // clear the connect start time to prevent misreporting
+        message.clearConnectStartTime();
+        notifyMonitoringListeners(MonitoringEvent.CONNECTION_REUSED, message); 
         // create a containing future object and set the session right away
         ConnectFuture future = new DefaultConnectFuture();
         future.setSession(cached);
@@ -534,23 +597,61 @@
     }
 
     /**
+     * Add a statistics listener to this client object.
+     * 
+     * @param listener The listener to add.
+     */
+    public void addMonitoringListener(MonitoringListener listener) {
+        synchronized (this) {
+            // we've deferred creation until we have someone listening 
+            if (eventDispatcher == null) {
+                eventDispatcher = new EventDispatcher();
+            }
+        }
+        eventDispatcher.addListener(listener);
+    }
+
+    /**
+     * Remove a listener from the client. 
+     * 
+     * @param listener The listener to remove.
+     */
+    public void removeMonitoringListener(MonitoringListener listener) {
+        if (eventDispatcher != null) {
+            eventDispatcher.removeListener(listener);
+        }
+    }
+
+    /**
+     * Send a notification event to any monitoring listeners.
+     * 
+     * @param type    The type of event.
+     * @param request The HttpRequestMessage that triggerd the event.
+     */
+    public void notifyMonitoringListeners(int type, HttpRequestMessage request) {
+        // if there's no event dispatcher, no point in dispatching this
+        if (eventDispatcher == null) {
+            return;
+        }
+        
+        MonitoringEvent event = new MonitoringEvent(type, request);         
+        eventDispatcher.dispatchEvent(event); 
+    }
+
+    /**
      * The listener interface for receiving connection events. Its main purpose is to notify the
      * callback of any exceptions that may have occurred, or to handle the session and inject
      * the proper SSL filter if the connection is to be used for <code>https</code>.  If a good
      * connection occurs, it is also responsible for sending the request.
      */
     class FutureListener implements IoFutureListener {
-        static final String SSL_FILTER = "SSL";
-        static final String PROTOCOL_FILTER = "protocolFilter";
-        static final String EVENT_THREAD_POOL_FILTER = "eventThreadPoolFilter";
-
         /** The request. */
         final HttpRequestMessage request;
         /** The response future. */
         final ResponseFuture response;
         
         /** The count of additional retries for the connection */
-        int retries = 0; 
+        volatile int retries = getConnectionRetries(); 
 
         /**
          * Instantiates a new future listener for a connection.
@@ -558,10 +659,9 @@
          * @param request the <code>HttpRequestMessage</code> request that is to be sent.
          * @param response the response future object.
          */
-        public FutureListener(HttpRequestMessage request, ResponseFuture response, int retries) {
+        public FutureListener(HttpRequestMessage request, ResponseFuture response) {
             this.request = request;
             this.response = response;
-            this.retries = retries; 
         }
 
         /**
@@ -574,24 +674,48 @@
         public void operationComplete(IoFuture future) {
             ConnectFuture connFuture = (ConnectFuture) future;
             if (connFuture.isConnected()) {
+                notifyMonitoringListeners(MonitoringEvent.CONNECTION_SUCCESSFUL, request); 
                 IoSession sess = future.getSession();
 
                 // see if we need to add the SSL filter
                 addSSLFilter(sess);
                 // add the protocol filter (if it's not there already like in a
                 // reused session)
-                if (!sess.getFilterChain().contains(PROTOCOL_FILTER)) {
-                    sess.getFilterChain().addLast(PROTOCOL_FILTER, new ProtocolCodecFilter(
-                            new HttpProtocolCodecFactory()));
-                }
+                addProtocolCodecFilter(sess);
                 // (optional) add the executor filter for the event thread pool 
                 // (if it's not there already like in a reused session)
-                if (eventThreadPool != null && 
-                        !sess.getFilterChain().contains(EVENT_THREAD_POOL_FILTER)) {
-                    sess.getFilterChain().addLast(EVENT_THREAD_POOL_FILTER, 
-                            new ExecutorFilter(eventThreadPool));
+                addEventThreadPoolFilter(sess);
+                // now that we're connection, configure the session appropriately. 
+                configureSession(sess);
+                // and finally start the request process rolling. 
+                sess.write(request);
+            } 
+            else {
+                if (retries-- > 0) {
+                    // go retry this connection 
+                    retryConnection(request, response, this); 
                 }
-                
+                else {
+                    try {
+                        notifyMonitoringListeners(MonitoringEvent.CONNECTION_FAILED, request); 
+                        future.getSession();
+                        response.setException(new AsyncHttpClientException("Connection failed."));
+                    } catch (RuntimeIoException e) {
+                        //Set the future exception
+                        response.setException(e);
+                    }
+                }
+            }
+        }
+
+        /**
+         * Configure the IoSession with the client connection 
+         * parameters.
+         * 
+         * @param sess   The session to which the configuration values are to
+         *               be applied.
+         */
+        protected void configureSession(IoSession sess) {
                 sess.setAttribute(HttpIoHandler.CURRENT_REQUEST, request);
 
                 sess.setAttachment(AsyncHttpClient.this);
@@ -606,25 +730,44 @@
                 config.setSoLinger(soLinger);
                 config.setTcpNoDelay(tcpNoDelay);
                 config.setTrafficClass(trafficClass);
+        }
 
-                sess.write(request);
-            } else {
-                if (retries > 0) {
-                    // go retry this connection 
-                    retryConnection(request, response, --retries); 
-                }
-                else {
-                    try {
-                        future.getSession();
-                        response.setException(new AsyncHttpClientException("Connection failed."));
-                    } catch (RuntimeIoException e) {
-                        //Set the future exception
-                        response.setException(e);
-                    }
-                }
+        /**
+         * Add the ExecutorFilter to the session filter chain.
+         * The ExecutorFilter allows session callbacks to be 
+         * dispatched using a different thread pool than the one 
+         * used for the I/O threads.
+         * 
+         * @param sess   The session to configure.
+         */
+        protected void addEventThreadPoolFilter(IoSession sess) {
+            if (eventThreadPool != null && 
+                    !sess.getFilterChain().contains(EVENT_THREAD_POOL_FILTER)) {
+                sess.getFilterChain().addLast(EVENT_THREAD_POOL_FILTER, 
+                        new ExecutorFilter(eventThreadPool));
             }
         }
 
+        /**
+         * Add the HttpProtocol filter to the session processing 
+         * chain.  The protocol filter handles the returned 
+         * response information.
+         * 
+         * @param sess   The target session.
+         */
+        protected void addProtocolCodecFilter(IoSession sess) {
+            if (!sess.getFilterChain().contains(PROTOCOL_FILTER)) {
+                sess.getFilterChain().addLast(PROTOCOL_FILTER, new ProtocolCodecFilter(
+                        new HttpProtocolCodecFactory()));
+            }
+        }
+
+        /**
+         * Add an SSL filter to the io session when the 
+         * connection type is "https".
+         * 
+         * @param sess   The session to configure.
+         */
         private void addSSLFilter(IoSession sess) {
             String scheme = request.getUrl().getProtocol();
             
@@ -634,14 +777,7 @@
                 // session
                 if (!sess.getFilterChain().contains(SSL_FILTER)) {
                     try {
-                        SSLContext context = request.getSSLContext();
-                        if (context == null) {
-                            // if the caller did not provide an SSL context
-                            // create a default SSL context
-                            context = createDefaultSSLContext();
-                        }
-                        SslFilter sslFilter = new SslFilter(context);
-                        sslFilter.setUseClientMode(true);
+                        SslFilter sslFilter = createSSLFilter();
                         sess.getFilterChain().addLast(SSL_FILTER, sslFilter);
                     } catch (GeneralSecurityException e) {
                         try {
@@ -655,6 +791,27 @@
         }
 
         /**
+         * Create an SslFilter instance for this client.  The 
+         * filter will be configured using any SSL context defined 
+         * for the request, or a default context if one has not
+         * been explicitly configured. 
+         * 
+         * @return An appropriately configured SSLFilter for this connection.
+         * @exception GeneralSecurityException
+         */
+        protected SslFilter createSSLFilter() throws GeneralSecurityException {
+            SSLContext context = request.getSSLContext();
+            if (context == null) {
+                // if the caller did not provide an SSL context
+                // create a default SSL context
+                context = createDefaultSSLContext();
+            }
+            SslFilter sslFilter = new SslFilter(context);
+            sslFilter.setUseClientMode(true);
+            return sslFilter;
+        }
+
+        /**
          * Creates a default SSL context in case it was not provided by the
          * caller.
          *
@@ -666,6 +823,103 @@
             context.init(null, TrustManagerFactoryImpl.X509_MANAGERS, null);
             return context;
         }
+    }
+    
+    /**
+     * A FutureListener for managing connections used with 
+     * proxied connections.  This Future manages establishing 
+     * the appropriate connection type with the proxy before 
+     * handling the actual client request. 
+     */
+    class ProxyFutureListener extends FutureListener {
+        public ProxyFutureListener(HttpRequestMessage request, 
+                                   ResponseFuture response) {
+            super(request, response);
+        }
 
+        @Override
+        /**
+         * Handle operation completion events.  This is primarly 
+         * to handle the tunneling protocol required by 
+         * https requests through a proxy server.  
+         * 
+         * @param future The Future object associated with the operation.
+         */
+        public void operationComplete(IoFuture future) {
+            ConnectFuture connectFuture = (ConnectFuture)future;
+            if (connectFuture.isConnected()) {
+                IoSession session = future.getSession();
+                // add the protocol filter (if it's not there already like in a
+                // reused session)
+                addProtocolCodecFilter(session);
+                addProxyFilter(session);
+                // (optional) add the executor filter for the event thread pool 
+                // (if it's not there already like in a reused session)
+                addEventThreadPoolFilter(session);
+                
+                configureSession(session);
+                
+                // write the connect request if the protocol is https
+                String protocol = request.getUrl().getProtocol();
+                if (protocol.toLowerCase().equals("https")) {
+                    session.write(createConnectRequest());
+                } else {
+                    session.write(request);
+                }
+            } else {
+                super.operationComplete(future);
+            }
+        }
+        
+        /**
+         * Compose the connection request used for SSL proxy 
+         * tunneling connections.  This CONNECT request tells
+         * the proxy server to establish a connection with 
+         * the remote target and tunnel it through to the 
+         * client.  Once the connection has been established, 
+         * an SLL connection will be layered over the top 
+         * of the connection, creating a secure channel between 
+         * the client and the server. 
+         * 
+         * @return The request message to send back to the proxy for 
+         *         establishing the tunneled connection.
+         */
+        private HttpRequestMessage createConnectRequest() {
+            try {
+                HttpRequestMessage req = new HttpRequestMessage(new URL("http", request.getHost(), request.getPort(), ""), null);
+                req.setRequestMethod(HttpRequestMessage.REQUEST_CONNECT);
+                return req;
+            } catch (MalformedURLException e) {
+                // ignored, shouldn't happen
+            } catch (ProtocolException e) {
+                // ignored, shouldn't happen
+            }
+            // this can't happen
+            return null;
+        }
+
+        /**
+         * Add a proxy filter to the session filter chain.
+         * The proxy filter will be either a plain filter or a 
+         * tunneling SSL filter. 
+         * 
+         * @param session
+         */
+        private void addProxyFilter(IoSession session) {
+            if (!session.getFilterChain().contains(PROXY_FILTER)) {
+                String scheme = request.getUrl().getProtocol();
+                ProxyFilter proxyFilter = null;
+                if (scheme.toLowerCase().equals("https")) {
+                    try {
+                        proxyFilter = new ProxyFilter(createSSLFilter());
+                    } catch (GeneralSecurityException e) {
+                        // this normally cannot happen
+                    }
+                } else {
+                    proxyFilter = new ProxyFilter();
+                }
+                session.getFilterChain().addLast(PROXY_FILTER, proxyFilter);
+            }
+        }
     }
 }

Modified: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java (original)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/auth/AuthChallengeParser.java Fri Feb  1 10:26:11 2008
@@ -86,28 +86,4 @@
         }
         return map;
     }
-
-    /**
-     * Extracts a map of challenges ordered by authentication scheme name
-     *
-     * @param headers the array of authorization challenges
-     * @return a map of authorization challenges
-     * 
-     * @throws MalformedChallengeException if any of challenge strings
-     *  is malformed
-     */
-//    public static Map parseChallenges(final Header[] headers)
-//      throws MalformedChallengeException {
-//        if (headers == null) {
-//            throw new IllegalArgumentException("Array of challenges may not be null");
-//        }
-//        String challenge = null;
-//        Map challengemap = new HashMap(headers.length);
-//        for (int i = 0; i < headers.length; i++) {
-//            challenge = headers[i].getValue();
-//            String s = AuthChallengeParser.extractScheme(challenge);
-//            challengemap.put(s, challenge);
-//        }
-//        return challengemap;
-//   }
 }

Modified: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpIoHandler.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpIoHandler.java?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpIoHandler.java (original)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpIoHandler.java Fri Feb  1 10:26:11 2008
@@ -33,6 +33,7 @@
 import org.apache.ahc.auth.AuthScheme;
 import org.apache.ahc.auth.AuthState;
 import org.apache.ahc.util.NameValuePair;
+import org.apache.ahc.util.MonitoringEvent; 
 import org.apache.mina.common.IoHandlerAdapter;
 import org.apache.mina.common.IoSession;
 
@@ -64,6 +65,11 @@
     private final ScheduledExecutorService scheduler;
 
     /**
+     * The session cache used for reusable connections 
+     */
+    private SessionCache sessionCache; 
+
+    /**
      * Instantiates a new HttpIoHandler with a new a single-threaded executor.
      */
     public HttpIoHandler() {
@@ -80,6 +86,27 @@
     }
 
     /**
+     * Set the session cache that should be used for 
+     * connection reuse.
+     * 
+     * @param cache  The new session cache.  If null, this will disable
+     *               future connection reuse.
+     */
+    public void setSessionCache(SessionCache cache) {
+        sessionCache = cache; 
+    }
+    
+    /**
+     * Retrieve the session cache used for storing 
+     * connections for reuse. 
+     * 
+     * @return The current session cache for the client. 
+     */
+    public SessionCache getSessionCache() {
+        return sessionCache; 
+    }
+
+    /**
      * Destroys the handler and shuts down the scheduler.
      */
     public void destroy() {
@@ -108,14 +135,17 @@
 
         HttpRequestMessage request = (HttpRequestMessage) ioSession.getAttribute(CURRENT_REQUEST);
 
+        AsyncHttpClient client = (AsyncHttpClient) ioSession.getAttachment();
+
         //Check if we are to handle redirects
         if ((response.getStatusCode() == 301
              || response.getStatusCode() == 302
              || response.getStatusCode() == 307)
             && request.isFollowRedirects())
         {
-            AsyncHttpClient client = (AsyncHttpClient) ioSession.getAttachment();
 
+            // notify any interesting parties that this is starting 
+            client.notifyMonitoringListeners(MonitoringEvent.REQUEST_REDIRECTED, request); 
             //Change the request url to the redirect
             request.setUrl(new URL(response.getLocation()));
             // if we're redirected via 30x, the request method should be reset to GET
@@ -128,13 +158,20 @@
             //Send the redirect
             client.sendRequest(request);
 
+            // if we've been provided with a cache, put this session into 
+            // the cache. 
+            SessionCache cache = getSessionCache(); 
+            if (cache != null) {
             // cache the session before we return
-            SessionCache.getInstance().cacheSession(ioSession);
+                cache.cacheSession(ioSession);
+            }
             return;
         }
 
         //Check if we have authentication
         if (response.getChallenges().size() > 0) {
+            // notify any interesting parties that this is starting 
+            client.notifyMonitoringListeners(MonitoringEvent.REQUEST_CHALLENGED, request); 
             for (NameValuePair nvp : response.getChallenges()) {
                 AuthState state = request.getAuthState();
                 if (state == null) {
@@ -147,28 +184,39 @@
                 }
             }
 
-            AsyncHttpClient client = (AsyncHttpClient) ioSession.getAttachment();
-
             //Authenticate
             int authCount = request.getAuthCount() + 1;
             if (authCount <= 3) {
+                // if we've been provided with a cache, put this session into 
+                // the cache. 
+                SessionCache cache = getSessionCache(); 
+                if (cache != null) {
+                    // cache the session before we return
+                    cache.cacheSession(ioSession);
+                }
+                
                 request.setAuthCount(authCount);
                 client.sendRequest(request);
 
-                // cache the session before we return
-                SessionCache.getInstance().cacheSession(ioSession);
                 return;
             }
         }
 
         cancelTasks(request);
 
+        // notify any interesting parties that this is starting 
+        client.notifyMonitoringListeners(MonitoringEvent.REQUEST_COMPLETED, request); 
         // complete the future which will also fire the callback
         ResponseFuture result = request.getResponseFuture();
         result.set(response);
 
+        // if we've been provided with a cache, put this session into 
+        // the cache. 
+        SessionCache cache = getSessionCache(); 
+        if (cache != null) {
         // cache the session before we return
-        SessionCache.getInstance().cacheSession(ioSession);
+            cache.cacheSession(ioSession);
+        }
     }
 
     /**
@@ -185,6 +233,10 @@
         HttpRequestMessage request = (HttpRequestMessage) ioSession.getAttribute(CURRENT_REQUEST);
         cancelTasks(request);
 
+        AsyncHttpClient client = (AsyncHttpClient) ioSession.getAttachment();
+
+        // notify any interesting parties that this is starting 
+        client.notifyMonitoringListeners(MonitoringEvent.REQUEST_FAILED, request); 
         // complete the future which will also fire the callback
         ResponseFuture result = request.getResponseFuture();
         result.setException(throwable);
@@ -202,10 +254,20 @@
     public void sessionClosed(IoSession ioSession) throws Exception {
         //Clean up if any in-proccess decoding was occurring
         ioSession.removeAttribute(CURRENT_RESPONSE);
-        // remove it from the cache
-        SessionCache.getInstance().removeSession(ioSession);
+        
+        // if we've been provided with a cache, remove this session from 
+        // the cache. 
+        SessionCache cache = getSessionCache(); 
+        if (cache != null) {
+            // cache the session before we return
+            cache.removeSession(ioSession);
+        }
         HttpRequestMessage request = (HttpRequestMessage) ioSession.getAttribute(CURRENT_REQUEST);
         cancelTasks(request);
+        AsyncHttpClient client = (AsyncHttpClient) ioSession.getAttachment();
+        // notify any interesting parties that this is starting 
+        client.notifyMonitoringListeners(MonitoringEvent.CONNECTION_CLOSED_BY_SERVER, request); 
+        
         AsyncHttpClientCallback callback = request.getCallback();
         if (callback != null) {
             callback.onClosed();
@@ -276,6 +338,12 @@
         public void run() {
             // complete the future which will also fire the callback
             HttpRequestMessage request = (HttpRequestMessage)sess.getAttribute(CURRENT_REQUEST);
+            
+            AsyncHttpClient client = (AsyncHttpClient) sess.getAttachment();
+
+            // notify any interesting parties that this is starting 
+            client.notifyMonitoringListeners(MonitoringEvent.REQUEST_TIMEOUT, request); 
+            
             ResponseFuture result = request.getResponseFuture();
             result.setException(new TimeoutException());
             

Modified: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java (original)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestEncoder.java Fri Feb  1 10:26:11 2008
@@ -106,16 +106,26 @@
             CharsetEncoder encoder = Charset.forName(HttpMessage.HTTP_ELEMENT_CHARSET).newEncoder();
             buf.putString(msg.getRequestMethod(), encoder);
             buf.putString(" ", encoder);
-            buf.putString(msg.getPath(), encoder);
+            if (msg.getRequestMethod().equals(HttpRequestMessage.REQUEST_CONNECT)) {
+                buf.putString(msg.getHost(), encoder);
+                buf.putString(":", encoder);
+                buf.putString(msg.getPort() + "", encoder);
+            } else {
+                if (msg.isProxyEnabled() && !msg.getProtocol().toLowerCase().equals("https")) {
+                    buf.putString(msg.getUrl().toString(), encoder);
+                } else {
+                    buf.putString(msg.getUrl().getFile(), encoder);
+                }
             //If its a GET, append the attributes
             if (msg.getRequestMethod().equals(HttpRequestMessage.REQUEST_GET) && attrCount > 0) {
                 //If there is not already a ? in the query, append one, otherwise append a &
-                if (!msg.getPath().contains("?")) {
+                    if (!msg.getUrl().getFile().contains("?")) {
                     buf.putString("?", encoder);
                 } else {
                     buf.putString("&", encoder);
                 }
                 buf.putString(urlAttrs, encoder);
+            }
             }
             buf.putString(" HTTP/1.1", encoder);
             buf.put(CRLF);

Modified: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java (original)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java Fri Feb  1 10:26:11 2008
@@ -32,6 +32,7 @@
 import org.apache.ahc.auth.AuthScope;
 import org.apache.ahc.auth.AuthState;
 import org.apache.ahc.auth.Credentials;
+import org.apache.ahc.proxy.ProxyConfiguration;
 
 /**
  * The Class HttpRequestMessage. This is an object representation of an HTTP request.
@@ -83,6 +84,8 @@
      */
     public static final String REQUEST_TRACE = "TRACE";
 
+    public static final String REQUEST_CONNECT = "CONNECT";
+
     /**
      * The request method.
      */
@@ -154,6 +157,12 @@
      */
     private SSLContext sslContext;
 
+    private ProxyConfiguration proxyConfig;
+    
+    private volatile long requestStartTime = 0L;
+    
+    private volatile long connectStartTime = 0L;
+
     /**
      * Instantiates a new http request message.
      *
@@ -249,7 +258,8 @@
             || requestMethod.equals(REQUEST_OPTIONS)
             || requestMethod.equals(REQUEST_PUT)
             || requestMethod.equals(REQUEST_DELETE)
-            || requestMethod.equals(REQUEST_TRACE)) {
+            || requestMethod.equals(REQUEST_TRACE)
+            || requestMethod.equals(REQUEST_CONNECT)) {
             this.requestMethod = requestMethod;
             return;
         }
@@ -538,5 +548,88 @@
      */
     public void setSSLContext(SSLContext sslContext) {
         this.sslContext = sslContext;
+    }
+    
+    /**
+     * Get the message proxy configuration.  This controls 
+     * how downstream filters handle the message connections.
+     * 
+     * @return The current proxy configuration.  Returns null if 
+     *         no proxying support is configured.
+     */
+    public ProxyConfiguration getProxyConfiguration() {
+        return proxyConfig;
+    }
+    
+    /**
+     * Set the proxy configuration use to control proxied 
+     * connections.
+     * 
+     * @param config The new proxy configuration.
+     */
+    public void setProxyConfiguration(ProxyConfiguration config) {
+        proxyConfig = config;
+    }
+    
+    /**
+     * Test if this request needs to go through a proxy 
+     * server.  To be proxied, there must be a proxy configuration 
+     * set and the request target must not be specified in 
+     * the proxy exclusion list.
+     * 
+     * @return true if this request must go through a proxy server, 
+     *         false if a direct connection can be used.
+     */
+    public boolean isProxyEnabled() {
+        return proxyConfig != null && !proxyConfig.isExcluded(getUrl());
+    }
+    
+    /**
+     * Return the time when the request was first initiated.
+     * 
+     * @return The time, in milliseconds, for when request processing
+     *         was initiated.
+     */
+    public long getRequestStartTime() {
+        return requestStartTime;
+    }
+    
+    /**
+     * Mark the start of request processing. 
+     */
+    public void setRequestStartTime() {
+        requestStartTime = System.nanoTime()/1000000;
+    }
+    
+    /**
+     * Clear the request starting time back to zero.
+     */
+    public void clearRequestStartTime() {
+        requestStartTime = 0L;
+    }
+    
+    /**
+     * Get the time stamp for when the connection request was 
+     * started.
+     * 
+     * @return The timestamp (in milliseconds) for when the connection
+     *         for this message request was initiated.
+     */
+    public long getConnectStartTime() {
+        return connectStartTime;
+    }
+    
+    /**
+     * Set the timestamp for connection initiation.
+     */
+    public void setConnectStartTime() {
+        connectStartTime = System.nanoTime()/1000000;
+    }
+    
+    /**
+     * Reset the connection start time.
+     */
+    public void clearConnectStartTime() {
+        connectStartTime = 0L;
     }
 }

Modified: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/ResponseFuture.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/ResponseFuture.java?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/ResponseFuture.java (original)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/ResponseFuture.java Fri Feb  1 10:26:11 2008
@@ -91,11 +91,16 @@
      */
     @Override
     public void set(HttpResponseMessage v) {
-        super.set(v);
-        // fire the callback once the future is done
+        try {
+            // fire the callback before completing the future to 
+            // ensure everything gets handled before the future gets 
+            // completed. 
         if (callback != null) {
             callback.onResponse(v);
         }
+        } finally {
+            super.set(v);
+        }
     }
 
     /**
@@ -107,14 +112,19 @@
      */
     @Override
     public void setException(Throwable t) {
-        super.setException(t);
-        // fire the callback once the future is done
+        try {
+            // fire the callback before completing the future to 
+            // ensure everything gets handled before the future gets 
+            // completed. 
         if (callback != null) {
             if (t instanceof TimeoutException) {
                 callback.onTimeout();
             } else {
                 callback.onException(t);
             }
+        }
+        } finally {
+            super.setException(t);
         }
     }
     

Modified: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/SessionCache.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/SessionCache.java?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/SessionCache.java (original)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/codec/SessionCache.java Fri Feb  1 10:26:11 2008
@@ -32,19 +32,10 @@
  * 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() {}
+    public SessionCache() {}
 
     /**
      * Returns an IoSession that is connected and considered usable.  Note that
@@ -116,10 +107,24 @@
         }
     }
     
+    /**
+     * Generate a request key from an HTTP request message.
+     * 
+     * @param msg    The request message we need a key from.
+     * 
+     * @return A String key instance for this request. 
+     */
     private String getKey(HttpRequestMessage msg) {
         return getKey(msg.getHost(), msg.getPort());
     }
     
+    /**
+     * Generate a session key from an InetSocketAddress 
+     * 
+     * @param remote The endpoint address of the connection.
+     * 
+     * @return A string key for this endpoint. 
+     */
     private String getKey(InetSocketAddress remote) {
         return getKey(remote.getHostName(), remote.getPort());
     }

Added: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java?rev=617593&view=auto
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java (added)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java Fri Feb  1 10:26:11 2008
@@ -0,0 +1,252 @@
+package org.apache.ahc.proxy;
+
+import java.net.InetSocketAddress;
+import java.net.InetAddress;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.StringTokenizer;
+
+import org.apache.ahc.auth.AuthScheme;
+
+public class ProxyConfiguration {
+    private final String httpProxyHost;
+    private final int httpProxyPort;
+    private final String httpsProxyHost;
+    private final int httpsProxyPort;
+    private String exclusionList = null;
+    private String proxyUser = null;
+    private String proxyPassword = null;
+    private AuthScheme scheme = null;
+    private List<String> wildCardExclusions = new ArrayList<String>(); 
+    private Map<String,String> directExclusions = new HashMap<String,String>(); 
+    
+    /**
+     * Construct a ProxyConfiguration instance.
+     * 
+     * @param proxyHost The host to be used for both http and https connections.
+     * @param proxyPort The port to be used for both http and https connections.
+     */
+    public ProxyConfiguration(String proxyHost, int proxyPort) {
+        this.httpProxyHost = proxyHost;
+        this.httpProxyPort = proxyPort;
+        this.httpsProxyHost = proxyHost;
+        this.httpsProxyPort = proxyPort;
+    }
+    
+    /**
+     * Construct a proxy configuration that uses separate 
+     * http and https proxy targets.
+     * 
+     * @param httpProxyHost
+     *               The host to be used for http requests.
+     * @param httpProxyPort
+     *               The port to be used for http requests.
+     * @param httpsProxyHost
+     *               The host to use for https requests.
+     * @param httpsProxyPort
+     */
+    public ProxyConfiguration(String httpProxyHost, int httpProxyPort, String httpsProxyHost, int httpsProxyPort) {
+        this.httpProxyHost = httpProxyHost;
+        this.httpProxyPort = httpProxyPort;
+        this.httpsProxyHost = httpsProxyHost;
+        this.httpsProxyPort = httpsProxyPort;
+    }
+    
+    
+    /**
+     * Get the target connection for a proxied request. 
+     * The target will depend on whether this is an http 
+     * or an https request.
+     * 
+     * @param target The target URL
+     * 
+     * @return An InetSocketAddress for the appropriate proxy target. 
+     */
+    public InetSocketAddress getProxyAddress(URL target) 
+    {
+        if (target.getProtocol().equalsIgnoreCase("https")) {
+            return new InetSocketAddress(getHttpsProxyHost(), getHttpsProxyPort()); 
+        }
+        else { 
+            return new InetSocketAddress(getHttpProxyHost(), getHttpProxyPort()); 
+        }
+    }
+    
+    /**
+     * Get the proxy host name for http connections.
+     * 
+     * @return The string name of the proxy host. 
+     */
+    public String getHttpProxyHost() {
+        return httpProxyHost;
+    }
+
+    /**
+     * Get the port of the proxy server used for servicing 
+     * http requests.
+     * 
+     * @return The port number of the configured http proxy server.
+     */
+    public int getHttpProxyPort() {
+        return httpProxyPort;
+    }
+
+    /**
+     * Get the host of the proxy server for handling 
+     * https requests.  If not explicitly set this defaults 
+     * to the http proxy host.
+     * 
+     * @return The string name of the proxy host. 
+     */
+    public String getHttpsProxyHost() {
+        return httpsProxyHost;
+    }
+
+    /**
+     * Get the port used to connect to the https proxy 
+     * server.  If not explictly set, this is the same as the 
+     * http server.
+     * 
+     * @return The connection port number for handling https requests.
+     */
+    public int getHttpsProxyPort() {
+        return httpsProxyPort;
+    }
+
+    /**
+     * Retrieve the exclusion list used for this configuration.
+     * If set, this returns a string containing the 
+     * individual exclusion domains separated by ";".
+     * 
+     * @return The string value of the exclusion list, or null 
+     *         if this has not been set.
+     */
+    public String getExclusionList() {
+        return exclusionList;
+    }
+
+    /**
+     * Set the exclusion list for the proxy configuration.
+     * The exclusion list is a set of explicit hosts and/or
+     * wildcard domains ("*.apache.org") separated by ";". 
+     * 
+     * @param exclusionList
+     */
+    public void setExclusionList(String exclusionList) {
+        this.exclusionList = exclusionList;
+        // we clear these out regardless 
+        wildCardExclusions.clear(); 
+        directExclusions.clear(); 
+        
+        if (exclusionList != null) {
+            // now divide the exclusion list into the explict and wildcard lists 
+            StringTokenizer tokenizer = new StringTokenizer(exclusionList, ";"); 
+            while (tokenizer.hasMoreTokens()) {
+                String domain = tokenizer.nextToken(); 
+                // wild card versions we just create a matching list that we run through 
+                if (domain.startsWith("*.")) {
+                    wildCardExclusions.add(domain.substring(1)); 
+                }
+                else {
+                    // the direct exlusions are names we can look up via a map. 
+                    directExclusions.put(domain, domain); 
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns the proxy authentication userid.
+     * 
+     * @return The name of the authentication userid.  Returns null 
+     *         if no user has been specified.
+     */
+    public String getProxyUser() {
+        return proxyUser;
+    }
+
+    /**
+     * Set the userid used for proxy server authentication.
+     * 
+     * @param proxyUser The userid to be used by the authentication schema.
+     */
+    public void setProxyUser(String proxyUser) {
+        this.proxyUser = proxyUser;
+    }
+
+    /**
+     * Returns the configured password used to access the 
+     * proxy server.
+     * 
+     * @return The configured password.  Returns null if no password is 
+     *         set.
+     */
+    public String getProxyPassword() {
+        return proxyPassword;
+    }
+
+    /**
+     * Set the password to be used for accessing the 
+     * proxy server.
+     * 
+     * @param proxyPassword
+     *               The password to be used for authentication.
+     */
+    public void setProxyPassword(String proxyPassword) {
+        this.proxyPassword = proxyPassword;
+    }
+    
+    /**
+     * Returns the authentication scheme used for logging 
+     * in the proxy server. 
+     * 
+     * @return The configured authentication scheme.  Returns null 
+     *         if one has not been set.
+     */
+    public AuthScheme getAuthScheme() {
+        return scheme;
+    }
+    
+    /**
+     * Set the authentication scheme to be used for logging 
+     * in the proxy server.
+     * 
+     * @param scheme The scheme to be used for logging in.  If null,
+     *               no login will be attempted with the proxy server.
+     */
+    public void setAuthScheme(AuthScheme scheme) {
+        this.scheme = scheme;
+    }
+    
+    /**
+     * Tests if the host in a target URL is specified in 
+     * the proxy configuration exclusion list.
+     * 
+     * @param target The target URL of the connection.
+     * 
+     * @return true if the host is included in the configuration 
+     *         exclusion list.  false indicates the connection
+     *         needs to go through the proxy server.
+     */
+    public boolean isExcluded(URL target) {
+        String host = target.getHost(); 
+        
+        // if the host is explicitly listed, this is easy 
+        if (directExclusions.get(host) != null) {
+            return true; 
+        }
+        // the wildcard elements are stored as ".apache.org", so 
+        // a simple endsWith() test will gives a match on something 
+        // like "people.apache.org". 
+        for (String domain : wildCardExclusions) {
+            if (host.endsWith(domain)) {
+                return true; 
+            }
+        }
+        // not found in any host 
+        return false; 
+    }
+}

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyConfiguration.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyFilter.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyFilter.java?rev=617593&view=auto
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyFilter.java (added)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyFilter.java Fri Feb  1 10:26:11 2008
@@ -0,0 +1,143 @@
+package org.apache.ahc.proxy;
+
+import org.apache.ahc.AsyncHttpClient;
+import org.apache.ahc.auth.UsernamePasswordCredentials;
+import org.apache.ahc.codec.HttpIoHandler;
+import org.apache.ahc.codec.HttpRequestMessage;
+import org.apache.ahc.codec.HttpResponseMessage;
+import org.apache.mina.common.IoFilterAdapter;
+import org.apache.mina.common.IoSession;
+import org.apache.mina.common.WriteRequest; 
+import org.apache.mina.filter.ssl.SslFilter;
+
+public class ProxyFilter extends IoFilterAdapter {
+    public static final String PROXY_AUTHORIZATION_HEADER = "Proxy-Authorization";
+    
+    private volatile boolean connectHandshakeComplete;
+    private final SslFilter sslFilter;
+
+    /**
+     * Create a proxy filter for a plain (http request) 
+     * to a proxy server. 
+     */
+    public ProxyFilter() {
+        this(null);
+    }
+    
+    /**
+     * Create a proxy filter that works in conjunction with 
+     * an SslFilter for a secure connection.
+     * 
+     * @param sslFilter The SSL filter to use for the secure connection.  If
+     *                  null, a plain connection will be used.
+     */
+    public ProxyFilter(SslFilter sslFilter) {
+        this.sslFilter = sslFilter;
+    }
+    
+    @Override
+    /**
+     * Process a message send event to the proxy.  This 
+     * will add any required proxy authentication headers
+     * to the request. 
+     * 
+     * @param nextFilter
+     * @param session
+     * @param message
+     * 
+     * @exception Exception
+     */
+    public void messageSent(NextFilter nextFilter, IoSession session,
+            WriteRequest message) throws Exception {
+        HttpRequestMessage request = (HttpRequestMessage)message.getMessage(); 
+        ProxyConfiguration proxyConfig = request.getProxyConfiguration();
+        if (proxyConfig != null && 
+                proxyConfig.getProxyUser() != null && 
+                proxyConfig.getProxyPassword() != null &&
+                proxyConfig.getAuthScheme() != null) { // can proxy config ever be null?
+            // add the proxy authorization header
+            UsernamePasswordCredentials cred = 
+                    new UsernamePasswordCredentials(proxyConfig.getProxyUser(), 
+                            proxyConfig.getProxyPassword());
+            String authHeader = proxyConfig.getAuthScheme().authenticate(cred, request);
+            request.setHeader(PROXY_AUTHORIZATION_HEADER, authHeader);
+        }
+        // always forward
+        super.messageSent(nextFilter, session, message);
+    }
+
+    /**
+     * Returns the HttpRequestMessage instance currently 
+     * being processed by the IoSession.
+     * 
+     * @param session The current session using the filter.
+     * 
+     * @return The request message being processed. 
+     */
+    private HttpRequestMessage getRequest(IoSession session) {
+        HttpRequestMessage request = 
+                (HttpRequestMessage)session.getAttribute(HttpIoHandler.CURRENT_REQUEST);
+        return request;
+    }
+
+    @Override
+    /**
+     * Process the messageReceived() filter response.  this
+     * will handle any proxy handshaking and SSL connection
+     * creation required.
+     * 
+     * @param nextFilter The next filter in the chain.
+     * @param session    The IoSession this operation is associated with.
+     * @param message    The message object under construction.
+     * 
+     * @exception Exception
+     */
+    public void messageReceived(NextFilter nextFilter, IoSession session,
+            Object message) throws Exception {
+        if (needConnectHandshake()) {
+            // we need to complete the connect handshake
+            handleConnectResponse(session, message);
+        } else {
+            // the connection is established, just allow this 
+            // to flow down the chain. 
+            super.messageReceived(nextFilter, session, message);
+        }
+    }
+    
+    /**
+     * Tests if we need to perform the SSL tunneling handshake. 
+     * If this is an https request, we need to handle the 
+     * tunneling here.  If we've already established this, 
+     * then the processing can continue with the next filter. 
+     * 
+     * @return true if we need to establish the handshake.
+     */
+    private boolean needConnectHandshake() {
+        return (sslFilter != null && !connectHandshakeComplete);
+    }
+
+    /**
+     * Handle the response from a CONNECT request sent to 
+     * the proxy server.  If we got a good CONNECT request 
+     * back, we add the SSL filter to the chain and 
+     * write the original request back to the proxy 
+     * server. 
+     * 
+     * @param session The current session.
+     * @param message The message object representing the request.
+     */
+    private void handleConnectResponse(IoSession session, Object message) {
+        HttpResponseMessage response = (HttpResponseMessage)message;
+        int status = response.getStatusCode();
+        if (status == 200) {
+            // layer the SSL socket by inserting the SSL filter
+            session.getFilterChain().addBefore(AsyncHttpClient.PROTOCOL_FILTER, "SSL", sslFilter);
+            connectHandshakeComplete = true; // handshake is done
+            HttpRequestMessage request = getRequest(session);
+            // write the original request intended for the remote target
+            session.write(request);
+        } else {
+            session.close();
+        }
+    }
+}

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyFilter.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyFilter.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/proxy/ProxyFilter.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/CountingMonitor.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/CountingMonitor.java?rev=617593&view=auto
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/CountingMonitor.java (added)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/CountingMonitor.java Fri Feb  1 10:26:11 2008
@@ -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.ahc.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+ 
+/**
+ * Simple implementation of an AsyncHttpMonitor that 
+ * just keeps a count of all of the events. 
+ */
+public class CountingMonitor implements MonitoringListener {
+    private AtomicInteger[] counters = 
+            new AtomicInteger[MonitoringEvent.CONNECTION_CLOSED + 1];
+    
+    /**
+     * Default constructor 
+     */
+    public CountingMonitor() {
+        for (int i = 0; i < counters.length; i++) {
+            counters[i] = new AtomicInteger(0);
+        }
+    }
+    
+    /**
+     * Handle a notification event from an AsyncHttpClient.
+     * 
+     * @param event  The triggered event.
+     */
+    public void notification(MonitoringEvent event) {
+        int type = event.getType();  
+        if (type < counters.length) {
+            counters[type].incrementAndGet();
+        }
+    }
+    
+    
+    /**
+     * Get the counter from a specific MonitoringEvent.
+     * 
+     * @param type   The type number of the event.
+     * 
+     * @return The current counter value. 
+     */
+    public int getCount(int type) {
+        // we only return values for ones in the defined range.  Anything else is 
+        // zero. 
+        if (type < counters.length) {
+            return counters[type].get();
+        } else {
+            return 0;
+        }
+    }
+    
+    
+    /**
+     * Zero all of the event counters. 
+     */
+    public void clearCounters() {
+        AtomicInteger[] newCounters = new AtomicInteger[MonitoringEvent.CONNECTION_CLOSED + 1];
+        for (int i = 0; i < newCounters.length; i++) {
+            newCounters[i] = new AtomicInteger(0);
+        }
+        counters = newCounters;
+    }
+}
+

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/CountingMonitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/CountingMonitor.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/CountingMonitor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java?rev=617593&view=auto
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java (added)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java Fri Feb  1 10:26:11 2008
@@ -0,0 +1,48 @@
+/*
+ * 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.util;
+
+import java.util.concurrent.Executors;
+import java.util.concurrent.ThreadFactory;
+
+/**
+ * Simple implementation of a ThreadFactory that 
+ * marks all of the threads as daemon threads.
+ */
+public class DaemonThreadFactory implements ThreadFactory {
+    private final ThreadFactory factory = Executors.defaultThreadFactory();
+
+    /**
+     * Create a new thread for the thread pool.  The create
+     * thread will be a daemon thread.
+     * 
+     * @param r      The runnable used by the thread pool.
+     * 
+     * @return The newly created thread. 
+     */
+    public Thread newThread(Runnable r) {
+        Thread t = factory.newThread(r);
+        if (!t.isDaemon()) {
+            t.setDaemon(true);
+        }
+        return t;
+    }
+
+}

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/EventDispatcher.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/EventDispatcher.java?rev=617593&view=auto
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/EventDispatcher.java (added)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/EventDispatcher.java Fri Feb  1 10:26:11 2008
@@ -0,0 +1,95 @@
+/*
+ * 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.util;
+
+import java.util.List;
+import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Executors;
+
+/**
+ * This is an event dispatcher to dispatch monitoring events on separate threads
+ * from the main thread.  
+ * @version $Rev$ $Date$
+ */
+public class EventDispatcher {
+    /** shared thread pool for dispatching events */
+    private static ExecutorService dispatchThreadPool;
+    
+    /** list of listeners for monitoring events */
+    private final List<MonitoringListener> listeners = 
+            new CopyOnWriteArrayList<MonitoringListener>();
+    
+    /**
+     * Creates a new EventDispatcher, including starting the new thread. 
+     */
+    public EventDispatcher() {
+        // initialize the thread pool if it is not initialized yet
+        synchronized (EventDispatcher.class) {
+            if (dispatchThreadPool == null) {
+                dispatchThreadPool = Executors.newSingleThreadExecutor(new DaemonThreadFactory());
+            }
+        }
+    }
+    
+    /**
+     * Add a listener for the dispatched events. 
+     * 
+     * @param listener The new listener.
+     */
+    public void addListener(MonitoringListener listener) {
+        listeners.add(listener);
+    }
+    
+    /**
+     * Remove a listener from the dispatch queue.
+     * 
+     * @param listener The listener to remove.
+     */
+    public void removeListener(MonitoringListener listener) {
+        listeners.remove(listener);
+    }
+    
+    /**
+     * Dispatch an event.  
+     * 
+     * @param event     The event to dispatch.
+     */
+    public void dispatchEvent(final MonitoringEvent event) { 
+        // if there's no active listeners, no point in dispatching this 
+        if (listeners.isEmpty()) {
+            return;
+        }
+
+        Runnable job = new Runnable() {
+            public void run() {
+                // iterate through the listeners list calling the handlers. 
+                for (MonitoringListener listener : listeners) {
+                    try {
+                        event.dispatch(listener); 
+                    } catch (Throwable e) {
+                        // just eat these 
+                    }
+                }
+            }
+        };
+        dispatchThreadPool.execute(job);
+    }
+}

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/EventDispatcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/EventDispatcher.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/EventDispatcher.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringEvent.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringEvent.java?rev=617593&view=auto
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringEvent.java (added)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringEvent.java Fri Feb  1 10:26:11 2008
@@ -0,0 +1,162 @@
+/*
+ * 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.util;
+ 
+import org.apache.ahc.codec.HttpRequestMessage; 
+
+/**
+ * An event triggered during various lifecycle events 
+ * of an HTTP request.  Intended for collection of 
+ * performance and general monitoring status.
+ */
+public class MonitoringEvent {
+    /**
+     * The event was triggered by a new HTTP request.
+     */
+    public static final int REQUEST_STARTED = 0; 
+    /**
+     * The event was triggered by successful completion of 
+     * an HTTP request.
+     */
+    public static final int REQUEST_COMPLETED = 1; 
+    /**
+     * The event was triggered by a failure return from 
+     * an HTTP request.
+     */
+    public static final int REQUEST_FAILED = 2; 
+    /**
+     * The event was triggered due to a timeout while 
+     * waiting for an HTTP request to complete.
+     */
+    public static final int REQUEST_TIMEOUT = 3; 
+    /**
+     * Indicates a new connection attempt.
+     */
+    public static final int CONNECTION_ATTEMPTED = 4; 
+    /**
+     * Indicates a failure occurred when attempting to 
+     * connect to a host.
+     */
+    public static final int CONNECTION_FAILED = 5; 
+    /**
+     * Indicates an attempt at retrying a host connect.
+     */
+    public static final int CONNECTION_RETRIED = 6; 
+    /**
+     * Indicates an existing connection is being reused
+     * for a request.
+     */
+    public static final int CONNECTION_REUSED = 7; 
+    /**
+     * Indicates a connection was successfully established 
+     * for a request.
+     */
+    public static final int CONNECTION_SUCCESSFUL = 8; 
+    /**
+     * The request was redirected to a different location. 
+     */
+    public static final int REQUEST_REDIRECTED = 9; 
+    /**
+     * An authentication challenge was received for the request 
+     */
+    public static final int REQUEST_CHALLENGED = 10; 
+    /**
+     * Indicates the connection was closed by the server.
+     */
+    public static final int CONNECTION_CLOSED_BY_SERVER = 11; 
+    /**
+     * Indicates the connection was closed by the client.
+     */
+    public static final int CONNECTION_CLOSED = 12; 
+    
+    /** the type of the event */
+    private final int eventType; 
+    /** timestamp of when the event occurred */
+    private final long timeStamp; 
+    /** the request message associated with the event */
+    private final HttpRequestMessage request; 
+    
+    /**
+     * Create a new monitoring event.
+     * 
+     * @param eventType The type of event.
+     * @param request   The Http request that his event is triggered by.
+     * 
+     * @see #REQUEST_STARTED
+     * @see #REQUEST_COMPLETED
+     * @see #REQUEST_FAILED
+     * @see #REQUEST_TIMEOUT
+     * @see #CONNECTION_ATTEMPTED
+     * @see #CONNECTION_FAILED
+     * @see #CONNECTION_RETRIED
+     * @see #CONNECTION_REUSED
+     * @see #CONNECTION_SUCCESSFUL
+     * @see #REQUEST_REDIRECTED
+     * @see #REQUEST_CHALLENGED 
+     * @see #CONNECTION_CLOSED_BY_SERVER
+     * @see #CONNECTION_CLOSED
+     */
+    public MonitoringEvent(int eventType, HttpRequestMessage request) {
+        this.eventType = eventType; 
+        this.request = request; 
+        // timestamp the event (hi-res)
+        this.timeStamp = System.nanoTime()/1000000; 
+    }
+    
+    /**
+     * Returns the type code for the event.  
+     * 
+     * @return The integer type code for the event. 
+     */
+    public int getType() {
+        return eventType; 
+    }
+    
+    /**
+     * Get the HTTP request that is associated with this event.
+     * 
+     * @return The HTTP message request being processed when one 
+     *         of these events occurred.
+     */
+    public HttpRequestMessage getRequest() 
+    {
+        return request; 
+    }
+    
+    /**
+     * Returns the timestamp that was taken when the event 
+     * was generated. 
+     * 
+     * @return The hi-res timer value (in ms) for when the 
+     *         event was generated.
+     */
+    public long getTimeStamp() {
+        return timeStamp; 
+    }
+    
+    /**
+     * Dispatch an event to a listener. 
+     * 
+     * @param listener The target listener
+     */
+    public void dispatch(Object listener) {
+        ((MonitoringListener)listener).notification(this);
+    }
+}

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringEvent.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringEvent.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringEvent.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringListener.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringListener.java?rev=617593&view=auto
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringListener.java (added)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringListener.java Fri Feb  1 10:26:11 2008
@@ -0,0 +1,34 @@
+/**
+ *
+ * Copyright 2003-2004 The Apache Software Foundation
+ *
+ *  Licensed under the Apache License, Version 2.0 (the "License");
+ *  you may not use this file except in compliance with the License.
+ *  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing, software
+ *  distributed under the License is distributed on an "AS IS" BASIS,
+ *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *  See the License for the specific language governing permissions and
+ *  limitations under the License.
+ */
+
+package org.apache.ahc.util;
+
+import java.util.EventListener;
+
+/**
+ * The interface for listening for monitoring events.
+ * @version $Rev$ $Date$
+ */
+public interface MonitoringListener extends EventListener {
+    /**
+     * Process a notification for a MonitoringEvent.
+     * 
+     * @param event  The particular event to be processed.
+     */
+    public void notification(MonitoringEvent event);
+}
+

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringListener.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/MonitoringListener.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Added: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/TimeMonitor.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/TimeMonitor.java?rev=617593&view=auto
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/TimeMonitor.java (added)
+++ geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/TimeMonitor.java Fri Feb  1 10:26:11 2008
@@ -0,0 +1,96 @@
+/*
+ *  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.util;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.concurrent.atomic.AtomicLong;
+
+/**
+ * Simple monitoring listener for tracking average request times and average
+ * connect times.  This is provided mainly to illustrate how one can collect 
+ * timing data. 
+ */
+public final class TimeMonitor extends CountingMonitor {
+    private final AtomicInteger requestCount = new AtomicInteger();
+    private final AtomicLong requestTimes = new AtomicLong();
+    
+    private final AtomicInteger connectCount = new AtomicInteger();
+    private final AtomicLong connectTimes = new AtomicLong();
+
+    @Override
+    /**
+     * Process a notification event.  If this is a 
+     * REQUEST_COMPLETED event, the request timing 
+     * information is added to the accumulators to that 
+     * average response time can be calculated.
+     * 
+     * @param event  The notification event.
+     */
+    public void notification(MonitoringEvent event) {
+        super.notification(event);
+        
+        // get the response time
+        int type = event.getType();
+        if (type == MonitoringEvent.REQUEST_COMPLETED) {
+            long requestStartTime = event.getRequest().getRequestStartTime();
+            if (requestStartTime != 0L) {
+                requestCount.incrementAndGet();
+                long elapsed = event.getTimeStamp() - requestStartTime;
+                requestTimes.addAndGet(elapsed);
+            }
+        } else if (type == MonitoringEvent.CONNECTION_SUCCESSFUL) {
+            long connectStartTime = event.getRequest().getConnectStartTime();
+            if (connectStartTime != 0L) {
+                connectCount.incrementAndGet();
+                long elapsed = event.getTimeStamp() - connectStartTime;
+                connectTimes.addAndGet(elapsed);
+            }
+        }
+    }
+    
+    /**
+     * Return the average calculated response time for 
+     * the processed requests. 
+     * 
+     * @return The average response time, in milliseconds, for 
+     *         all recorded completed requests.
+     */
+    public long getAverageResponseTime() {
+        if (requestCount.get() == 0) {
+            return 0L;
+        }
+        return requestTimes.get()/requestCount.get();
+    }
+    
+    
+    /**
+     * Return the average calculated connect time for 
+     * the processed requests. 
+     * 
+     * @return The average connect time, in milliseconds, for 
+     *         all recorded completed requests.
+     */
+    public long getAverageConnectTime() {
+        if (connectCount.get() == 0) {
+            return 0L;
+        }
+        return connectTimes.get()/connectCount.get();
+    }
+}

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/TimeMonitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/TimeMonitor.java
------------------------------------------------------------------------------
    svn:keywords = Date Revision

Propchange: geronimo/sandbox/async-http-client-mina2/src/main/java/org/apache/ahc/util/TimeMonitor.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/index.jsp
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/index.jsp?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/index.jsp (original)
+++ geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/index.jsp Fri Feb  1 10:26:11 2008
@@ -2,5 +2,4 @@
     Cookie cookie = new Cookie("test","value");
     cookie.setMaxAge(3600);
     response.addCookie(cookie);
-%>
-Hello World!
\ No newline at end of file
+%>Hello World!
\ No newline at end of file

Modified: geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/timeout.jsp
URL: http://svn.apache.org/viewvc/geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/timeout.jsp?rev=617593&r1=617592&r2=617593&view=diff
==============================================================================
--- geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/timeout.jsp (original)
+++ geronimo/sandbox/async-http-client-mina2/src/test/catalina/webapps/ROOT/timeout.jsp Fri Feb  1 10:26:11 2008
@@ -1,5 +1,4 @@
 <%
     Thread.sleep(5000);
-%>
-Hello World!
+%>Hello World!