package net.sourceforge.tuned; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLEncoder; import java.nio.ByteBuffer; import java.nio.channels.Channels; import java.nio.channels.ReadableByteChannel; import java.nio.channels.WritableByteChannel; import java.nio.charset.Charset; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.logging.Level; import java.util.logging.Logger; import javax.swing.SwingWorker; public class DownloadTask extends SwingWorker { public static final String DOWNLOAD_STATE = "download state"; public static final String DOWNLOAD_PROGRESS = "download progress"; public static enum DownloadState { PENDING, CONNECTING, DOWNLOADING, DONE } private URL url; private long contentLength = -1; private DownloadState state = DownloadState.PENDING; private Map postParameters; private Map requestHeaders; private Map> responseHeaders; public DownloadTask(URL url) { this.url = url; } protected HttpURLConnection createConnection() throws Exception { HttpURLConnection connection = (HttpURLConnection) url.openConnection(); if (requestHeaders != null) { for (Entry entry : requestHeaders.entrySet()) { connection.addRequestProperty(entry.getKey(), entry.getValue()); } } return connection; } @Override protected ByteBuffer doInBackground() throws Exception { setDownloadState(DownloadState.CONNECTING); HttpURLConnection connection = createConnection(); if (postParameters != null) { ByteBuffer postData = encodeParameters(postParameters); // add content type and content length headers connection.addRequestProperty("Content-Type", "application/x-www-form-urlencoded"); connection.addRequestProperty("Content-Length", String.valueOf(postData.remaining())); connection.setRequestMethod("POST"); connection.setDoOutput(true); // write post data WritableByteChannel out = Channels.newChannel(connection.getOutputStream()); out.write(postData); out.close(); } contentLength = connection.getContentLength(); responseHeaders = connection.getHeaderFields(); setDownloadState(DownloadState.DOWNLOADING); ReadableByteChannel in = Channels.newChannel(connection.getInputStream()); ByteBufferOutputStream buffer = new ByteBufferOutputStream((int) (contentLength > 0 ? contentLength : 32 * 1024)); try { while (!isCancelled() && ((buffer.transferFrom(in)) >= 0)) { firePropertyChange(DOWNLOAD_PROGRESS, null, buffer.position()); if (isContentLengthKnown()) { setProgress((int) ((buffer.position() * 100) / contentLength)); } } } catch (IOException e) { // if the content length is not known in advance an IOException (Premature EOF) // is always thrown after all the data has been read if (isContentLengthKnown()) throw e; } finally { in.close(); // download either finished or an exception is thrown setDownloadState(DownloadState.DONE); } return buffer.getByteBuffer(); } protected void setDownloadState(DownloadState state) { this.state = state; firePropertyChange(DOWNLOAD_STATE, null, state); } public DownloadState getDownloadState() { return state; } public URL getUrl() { return url; } public boolean isContentLengthKnown() { return contentLength >= 0; } public long getContentLength() { return contentLength; } public void setRequestHeaders(Map requestHeaders) { this.requestHeaders = new HashMap(requestHeaders); } public void setPostParameters(Map postParameters) { this.postParameters = new HashMap(postParameters); } public Map> getResponseHeaders() { return responseHeaders; } public Map getPostParameters() { return postParameters; } public Map getRequestHeaders() { return requestHeaders; } protected ByteBuffer encodeParameters(Map parameters) { StringBuilder sb = new StringBuilder(); for (Entry entry : parameters.entrySet()) { if (sb.length() > 0) sb.append("&"); sb.append(entry.getKey()); sb.append("="); try { sb.append(URLEncoder.encode(entry.getValue(), "UTF-8")); } catch (UnsupportedEncodingException e) { // will never happen Logger.getLogger("global").log(Level.SEVERE, e.toString(), e); } } return Charset.forName("UTF-8").encode(sb.toString()); } }