Hi, Which version of HttpCore NIO are you using?
That looks like it may be a bug which would need raising in JIRA. The Android docs [1] say that 1, 4, 8 and 16 are valid values for the bitmask ops parameter (or use the constants defined on SelectionKey); the code [2] is currently passing in 0. The JavaDoc says that may throw an IllegalArgumentException in that case [3]. I'm not familiar enough with the code to know what the intent of the code is at that point. Cheers, James [1] http://developer.android.com/reference/java/nio/channels/SelectionKey.html#OP_ACCEPT [2] http://svn.apache.org/repos/asf/httpcomponents/httpcore/trunk/httpcore-nio/src/main/java/org/apache/http/impl/nio/reactor/DefaultConnectingIOReactor.java [3] http://java.sun.com/j2se/1.5.0/docs/api/java/nio/channels/SelectableChannel.html#register(java.nio.channels.Selector, int) On 29 April 2010 10:51, ching <[email protected]> wrote: > I'm implementing a download application that works like android's > DownloadProvider but based HttpCore NIO. > > A fatal problem I encountered is this: > 04-29 09:26:05.419: ERROR/AndroidRuntime(332): > java.nio.channels.IllegalSelectorException > 04-29 09:26:05.419: ERROR/AndroidRuntime(332): at > java.nio.channels.spi.AbstractSelectableChannel.register(AbstractSelectableChannel.java:158) > 04-29 09:26:05.419: ERROR/AndroidRuntime(332): at > java.nio.channels.SelectableChannel.register(SelectableChannel.java:158) > 04-29 09:26:05.419: ERROR/AndroidRuntime(332): at > org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processSessionRequests(DefaultConnectingIOReactor.java:244) > 04-29 09:26:05.419: ERROR/AndroidRuntime(332): at > org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor.processEvents(DefaultConnectingIOReactor.java:97) > 04-29 09:26:05.419: ERROR/AndroidRuntime(332): at > org.apache.http.impl.nio.reactor.AbstractMultiworkerIOReactor.execute(AbstractMultiworkerIOReactor.java:317) > 04-29 09:26:05.419: ERROR/AndroidRuntime(332): at > io.DownloadClient.run(DownloadClient.java:168) > 04-29 09:26:05.419: ERROR/AndroidRuntime(332): at > java.lang.Thread.run(Thread.java:1096) > > however it can work on real j2se6, is that unsupported on android at all? > > And a minor problem: Will "ioReactor.execute(ioEventDispatch)" really throw > out a InterruptedIOException? > I found it will be never even when I interrupt the thread explicitly. > shutdown can't be done gracefully without this. > > > Thanks for help, my raw codes for testing: > //********** io.DownloadClient.java ********** > package io; > > import java.io.File; > import java.io.IOException; > import java.io.InterruptedIOException; > import java.net.InetSocketAddress; > import java.net.URI; > import java.util.Date; > import java.util.concurrent.ExecutorService; > import java.util.concurrent.Executors; > > import org.apache.http.HttpException; > import org.apache.http.HttpHost; > import org.apache.http.HttpRequest; > import org.apache.http.HttpResponse; > import org.apache.http.impl.DefaultConnectionReuseStrategy; > import org.apache.http.impl.nio.DefaultClientIOEventDispatch; > import org.apache.http.impl.nio.reactor.DefaultConnectingIOReactor; > import org.apache.http.nio.ContentDecoder; > import org.apache.http.nio.FileContentDecoder; > import org.apache.http.nio.NHttpClientConnection; > import org.apache.http.nio.NHttpClientHandler; > import org.apache.http.nio.entity.ConsumingNHttpEntity; > import org.apache.http.nio.protocol.AsyncNHttpClientHandler; > import org.apache.http.nio.protocol.NHttpRequestExecutionHandler; > import org.apache.http.nio.reactor.ConnectingIOReactor; > import org.apache.http.nio.reactor.IOEventDispatch; > import org.apache.http.nio.util.HeapByteBufferAllocator; > import org.apache.http.params.BasicHttpParams; > import org.apache.http.params.CoreConnectionPNames; > import org.apache.http.params.CoreProtocolPNames; > import org.apache.http.params.HttpParams; > import org.apache.http.protocol.BasicHttpProcessor; > import org.apache.http.protocol.HttpContext; > 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; > > public class DownloadClient implements Runnable { > public static final String TAG = "nioDownload"; > > Thread dispatcher; > ExecutorService workers; > > ConnectingIOReactor reactor; > IOEventDispatch eventDispatch; > > public DownloadClient() throws IOException { > BasicHttpProcessor httpproc = new BasicHttpProcessor(); > httpproc.addInterceptor(new RequestContent()); > httpproc.addInterceptor(new RequestTargetHost()); > httpproc.addInterceptor(new RequestConnControl()); > httpproc.addInterceptor(new RequestUserAgent()); > httpproc.addInterceptor(new RequestExpectContinue()); > HttpParams params = new BasicHttpParams(); > params.setParameter(CoreProtocolPNames.USER_AGENT, > "HttpComponents/1.1"); > params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT, 5000); > params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT, 10000); > params.setIntParameter(CoreConnectionPNames.SOCKET_BUFFER_SIZE, 8 * > 1024); > params.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK, > false); > params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY, true); > NHttpRequestExecutionHandler execHandler = new > NHttpRequestExecutionHandler() { > HttpRequest request = null; > @Override > public void initalizeContext(HttpContext context, Object attachment) { > if(attachment != null && attachment instanceof DownloadCourse) { > DownloadCourse course = (DownloadCourse)attachment; > request = course.getRequest(); > context.setAttribute(TAG, attachment); > } > } > @Override > public HttpRequest submitRequest(HttpContext context) { > HttpRequest result = this.request; > if(result != null) { > this.request = null; > } > return result; > } > @Override > public ConsumingNHttpEntity responseEntity(HttpResponse response, > HttpContext context) > throws IOException { > //nothing, handle in NHttpClientHandler.inputReady > return null; > } > @Override > public void handleResponse(HttpResponse response, HttpContext context) > throws IOException { > //nothing, handle in NHttpClientHandler.inputReady > } > @Override > public void finalizeContext(HttpContext context) { > context.removeAttribute(TAG); > } > }; > NHttpClientHandler handler = new AsyncNHttpClientHandler( > httpproc, execHandler, new DefaultConnectionReuseStrategy(), > new HeapByteBufferAllocator(), params) { > @Override > public void closed(NHttpClientConnection conn) { > super.closed(conn); > Object attachment = conn.getContext().getAttribute(TAG); > if(attachment != null && attachment instanceof DownloadCourse) { > DownloadCourse course = (DownloadCourse)attachment; > course.interrupt(); > } > } > @Override > public void exception(NHttpClientConnection conn, HttpException ex) { > super.exception(conn, ex); > Object attachment = conn.getContext().getAttribute(TAG); > if(attachment != null && attachment instanceof DownloadCourse) { > DownloadCourse course = (DownloadCourse)attachment; > course.error(ex); > } > } > @Override > public void exception(NHttpClientConnection conn, IOException ex) { > super.exception(conn, ex); > Object attachment = conn.getContext().getAttribute(TAG); > if(attachment != null && attachment instanceof DownloadCourse) { > DownloadCourse course = (DownloadCourse)attachment; > course.error(ex); > } > } > @Override > public void inputReady(NHttpClientConnection conn, ContentDecoder > content) { > //don't invoke super, handle by self > log("inputReady: %s", content); > Object attachment = conn.getContext().getAttribute(TAG); > if(attachment != null && attachment instanceof DownloadCourse) { > DownloadCourse course = (DownloadCourse)attachment; > course.update((FileContentDecoder)content); > if(content.isCompleted()) { > course.complete(conn.getHttpResponse()); > } > } > } > @Override > public void responseReceived(NHttpClientConnection conn) { > super.responseReceived(conn); > Object attachment = conn.getContext().getAttribute(TAG); > if(attachment != null && attachment instanceof DownloadCourse) { > DownloadCourse course = (DownloadCourse)attachment; > course.prepare(conn.getHttpResponse()); > } > } > @Override > public void timeout(NHttpClientConnection conn) { > super.timeout(conn); > Object attachment = conn.getContext().getAttribute(TAG); > if(attachment != null && attachment instanceof DownloadCourse) { > DownloadCourse course = (DownloadCourse)attachment; > course.interrupt(); > } > } > }; > eventDispatch = new DefaultClientIOEventDispatch(handler, params); > reactor = new DefaultConnectingIOReactor(1, params); > } > > @Override > public void run() { > log("DownloadClient run at %s", new Date()); > try { > reactor.execute(eventDispatch); > } catch(InterruptedIOException e) { > //never thrown? even when being interrupted? > log("DownloadClient interrupted"); > } catch(IOException e) { > e.printStackTrace(); > } finally { > try { > reactor.shutdown(); > } catch(IOException e) { > e.printStackTrace(); > } > } > } > > public void execute(DownloadCourse course) { > HttpHost host = course.getHost(); > reactor.connect( > new InetSocketAddress(host.getHostName(), host.getPort() > 0 ? > host.getPort() : 80), null, > course, null); > } > > public void startup() { > dispatcher = new Thread(this, "DownloadClient Thread"); > dispatcher.setDaemon(true); > dispatcher.start(); > workers = Executors.newFixedThreadPool(2); > } > > public void shutdown() { > dispatcher.interrupt(); > workers.shutdown(); > } > > public void download(URI uri, File saveFile) throws IOException { > DownloadCourse course = new DownloadCourse(this, uri, saveFile); > workers.submit(course); > } > > public static void log(Object s) { > System.out.println(s); > } > > public static void log(String format, Object... a) { > System.out.println(String.format(format, a)); > } > > public static void main(String[] args) throws Exception { > //startup client > DownloadClient client = new DownloadClient(); > client.startup(); > > //submit request > File saveFile = new File("/sdcard/download.txt"); > if(!saveFile.exists()) saveFile.createNewFile(); > client.download(new URI("http://www.google.com/"), saveFile); > > //for a while... > Thread.sleep(10000); > > //clear for end > client.shutdown(); > } > > } > > > //********** io.DownloadCourse.java ********** > package io; > > import static io.DownloadClient.log; > > import java.io.File; > import java.io.IOException; > import java.io.RandomAccessFile; > import java.net.URI; > import java.nio.channels.FileChannel; > import java.util.Date; > > import org.apache.commons.io.FileUtils; > import org.apache.http.Header; > import org.apache.http.HttpHost; > import org.apache.http.HttpRequest; > import org.apache.http.HttpResponse; > import org.apache.http.message.BasicHttpRequest; > import org.apache.http.nio.FileContentDecoder; > > public class DownloadCourse implements Runnable { > > public static final int maxRetryTimes = 5; > public static final byte QUEUING_STATUS = 0; > public static final byte DOWNLOADING = 1; > public static final byte PAUSED = 15; > public static final byte FINISHED = 127; > public static final byte VIOLATED = -1; > public static final byte TERMINATED = -128; > > DownloadClient client; > URI uri; > File file; > RandomAccessFile randomAccessFile; > FileChannel fileChannel; > long contentLength; > long position; > int status; > int retryTimes; > > public DownloadCourse(DownloadClient client, URI uri, File file) { > this.client = client; > this.uri = uri; > this.file = file; > status = QUEUING_STATUS; > } > > @Override > public void run() { > log("DownloadCourse run at %s", new Date()); > status = QUEUING_STATUS; > client.execute(this); > } > > public HttpHost getHost() { > return uri.getPort() > 0 ? > new HttpHost(uri.getHost(), uri.getPort()) : > new HttpHost(uri.getHost()); > } > > public HttpRequest getRequest() { > HttpRequest request = uri.getQuery() != null ? > new BasicHttpRequest("GET", uri.getPath() + "?" + uri.getQuery()) : > new BasicHttpRequest("GET", uri.getPath()); > request.setHeader("Host", getHost().toHostString()); > return request; > } > > public void prepare(HttpResponse response) { > Header contentLengthHeader = response.getFirstHeader("Content-Length"); > if(contentLengthHeader != null) { > contentLength = Integer.parseInt(contentLengthHeader.getValue().trim()); > } > try { > randomAccessFile = new RandomAccessFile(file, "rw"); > if(contentLength > 0) { > randomAccessFile.setLength(contentLength); > } > fileChannel = randomAccessFile.getChannel(); > } catch(IOException e) { > throw new RuntimeException(e); > } > status = DOWNLOADING; > } > > public void update(FileContentDecoder content) { > long length = 0; > try { > length = content.transfer(fileChannel, position, contentLength); > } catch(IOException e) { > throw new RuntimeException(e); > } > position += length; > log("download progress: %d/%d", position, contentLength); > } > > public void complete(HttpResponse response) { > try { > randomAccessFile.close(); > log(response.getStatusLine().toString()); > log(new String(FileUtils.readFileToByteArray(file))); > } catch(IOException e) { > throw new RuntimeException(e); > } > status = FINISHED; > } > > public void error(Exception ex) { > try { > randomAccessFile.close(); > } catch(IOException e) { > throw new RuntimeException(e); > } > if(++retryTimes < maxRetryTimes) { > status = VIOLATED; > } else { > status = TERMINATED; > } > } > > public void interrupt() { > try { > randomAccessFile.close(); > } catch(IOException e) { > throw new RuntimeException(e); > } > status = PAUSED; > } > > public String toString() { > return String.format("%s\n\turi: %s\n\tfile: %s\n\tstatus: > %d\n\tcontentLength: %d\n\tposition:%d", super.toString(), uri, file, > status, contentLength, position); > } > > } > >
