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/01/24 19:24:25 UTC

svn commit: r614952 - in /geronimo/sandbox/AsyncHttpClient/src: main/java/org/apache/ahc/ main/java/org/apache/ahc/codec/ main/java/org/apache/ahc/util/ test/java/org/apache/ahc/

Author: rickmcguire
Date: Thu Jan 24 10:24:21 2008
New Revision: 614952

URL: http://svn.apache.org/viewvc?rev=614952&view=rev
Log:
GERONIMO-3761 Add data collection and instrumentation to the AsyncHttpClient

Developed with a lot of assistance from Sangjin Lee. 


Added:
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/CountingMonitor.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/EventDispatcher.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringEvent.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringListener.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/TimeMonitor.java   (with props)
    geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/MonitoringTest.java   (with props)
Modified:
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java
    geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java
    geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/RetryTest.java

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java?rev=614952&r1=614951&r2=614952&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/AsyncHttpClient.java Thu Jan 24 10:24:21 2008
@@ -39,6 +39,9 @@
 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.ConnectFuture;
 import org.apache.mina.common.IoFuture;
 import org.apache.mina.common.IoFutureListener;
@@ -144,7 +147,10 @@
     
     /** flag to make this as having been disposed of */
     private boolean destroyed = false; 
-
+    
+    /** 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";
@@ -453,6 +459,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)
@@ -476,6 +487,12 @@
         // 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();
@@ -498,6 +515,9 @@
      *                 used for the previous attempt.
      */
     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(listener);
     }
@@ -534,6 +554,9 @@
             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);
@@ -576,6 +599,7 @@
         if (connector != null) {
             connector.setWorkerTimeout(0);
         }
+        
         // release the thread pool references 
         threadPool = null; 
         eventThreadPool = null; 
@@ -584,6 +608,48 @@
     }
 
     /**
+     * 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
@@ -619,6 +685,7 @@
         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
@@ -641,6 +708,7 @@
                 }
                 else {
                     try {
+                        notifyMonitoringListeners(MonitoringEvent.CONNECTION_FAILED, request); 
                         future.getSession();
                         response.setException(new AsyncHttpClientException("Connection failed."));
                     } catch (RuntimeIOException e) {

Modified: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java?rev=614952&r1=614951&r2=614952&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpIoHandler.java Thu Jan 24 10:24:21 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;
 
@@ -133,6 +134,8 @@
         HttpResponseMessage response = (HttpResponseMessage) object;
 
         HttpRequestMessage request = (HttpRequestMessage) ioSession.getAttribute(CURRENT_REQUEST);
+        
+        AsyncHttpClient client = (AsyncHttpClient) ioSession.getAttachment();
 
         //Check if we are to handle redirects
         if ((response.getStatusCode() == 301
@@ -140,8 +143,9 @@
              || 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
@@ -166,6 +170,8 @@
 
         //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) {
@@ -178,14 +184,9 @@
                 }
             }
 
-            AsyncHttpClient client = (AsyncHttpClient) ioSession.getAttachment();
-
             //Authenticate
             int authCount = request.getAuthCount() + 1;
             if (authCount <= 3) {
-                request.setAuthCount(authCount);
-                client.sendRequest(request);
-
                 // if we've been provided with a cache, put this session into 
                 // the cache. 
                 SessionCache cache = getSessionCache(); 
@@ -193,12 +194,18 @@
                     // cache the session before we return
                     cache.cacheSession(ioSession);
                 }
+                
+                request.setAuthCount(authCount);
+                client.sendRequest(request);
+
                 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);
@@ -225,11 +232,17 @@
 
         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);
 
+        // notify any interesting parties that this is starting 
+        client.notifyMonitoringListeners(MonitoringEvent.CONNECTION_CLOSED_BY_SERVER, request); 
         //Exception is bad, so just close it up
         ioSession.close();
     }
@@ -253,6 +266,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.CONNECTION_CLOSED_BY_SERVER, request); 
+        
         AsyncHttpClientCallback callback = request.getCallback();
         if (callback != null) {
             callback.onClosed();
@@ -323,6 +340,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/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java?rev=614952&r1=614951&r2=614952&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/codec/HttpRequestMessage.java Thu Jan 24 10:24:21 2008
@@ -158,6 +158,10 @@
     private SSLContext sslContext;
     
     private ProxyConfiguration proxyConfig;
+    
+    private volatile long requestStartTime = 0L;
+    
+    private volatile long connectStartTime = 0L;
 
     /**
      * Instantiates a new http request message.
@@ -578,5 +582,54 @@
      */
     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;
     }
 }

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/CountingMonitor.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/CountingMonitor.java?rev=614952&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/CountingMonitor.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/CountingMonitor.java Thu Jan 24 10:24:21 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/AsyncHttpClient/src/main/java/org/apache/ahc/util/CountingMonitor.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

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

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java?rev=614952&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java Thu Jan 24 10:24:21 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/AsyncHttpClient/src/main/java/org/apache/ahc/util/DaemonThreadFactory.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

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

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/EventDispatcher.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/EventDispatcher.java?rev=614952&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/EventDispatcher.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/EventDispatcher.java Thu Jan 24 10:24:21 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/AsyncHttpClient/src/main/java/org/apache/ahc/util/EventDispatcher.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

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

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringEvent.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringEvent.java?rev=614952&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringEvent.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringEvent.java Thu Jan 24 10:24:21 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/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringEvent.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

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

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringListener.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringListener.java?rev=614952&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringListener.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringListener.java Thu Jan 24 10:24:21 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/AsyncHttpClient/src/main/java/org/apache/ahc/util/MonitoringListener.java
------------------------------------------------------------------------------
    svn:eol-style = native

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

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

Added: geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/TimeMonitor.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/TimeMonitor.java?rev=614952&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/TimeMonitor.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/main/java/org/apache/ahc/util/TimeMonitor.java Thu Jan 24 10:24:21 2008
@@ -0,0 +1,66 @@
+/*
+ *  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. 
+ */
+public final class TimeMonitor extends CountingMonitor {
+	private final AtomicInteger requestCount = new AtomicInteger();
+	private final AtomicLong requestTimes = new AtomicLong();
+
+	@Override
+    /**
+     * Process a notification event.  If this is a 
+     * REQUEST_COMPLETED event, the request timeing 
+     * 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) {
+			requestCount.incrementAndGet();
+			long elapsed = event.getTimeStamp() - event.getRequest().getRequestStartTime();
+			requestTimes.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();
+	}
+}

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

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

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

Added: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/MonitoringTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/MonitoringTest.java?rev=614952&view=auto
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/MonitoringTest.java (added)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/MonitoringTest.java Thu Jan 24 10:24:21 2008
@@ -0,0 +1,179 @@
+/*
+ *  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.io.File;
+import java.io.FileInputStream;
+import java.net.URL;
+import java.util.Arrays;
+import java.util.concurrent.TimeUnit;
+
+import org.apache.ahc.codec.HttpRequestMessage;
+import org.apache.ahc.codec.HttpResponseMessage;
+import org.apache.ahc.util.CountingMonitor;
+import org.apache.ahc.util.MonitoringEvent;
+import org.apache.ahc.util.MonitoringListener; 
+
+public class MonitoringTest extends AbstractTest {
+
+    public void testHtmlConnection() throws Exception {
+        TestCallback callback = new TestCallback();
+        CountingMonitor counter = new CountingMonitor(); 
+        doGetConnection(callback, "http://localhost:8282/", false, true, counter);
+
+        HttpResponseMessage msg = callback.getMessage();
+        assertEquals("\nHello World!", msg.getStringContent());
+        
+        // the monitor events are dispatched asynchronously, so give a little time 
+        // for them all to be dispatched. 
+        Thread.sleep(500); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_STARTED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_COMPLETED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_FAILED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_TIMEOUT), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_ATTEMPTED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_RETRIED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_REUSED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_SUCCESSFUL), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_REDIRECTED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_CHALLENGED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_CLOSED_BY_SERVER), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_CLOSED), 0); 
+    }
+
+    public void testSSLHtmlConnection() throws Exception {
+        TestCallback callback = new TestCallback();
+        CountingMonitor counter = new CountingMonitor(); 
+        doGetConnection(callback, "https://localhost:8383/", false, true, counter);
+
+        HttpResponseMessage msg = callback.getMessage();
+        assertEquals("\nHello World!", msg.getStringContent());
+        // the monitor events are dispatched asynchronously, so give a little time 
+        // for them all to be dispatched. 
+        Thread.sleep(500); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_STARTED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_COMPLETED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_FAILED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_TIMEOUT), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_ATTEMPTED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_RETRIED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_REUSED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_SUCCESSFUL), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_REDIRECTED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_CHALLENGED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_CLOSED_BY_SERVER), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_CLOSED), 0); 
+    }
+
+    public void testRedirect() throws Exception {
+        TestCallback callback = new TestCallback();
+
+        CountingMonitor counter = new CountingMonitor(); 
+        //Test that we are following redirects
+        doGetConnection(callback, "http://localhost:8282/redirect.jsp", false, true, counter);
+
+        HttpResponseMessage msg = callback.getMessage();
+        assertEquals("\nHello World!", msg.getStringContent());
+        // the monitor events are dispatched asynchronously, so give a little time 
+        // for them all to be dispatched. 
+        Thread.sleep(500); 
+        
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_STARTED), 2); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_COMPLETED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_FAILED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_TIMEOUT), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_ATTEMPTED), 2); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_RETRIED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_REUSED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_SUCCESSFUL), 2); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_REDIRECTED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_CHALLENGED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_CLOSED_BY_SERVER), 2); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_CLOSED), 0); 
+        
+        counter.clearCounters(); 
+
+        //Test that we are not following redirects
+        callback = new TestCallback();
+        doGetConnection(callback, "http://localhost:8282/redirect.jsp", false, false, counter);
+
+        msg = callback.getMessage();
+        assertEquals(302, msg.getStatusCode());
+        assertEquals(msg.getLocation(), "http://localhost:8282/index.jsp"); 
+        
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_STARTED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_COMPLETED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_FAILED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_TIMEOUT), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_ATTEMPTED), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_RETRIED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_REUSED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_SUCCESSFUL), 1); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_REDIRECTED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.REQUEST_CHALLENGED), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_CLOSED_BY_SERVER), 0); 
+        assertEquals(counter.getCount(MonitoringEvent.CONNECTION_CLOSED), 0); 
+    }
+
+    private void doGetConnection(TestCallback callback, String url,
+                                 boolean testForException, boolean followRedirects, 
+                                 MonitoringListener listener)  
+        throws Exception {
+        HttpRequestMessage request = new HttpRequestMessage(new URL(url), callback);
+        request.setFollowRedirects(followRedirects);
+
+        request.setParameter("TEST1", "Test One");
+        request.setParameter("TEST2", "Test Two");
+        doConnection(request, false, callback, listener);
+    }
+
+    private void doPostConnection(TestCallback callback, String url, 
+                                  boolean testForException, MonitoringListener listener)
+        throws Exception {
+        HttpRequestMessage request = new HttpRequestMessage(new URL(url), callback);
+        request.setParameter("TEST1", "Test One");
+        request.setParameter("TEST2", "Test Two");
+        request.setRequestMethod(HttpRequestMessage.REQUEST_POST);
+        doConnection(request, false, callback, listener);
+    }
+
+    private void doConnection(HttpRequestMessage request, boolean testForException, 
+                              TestCallback callback, MonitoringListener listener) throws Exception {
+        AsyncHttpClient ahc = new AsyncHttpClient();
+        ahc.addMonitoringListener(listener); 
+        ahc.setTcpNoDelay(true);
+
+        ahc.sendRequest(request);
+
+        //We are done...Thread would normally end...
+        //So this little wait simulates the thread going back in the pool
+        //5 second timeout due to no response
+        callback.await(5, TimeUnit.SECONDS);
+
+        if (!testForException) {
+            if (((TestCallback)request.getCallback()).isException()) {
+                throw new Exception(((TestCallback)request.getCallback()).getThrowable());
+            }
+        }
+
+    }
+
+}
+

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

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

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

Modified: geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/RetryTest.java
URL: http://svn.apache.org/viewvc/geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/RetryTest.java?rev=614952&r1=614951&r2=614952&view=diff
==============================================================================
--- geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/RetryTest.java (original)
+++ geronimo/sandbox/AsyncHttpClient/src/test/java/org/apache/ahc/RetryTest.java Thu Jan 24 10:24:21 2008
@@ -19,47 +19,49 @@
  */
 package org.apache.ahc;
 
-import java.io.File;
-import java.io.FileInputStream;
 import java.net.URL;
-import java.util.Arrays;
 import java.util.concurrent.TimeUnit;
 
 import org.apache.ahc.codec.HttpRequestMessage;
-import org.apache.ahc.codec.HttpResponseMessage;
+import org.apache.ahc.util.CountingMonitor;
+import org.apache.ahc.util.MonitoringEvent;
 
 public class RetryTest extends AbstractTest {
 
 
     public void testHtmlConnection() throws Exception {
         TestCallback callback = new TestCallback();
-        doGetConnection(callback, "http://localhost:8284/");
+        CountingMonitor counter = doGetConnection(callback, "http://localhost:8284/");
         Thread.sleep(5000); 
         assertTrue(callback.isException());
+        assertTrue(counter.getCount(MonitoringEvent.CONNECTION_RETRIED) == 3); 
     }
 
     public void testSSLHtmlConnection() throws Exception {
         TestCallback callback = new TestCallback();
-        doGetConnection(callback, "https://localhost:8385/");
+        CountingMonitor counter = doGetConnection(callback, "https://localhost:8385/");
         
         Thread.sleep(5000); 
         assertTrue(callback.isException());
+        assertTrue(counter.getCount(MonitoringEvent.CONNECTION_RETRIED) == 3); 
     }
 
-    private void doGetConnection(TestCallback callback, String url)
+    private CountingMonitor doGetConnection(TestCallback callback, String url)
         throws Exception {
         HttpRequestMessage request = new HttpRequestMessage(new URL(url), callback);
 
         request.setParameter("TEST1", "Test One");
         request.setParameter("TEST2", "Test Two");
-        doConnection(request, callback);
-    }
+        return doConnection(request, callback);
+    }          
 
-    private void doConnection(HttpRequestMessage request,
+    private CountingMonitor doConnection(HttpRequestMessage request,
                               TestCallback callback) throws Exception {
         AsyncHttpClient ahc = new AsyncHttpClient();
         ahc.setTcpNoDelay(true);
         ahc.setConnectionRetries(3); 
+        CountingMonitor counter = new CountingMonitor(); 
+        ahc.addMonitoringListener(counter); 
         // set a short timeout 
         request.setTimeOut(200); 
 
@@ -69,6 +71,8 @@
         //So this little wait simulates the thread going back in the pool
         //5 second timeout due to no response
         callback.await(5, TimeUnit.SECONDS);
+        // and return our monitor 
+        return counter; 
     }
 }