hc-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ol...@apache.org
Subject svn commit: r453255 - in /jakarta/httpcomponents/httpcore/trunk/module-nio/src: examples/org/apache/http/nio/examples/ main/java/org/apache/http/nio/ main/java/org/apache/http/nio/impl/
Date Thu, 05 Oct 2006 14:40:40 GMT
Author: olegk
Date: Thu Oct  5 07:40:39 2006
New Revision: 453255

URL: http://svn.apache.org/viewvc?view=rev&rev=453255
Log:
[HTTPCORE-9] Provide non-blocking (async) server side I/O transport based on NIO
[HTTPCORE-10] Provide non-blocking (async) client side I/O transport based on NIO

Changelog:
=========
* A number of bug fixes
* Correctly implemented connect finisning logic
* Connect timeout does not cancel the session request. The connect timeout can be optionally
increased
* I/O exception that occurred during the process of establishing a connection is now correctly
propagated to the caller
* Added a rudimentary async client example

Added:
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/examples/org/apache/http/nio/examples/AsyncHttpClient.java
  (with props)
Modified:
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequest.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequestCallback.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/AsyncHttpClientConnection.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/DefaultIOReactor.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/IOSessionImpl.java
    jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/SessionRequestImpl.java

Added: jakarta/httpcomponents/httpcore/trunk/module-nio/src/examples/org/apache/http/nio/examples/AsyncHttpClient.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/examples/org/apache/http/nio/examples/AsyncHttpClient.java?view=auto&rev=453255
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/examples/org/apache/http/nio/examples/AsyncHttpClient.java
(added)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/examples/org/apache/http/nio/examples/AsyncHttpClient.java
Thu Oct  5 07:40:39 2006
@@ -0,0 +1,257 @@
+package org.apache.http.nio.examples;
+
+import java.io.IOException;
+import java.io.InterruptedIOException;
+import java.net.InetSocketAddress;
+import java.net.SocketTimeoutException;
+
+import org.apache.http.ConnectionReuseStrategy;
+import org.apache.http.HttpClientConnection;
+import org.apache.http.HttpConnection;
+import org.apache.http.HttpException;
+import org.apache.http.HttpHost;
+import org.apache.http.HttpResponse;
+import org.apache.http.impl.DefaultConnectionReuseStrategy;
+import org.apache.http.impl.DefaultHttpParams;
+import org.apache.http.message.HttpGet;
+import org.apache.http.nio.IOConsumer;
+import org.apache.http.nio.IOEventDispatch;
+import org.apache.http.nio.IOProducer;
+import org.apache.http.nio.IOReactor;
+import org.apache.http.nio.IOSession;
+import org.apache.http.nio.SessionRequest;
+import org.apache.http.nio.SessionRequestCallback;
+import org.apache.http.nio.impl.AsyncHttpClientConnection;
+import org.apache.http.nio.impl.DefaultIOReactor;
+import org.apache.http.params.HttpConnectionParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.HttpProtocolParams;
+import org.apache.http.protocol.BasicHttpProcessor;
+import org.apache.http.protocol.HttpContext;
+import org.apache.http.protocol.HttpExecutionContext;
+import org.apache.http.protocol.HttpRequestExecutor;
+import org.apache.http.protocol.RequestConnControl;
+import org.apache.http.protocol.RequestContent;
+import org.apache.http.protocol.RequestExpectContinue;
+import org.apache.http.protocol.RequestTargetHost;
+import org.apache.http.protocol.RequestUserAgent;
+import org.apache.http.util.EntityUtils;
+
+public class AsyncHttpClient {
+
+    public static void main(String[] args) throws Exception {
+        HttpParams params = new DefaultHttpParams(null);
+        params
+            .setIntParameter(HttpConnectionParams.SO_TIMEOUT, 5000)
+            .setIntParameter(HttpConnectionParams.CONNECTION_TIMEOUT, 10000)
+            .setIntParameter(HttpConnectionParams.SOCKET_BUFFER_SIZE, 8 * 1024)
+            .setBooleanParameter(HttpConnectionParams.STALE_CONNECTION_CHECK, false)
+            .setBooleanParameter(HttpConnectionParams.TCP_NODELAY, true)
+            .setParameter(HttpProtocolParams.USER_AGENT, "Jakarta-HttpComponents-NIO/1.1");
+
+        final IOEventDispatch ioEventDispatch = new DefaultIoEventDispatch(params);
+        final IOReactor ioReactor = new DefaultIOReactor(params);
+
+        SessionRequestCallback sessionReqCallback = new DefaultSessionRequestCallback(params);
+        
+        SessionRequest req1 = ioReactor.connect(
+                new InetSocketAddress("www.yahoo.com", 80), null);
+        req1.setCallback(sessionReqCallback);
+        
+        SessionRequest req2 = ioReactor.connect(
+                new InetSocketAddress("www.google.com", 80), null);
+        req2.setCallback(sessionReqCallback);
+        
+        SessionRequest req3 = ioReactor.connect(
+                new InetSocketAddress("www.apache.org", 80), null);
+        req3.setCallback(sessionReqCallback);
+        
+        Thread ioThread = new Thread(new Runnable() {
+
+            public void run() {
+                try {
+                    
+                    ioReactor.execute(ioEventDispatch);
+                } catch (InterruptedIOException ex) {
+                    System.err.println("Interrupted");
+                } catch (IOException e) {
+                    System.err.println("I/O error: " + e.getMessage());
+                }
+                System.out.println("Shutdown");
+            }
+            
+        });
+        ioThread.setDaemon(true);
+        ioThread.start();
+        req1.waitFor();
+        req2.waitFor();
+        req3.waitFor();
+    }
+
+    private static final String CONSUMER = "CONSUMER";
+    private static final String PRODUCER = "PRODUCER";
+    private static final String CONNECTION = "CONNECTION";
+    private static final String TARGET = "TARGET";
+    private static final String WORKER = "WORKER";
+    
+    static class DefaultSessionRequestCallback implements SessionRequestCallback {
+
+        private final HttpParams params;
+        
+        public DefaultSessionRequestCallback(final HttpParams params) {
+            super();
+            if (params == null) {
+                throw new IllegalArgumentException("HTTP parameters may nor be null");
+            }
+            this.params = params;
+        }
+        
+        public void completed(final SessionRequest request) {
+            IOSession session = request.getSession();
+            InetSocketAddress address = (InetSocketAddress) request.getRemoteAddress();
+            
+            HttpHost targetHost = new HttpHost(address.getHostName(), address.getPort());

+            
+            AsyncHttpClientConnection conn = new AsyncHttpClientConnection(session, this.params);

+            session.setAttribute(CONNECTION, conn);
+            session.setAttribute(TARGET, targetHost);
+            session.setAttribute(CONSUMER, conn.getIOConsumer());
+            session.setAttribute(PRODUCER, conn.getIOProducer());
+            
+        }
+
+        public void failed(final SessionRequest request) {
+            IOException ex = request.getException();
+            System.err.println(request.getRemoteAddress() + ": Connection failed");
+            ex.printStackTrace();
+        }
+
+        public void timeout(final SessionRequest request) {
+            request.cancel();
+            System.err.println(request.getRemoteAddress() + ": Connection timeout");
+        }
+
+    }    
+    
+    static class DefaultIoEventDispatch implements IOEventDispatch {
+
+        public DefaultIoEventDispatch(final HttpParams params) {
+            super();
+        }
+        
+        public void connected(final IOSession session) {
+            
+            HttpClientConnection conn = (HttpClientConnection) session.getAttribute(CONNECTION);
+            HttpHost targetHost = (HttpHost) session.getAttribute(TARGET);
+            HttpContext localcontext = new HttpExecutionContext(null);
+            localcontext.setAttribute(HttpExecutionContext.HTTP_TARGET_HOST, targetHost);
+            
+            // Set up HTTP executor
+            BasicHttpProcessor httpproc = new BasicHttpProcessor();
+            httpproc.addInterceptor(new RequestContent());
+            httpproc.addInterceptor(new RequestTargetHost());
+            httpproc.addInterceptor(new RequestConnControl());
+            httpproc.addInterceptor(new RequestUserAgent());
+            httpproc.addInterceptor(new RequestExpectContinue());
+            HttpRequestExecutor httpexecutor = new HttpRequestExecutor(httpproc); 
+            
+            Thread worker = new WorkerThread(httpexecutor, conn, localcontext);
+            session.setAttribute(WORKER, worker);
+
+            worker.setDaemon(false);
+            worker.start();
+            session.setSocketTimeout(20000);
+        }
+
+        public void inputReady(final IOSession session) {
+            IOConsumer consumer = (IOConsumer) session.getAttribute(CONSUMER);
+            try {
+                consumer.consumeInput();
+            } catch (IOException ex) {
+                consumer.shutdown(ex);
+            }
+        }
+
+        public void outputReady(final IOSession session) {
+            IOProducer producer = (IOProducer) session.getAttribute(PRODUCER);
+            try {
+                producer.produceOutput();
+            } catch (IOException ex) {
+                producer.shutdown(ex);
+            }
+        }
+
+        public void timeout(final IOSession session) {
+            IOConsumer consumer = (IOConsumer) session.getAttribute(CONSUMER);
+            consumer.shutdown(new SocketTimeoutException("Socket read timeout"));
+        }
+        
+        public void disconnected(final IOSession session) {
+            HttpConnection conn = (HttpConnection) session.getAttribute(CONNECTION);
+            try {
+                conn.shutdown();
+            } catch (IOException ex) {
+                System.err.println("I/O error while shutting down connection");
+            }
+            
+            Thread worker = (Thread) session.getAttribute(WORKER);
+            worker.interrupt();
+        }
+        
+    }
+    
+    static class WorkerThread extends Thread {
+
+        private final HttpRequestExecutor httpexecutor;
+        private final HttpClientConnection conn;
+        private final HttpContext context;
+        
+        public WorkerThread(
+                final HttpRequestExecutor httpexecutor, 
+                final HttpClientConnection conn, 
+                final HttpContext context) {
+            super();
+            this.httpexecutor = httpexecutor;
+            this.conn = conn;
+            this.context = context;
+        }
+        
+        public void run() {
+            System.out.println("New connection thread");
+            try {
+                String[] targets = {
+                        "/",
+                        "/servlets-examples/servlet/RequestInfoExample", 
+                        "/somewhere%20in%20pampa"};
+                
+                ConnectionReuseStrategy connStrategy = new DefaultConnectionReuseStrategy();
+                
+                for (int i = 0; i < targets.length; i++) {
+                    HttpGet request = new HttpGet(targets[i]);
+                    System.out.println(">> Request URI: " + request.getRequestLine().getUri());
+                    HttpResponse response = httpexecutor.execute(request, this.conn, this.context);
+                    System.out.println("<< Response: " + response.getStatusLine());
+                    System.out.println(EntityUtils.toString(response.getEntity()));
+                    System.out.println("==============");
+                    if (!connStrategy.keepAlive(response, this.context)) {
+                        conn.close();
+                        System.out.println("Connection closed...");
+                        break;
+                    } else {
+                        System.out.println("Connection kept alive...");
+                    }
+                }
+            } catch (IOException ex) {
+                System.err.println("I/O error: " + ex.getMessage());
+            } catch (HttpException ex) {
+                System.err.println("Unrecoverable HTTP protocol violation: " + ex.getMessage());
+            } finally {
+                try {
+                    this.conn.shutdown();
+                } catch (IOException ignore) {}
+            }
+        }
+
+    }
+    
+}

Propchange: jakarta/httpcomponents/httpcore/trunk/module-nio/src/examples/org/apache/http/nio/examples/AsyncHttpClient.java
------------------------------------------------------------------------------
    svn:keywords = Date Author Id Revision HeadURL

Propchange: jakarta/httpcomponents/httpcore/trunk/module-nio/src/examples/org/apache/http/nio/examples/AsyncHttpClient.java
------------------------------------------------------------------------------
    svn:mime-type = text/plain

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequest.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequest.java?view=diff&rev=453255&r1=453254&r2=453255
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequest.java
(original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequest.java
Thu Oct  5 07:40:39 2006
@@ -29,11 +29,18 @@
 
 package org.apache.http.nio;
 
+import java.io.IOException;
+import java.net.SocketAddress;
+
 public interface SessionRequest {
 
+    public SocketAddress getRemoteAddress();
+    
     boolean isCompleted();
     
     IOSession getSession();
+    
+    IOException getException();
 
     void waitFor() throws InterruptedException;
     

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequestCallback.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequestCallback.java?view=diff&rev=453255&r1=453254&r2=453255
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequestCallback.java
(original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/SessionRequestCallback.java
Thu Oct  5 07:40:39 2006
@@ -33,6 +33,8 @@
 
     void completed(SessionRequest request);
     
-    void timeout(SessionRequest request);
+    void failed(SessionRequest request);
     
+    void timeout(SessionRequest request);
+
 }

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/AsyncHttpClientConnection.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/AsyncHttpClientConnection.java?view=diff&rev=453255&r1=453254&r2=453255
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/AsyncHttpClientConnection.java
(original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/AsyncHttpClientConnection.java
Thu Oct  5 07:40:39 2006
@@ -30,7 +30,6 @@
 
 import java.io.IOException;
 
-import org.apache.http.HttpHost;
 import org.apache.http.impl.AbstractHttpClientConnection;
 import org.apache.http.impl.DefaultHttpResponseFactory;
 import org.apache.http.impl.entity.DefaultEntityDeserializer;
@@ -43,19 +42,14 @@
 
 public class AsyncHttpClientConnection extends AbstractHttpClientConnection {
 
-    private final HttpHost targetHost;
     private final IOSession session;
     private final IOProducer ioProducer;
     private final IOConsumer ioConsumer;
     
     public AsyncHttpClientConnection(
-            final HttpHost targetHost, 
             final IOSession session, 
             final HttpParams params) {
         super();
-        if (targetHost == null) {
-            throw new IllegalArgumentException("Target host may not be null");
-        }
         if (session == null) {
             throw new IllegalArgumentException("IO session may not be null");
         }
@@ -63,7 +57,6 @@
             throw new IllegalArgumentException("HTTP parameters may not be null");
         }
         this.session = session;
-        this.targetHost = targetHost;
         int buffersize = HttpConnectionParams.getSocketBufferSize(params);
         
         AsyncHttpDataReceiver datareceiver = new AsyncHttpDataReceiver(
@@ -83,10 +76,6 @@
         setEntityDeserializer(new DefaultEntityDeserializer());
     }
 
-    public HttpHost getTargetHost() {
-        return this.targetHost;
-    }
-    
     public IOConsumer getIOConsumer() {
         return this.ioConsumer;
     }

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/DefaultIOReactor.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/DefaultIOReactor.java?view=diff&rev=453255&r1=453254&r2=453255
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/DefaultIOReactor.java
(original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/DefaultIOReactor.java
Thu Oct  5 07:40:39 2006
@@ -144,25 +144,34 @@
             if (key.isConnectable()) {
 
                 SocketChannel socketChannel = (SocketChannel) key.channel();
-                if (socketChannel != null) {
-                    // Configure new socket
-                    onNewSocket(socketChannel.socket());
-                    // Set up new session
-                    IOSession session = newSession(key);
-
-                    // Get request handle
-                    SessionRequestHandle requestHandle = (SessionRequestHandle) key.attachment();
-                    SessionRequestImpl sessionRequest = requestHandle.getSessionRequest();
-                    
-                    // Attach session handle to the selection key
-                    SessionHandle handle = new SessionHandle(session); 
-                    key.attach(handle);
-                    
-                    this.eventDispatch.connected(session);
-                    
-                    sessionRequest.completed(session);
+                // Get request handle
+                SessionRequestHandle requestHandle = (SessionRequestHandle) key.attachment();
+                SessionRequestImpl sessionRequest = requestHandle.getSessionRequest();
+                
+                // Finish connection process
+                try {
+                    socketChannel.finishConnect();
+                } catch (IOException ex) {
+                    sessionRequest.failed(ex);
+                    key.cancel();
+                    return;
                 }
 
+                // Configure new socket
+                onNewSocket(socketChannel.socket());
+                // Set up new session
+                IOSession session = newSession(key);
+
+                // Attach session handle to the selection key
+                SessionHandle handle = new SessionHandle(session); 
+                key.attach(handle);
+                
+                // Fire the request completion notification first
+                sessionRequest.completed(session);
+                
+                // Followed by session connected notification
+                this.eventDispatch.connected(session);
+
             }
             
             if (key.isReadable()) {
@@ -182,11 +191,12 @@
             }
             
         } catch (CancelledKeyException ex) {
-            SessionHandle handle = (SessionHandle) key.attachment();
-            if (handle != null) {
-                key.attach(null);
+            Object attachment = key.attachment();
+            if (attachment instanceof SessionHandle) {
+                SessionHandle handle = (SessionHandle) attachment;
                 IOSession session = handle.getSession();
                 this.closedSessions.push(session);
+                key.attach(null);
             }
         }
     }
@@ -285,7 +295,7 @@
         socketChannel.connect(remoteAddress);
         SelectionKey key = socketChannel.register(this.selector, SelectionKey.OP_CONNECT);
         
-        SessionRequestImpl sessionRequest = new SessionRequestImpl(key);
+        SessionRequestImpl sessionRequest = new SessionRequestImpl(remoteAddress, key);
         sessionRequest.setConnectTimeout(HttpConnectionParams.getConnectionTimeout(this.params));
 
         SessionRequestHandle requestHandle = new SessionRequestHandle(sessionRequest); 

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/IOSessionImpl.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/IOSessionImpl.java?view=diff&rev=453255&r1=453254&r2=453255
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/IOSessionImpl.java
(original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/IOSessionImpl.java
Thu Oct  5 07:40:39 2006
@@ -38,7 +38,7 @@
 
 import org.apache.http.nio.IOSession;
 
-public class IOSessionImpl implements IOSession {
+class IOSessionImpl implements IOSession {
     
     private volatile boolean closed = false;
     

Modified: jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/SessionRequestImpl.java
URL: http://svn.apache.org/viewvc/jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/SessionRequestImpl.java?view=diff&rev=453255&r1=453254&r2=453255
==============================================================================
--- jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/SessionRequestImpl.java
(original)
+++ jakarta/httpcomponents/httpcore/trunk/module-nio/src/main/java/org/apache/http/nio/impl/SessionRequestImpl.java
Thu Oct  5 07:40:39 2006
@@ -29,28 +29,43 @@
 
 package org.apache.http.nio.impl;
 
+import java.io.IOException;
+import java.net.SocketAddress;
 import java.nio.channels.SelectionKey;
 
 import org.apache.http.nio.SessionRequest;
 import org.apache.http.nio.IOSession;
 import org.apache.http.nio.SessionRequestCallback;
 
-public class SessionRequestImpl implements SessionRequest {
+class SessionRequestImpl implements SessionRequest {
 
     private volatile boolean completed;
 
     private final SelectionKey key;
+    private final SocketAddress remoteAddress;
     
     private int connectTimeout;
     private SessionRequestCallback callback;
     private IOSession session = null;
+    private IOException exception = null;
     
-    public SessionRequestImpl(final SelectionKey key) {
+    public SessionRequestImpl(final SocketAddress remoteAddress, SelectionKey key) {
         super();
+        if (remoteAddress == null) {
+            throw new IllegalArgumentException("Remote address may not be null");
+        }
+        if (key == null) {
+            throw new IllegalArgumentException("Selection key may not be null");
+        }
+        this.remoteAddress = remoteAddress;
         this.key = key;
         this.connectTimeout = 0;
     }
     
+    public SocketAddress getRemoteAddress() {
+        return this.remoteAddress;
+    }
+    
     public boolean isCompleted() {
         return this.completed;
     }
@@ -72,6 +87,12 @@
         }
     }
     
+    public IOException getException() {
+        synchronized (this) {
+            return this.exception;
+        }
+    }
+    
     public void completed(final IOSession session) {
         if (session == null) {
             throw new IllegalArgumentException("Session may not be null");
@@ -89,19 +110,34 @@
         }
     }
  
-    public synchronized void timeout() {
+    public void failed(final IOException exception) {
+        if (exception == null) {
+            return;
+        }
         if (this.completed) {
             throw new IllegalStateException("Session request already completed");
         }
         this.completed = true;
         synchronized (this) {
+            this.exception = exception;
             if (this.callback != null) {
-                this.callback.timeout(this);
+                this.callback.failed(this);
             }
             notifyAll();
         }
     }
  
+    public void timeout() {
+        if (this.completed) {
+            throw new IllegalStateException("Session request already completed");
+        }
+        synchronized (this) {
+            if (this.callback != null) {
+                this.callback.timeout(this);
+            }
+        }
+    }
+ 
     public int getConnectTimeout() {
         return this.connectTimeout;
     }
@@ -114,16 +150,24 @@
     }
 
     public void setCallback(final SessionRequestCallback callback) {
-        if (this.completed) {
-            throw new IllegalStateException("Session request already completed");
-        }
         synchronized (this) {
             this.callback = callback;
+            if (this.completed) {
+                if (this.session != null) {
+                    callback.completed(this);
+                } else {
+                    callback.timeout(this);
+                }
+            }
         }
     }
 
     public void cancel() {
         this.key.cancel();
+        this.completed = true;
+        synchronized (this) {
+            notifyAll();
+        }
     }
     
 }



Mime
View raw message