diff options
Diffstat (limited to 'providers/bundles/org.eclipse.ecf.provider.filetransfer')
3 files changed, 360 insertions, 54 deletions
diff --git a/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/AbstractRetrieveFileTransfer.java b/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/AbstractRetrieveFileTransfer.java index 2839128ce..32e972ee7 100644 --- a/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/AbstractRetrieveFileTransfer.java +++ b/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/AbstractRetrieveFileTransfer.java @@ -11,6 +11,8 @@ ******************************************************************************/ package org.eclipse.ecf.provider.filetransfer.retrieve; +import org.eclipse.ecf.provider.filetransfer.util.PollingInputStream; + import java.io.*; import java.net.MalformedURLException; import java.net.URL; @@ -41,6 +43,8 @@ public abstract class AbstractRetrieveFileTransfer implements private static final int FILETRANSFER_ERRORCODE = 1001; + protected static final int POLLING_RETRY_ATTEMPTS = 20; + protected Job job; protected URL remoteFileURL; @@ -99,13 +103,17 @@ public abstract class AbstractRetrieveFileTransfer implements getRemoteFileURL().toString() + Messages.AbstractRetrieveFileTransfer_Progress_Data, work); + InputStream readInputStream = new PollingInputStream( + remoteFileContents, POLLING_RETRY_ATTEMPTS, monitor); try { while (!isDone() && !isPaused()) { - if (monitor.isCanceled()) + try { + final int bytes = readInputStream.read(buf); + handleReceivedData(buf, bytes, factor, monitor); + } catch (OperationCanceledException e) { throw new UserCancelledException( Messages.AbstractRetrieveFileTransfer_Exception_User_Cancelled); - final int bytes = remoteFileContents.read(buf); - handleReceivedData(buf, bytes, factor, monitor); + } } } catch (final Exception e) { exception = e; diff --git a/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/UrlConnectionRetrieveFileTransfer.java b/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/UrlConnectionRetrieveFileTransfer.java index db2f5c645..f54c82bba 100644 --- a/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/UrlConnectionRetrieveFileTransfer.java +++ b/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/UrlConnectionRetrieveFileTransfer.java @@ -21,7 +21,8 @@ import org.eclipse.ecf.internal.provider.filetransfer.*; import org.eclipse.ecf.provider.filetransfer.util.JREProxyHelper; import org.eclipse.osgi.util.NLS; -public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTransfer { +public class UrlConnectionRetrieveFileTransfer extends + AbstractRetrieveFileTransfer { private static final String USERNAME_PREFIX = Messages.UrlConnectionRetrieveFileTransfer_USERNAME_PROMPT; @@ -31,11 +32,11 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans private static final String JRE_CONNECT_TIMEOUT_PROPERTY = "sun.net.client.defaultConnectTimeout"; //$NON-NLS-1$ - private static final String DEFAULT_CONNECT_TIMEOUT = "30000"; //$NON-NLS-1$ + private static final String DEFAULT_CONNECT_TIMEOUT = "15000"; //$NON-NLS-1$ private static final String JRE_READ_TIMEOUT_PROPERTY = "sun.net.client.defaultReadTimeout"; //$NON-NLS-1$ - private static final String DEFAULT_READ_TIMEOUT = "30000"; //$NON-NLS-1$ + private static final String DEFAULT_READ_TIMEOUT = "1000"; //$NON-NLS-1$ protected URLConnection urlConnection; @@ -58,8 +59,12 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans proxyHelper = new JREProxyHelper(); } - /* (non-Javadoc) - * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#getRemoteFileName() + /* + * (non-Javadoc) + * + * @see + * org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer + * #getRemoteFileName() */ public String getRemoteFileName() { return remoteFileName; @@ -69,11 +74,13 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans setupTimeouts(); urlConnection = getRemoteFileURL().openConnection(); // set cache to off if using jar protocol - // this is for addressing bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=235933 + // this is for addressing bug + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=235933 if (getRemoteFileURL().getProtocol().equalsIgnoreCase("jar")) { //$NON-NLS-1$ urlConnection.setUseCaches(false); } - IURLConnectionModifier connectionModifier = Activator.getDefault().getURLConnectionModifier(); + IURLConnectionModifier connectionModifier = Activator.getDefault() + .getURLConnectionModifier(); if (connectionModifier != null) { connectionModifier.setSocketFactoryForConnection(urlConnection); } @@ -85,29 +92,39 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans protected void setResumeRequestHeaderValues() throws IOException { if (this.bytesReceived <= 0 || this.fileLength <= this.bytesReceived) - throw new IOException(Messages.UrlConnectionRetrieveFileTransfer_RESUME_START_ERROR); + throw new IOException( + Messages.UrlConnectionRetrieveFileTransfer_RESUME_START_ERROR); setRangeHeader("bytes=" + this.bytesReceived + "-"); //$NON-NLS-1$ //$NON-NLS-2$ - // set max-age for cache control to 0 for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=249990 + // set max-age for cache control to 0 for bug + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=249990 urlConnection.setRequestProperty("Cache-Control", "max-age=0"); //$NON-NLS-1$//$NON-NLS-2$ } - protected void setRequestHeaderValues() throws InvalidFileRangeSpecificationException { + protected void setRequestHeaderValues() + throws InvalidFileRangeSpecificationException { final IFileRangeSpecification rangeSpec = getFileRangeSpecification(); if (rangeSpec != null && isHTTP()) { final long startPosition = rangeSpec.getStartPosition(); final long endPosition = rangeSpec.getEndPosition(); if (startPosition < 0) - throw new InvalidFileRangeSpecificationException(Messages.UrlConnectionRetrieveFileTransfer_RESUME_START_POSITION_LESS_THAN_ZERO, rangeSpec); + throw new InvalidFileRangeSpecificationException( + Messages.UrlConnectionRetrieveFileTransfer_RESUME_START_POSITION_LESS_THAN_ZERO, + rangeSpec); if (endPosition != -1L && endPosition <= startPosition) - throw new InvalidFileRangeSpecificationException(Messages.UrlConnectionRetrieveFileTransfer_RESUME_ERROR_END_POSITION_LESS_THAN_START, rangeSpec); + throw new InvalidFileRangeSpecificationException( + Messages.UrlConnectionRetrieveFileTransfer_RESUME_ERROR_END_POSITION_LESS_THAN_START, + rangeSpec); setRangeHeader("bytes=" + startPosition + "-" + ((endPosition == -1L) ? "" : ("" + endPosition))); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$ } // Add http 1.1 'Connection: close' header in order to potentially avoid - // server issue described here https://bugs.eclipse.org/bugs/show_bug.cgi?id=234916#c13 + // server issue described here + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=234916#c13 // See bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=247197 - // also see http 1.1 rfc section 14-10 in http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html + // also see http 1.1 rfc section 14-10 in + // http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html urlConnection.setRequestProperty("Connection", "close"); //$NON-NLS-1$ //$NON-NLS-2$ - // set max-age for cache control to 0 for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=249990 + // set max-age for cache control to 0 for bug + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=249990 urlConnection.setRequestProperty("Cache-Control", "max-age=0"); //$NON-NLS-1$//$NON-NLS-2$ } @@ -161,15 +178,19 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans protected void getResponseHeaderValues() throws IOException { if (!isConnected()) - throw new ConnectException(Messages.UrlConnectionRetrieveFileTransfer_CONNECT_EXCEPTION_NOT_CONNECTED); + throw new ConnectException( + Messages.UrlConnectionRetrieveFileTransfer_CONNECT_EXCEPTION_NOT_CONNECTED); if (getResponseCode() == -1) - throw new IOException(Messages.UrlConnectionRetrieveFileTransfer_EXCEPTION_INVALID_SERVER_RESPONSE); + throw new IOException( + Messages.UrlConnectionRetrieveFileTransfer_EXCEPTION_INVALID_SERVER_RESPONSE); setLastModifiedTime(urlConnection.getLastModified()); setFileLength(urlConnection.getContentLength()); - String contentDispositionValue = urlConnection.getHeaderField(HttpHelper.CONTENT_DISPOSITION_HEADER); + String contentDispositionValue = urlConnection + .getHeaderField(HttpHelper.CONTENT_DISPOSITION_HEADER); if (contentDispositionValue != null) { - remoteFileName = HttpHelper.getRemoteFileNameFromContentDispositionHeader(contentDispositionValue); + remoteFileName = HttpHelper + .getRemoteFileNameFromContentDispositionHeader(contentDispositionValue); } if (remoteFileName == null) { @@ -186,41 +207,52 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans protected void getResumeResponseHeaderValues() throws IOException { if (!isConnected()) - throw new ConnectException(Messages.UrlConnectionRetrieveFileTransfer_CONNECT_EXCEPTION_NOT_CONNECTED); + throw new ConnectException( + Messages.UrlConnectionRetrieveFileTransfer_CONNECT_EXCEPTION_NOT_CONNECTED); if (getResponseCode() != HTTP_RANGE_RESPONSE) - throw new IOException(Messages.UrlConnectionRetrieveFileTransfer_INVALID_SERVER_RESPONSE_TO_PARTIAL_RANGE_REQUEST); + throw new IOException( + Messages.UrlConnectionRetrieveFileTransfer_INVALID_SERVER_RESPONSE_TO_PARTIAL_RANGE_REQUEST); if (lastModifiedTime != urlConnection.getLastModified()) - throw new IOException(Messages.UrlConnectionRetrieveFileTransfer_EXCEPTION_FILE_MODIFIED_SINCE_LAST_ACCESS); + throw new IOException( + Messages.UrlConnectionRetrieveFileTransfer_EXCEPTION_FILE_MODIFIED_SINCE_LAST_ACCESS); } /** - * @param proxy2 the ECF proxy to setup + * @param proxy2 + * the ECF proxy to setup */ protected void setupProxy(final Proxy proxy2) { proxyHelper.setupProxy(proxy2); } - protected void setupAuthentication() throws IOException, UnsupportedCallbackException { + protected void setupAuthentication() throws IOException, + UnsupportedCallbackException { if (connectContext == null) return; - final CallbackHandler callbackHandler = connectContext.getCallbackHandler(); + final CallbackHandler callbackHandler = connectContext + .getCallbackHandler(); if (callbackHandler == null) return; final NameCallback usernameCallback = new NameCallback(USERNAME_PREFIX); final ObjectCallback passwordCallback = new ObjectCallback(); // Call callback with username and password callbacks - callbackHandler.handle(new Callback[] {usernameCallback, passwordCallback}); + callbackHandler.handle(new Callback[] { usernameCallback, + passwordCallback }); username = usernameCallback.getName(); Object o = passwordCallback.getObject(); if (!(o instanceof String)) - throw new UnsupportedCallbackException(passwordCallback, Messages.UrlConnectionRetrieveFileTransfer_UnsupportedCallbackException); + throw new UnsupportedCallbackException( + passwordCallback, + Messages.UrlConnectionRetrieveFileTransfer_UnsupportedCallbackException); password = (String) passwordCallback.getObject(); // Now set authenticator to our authenticator with user and password Authenticator.setDefault(new UrlConnectionAuthenticator()); } class UrlConnectionAuthenticator extends Authenticator { - /* (non-Javadoc) + /* + * (non-Javadoc) + * * @see java.net.Authenticator#getPasswordAuthentication() */ protected PasswordAuthentication getPasswordAuthentication() { @@ -230,10 +262,13 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans /* * (non-Javadoc) - * - * @see org.eclipse.ecf.filetransfer.IRetrieveFileTransferContainerAdapter#setConnectContextForAuthentication(org.eclipse.ecf.core.security.IConnectContext) + * + * @seeorg.eclipse.ecf.filetransfer.IRetrieveFileTransferContainerAdapter# + * setConnectContextForAuthentication + * (org.eclipse.ecf.core.security.IConnectContext) */ - public void setConnectContextForAuthentication(IConnectContext connectContext) { + public void setConnectContextForAuthentication( + IConnectContext connectContext) { super.setConnectContextForAuthentication(connectContext); this.username = null; this.password = null; @@ -241,8 +276,10 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans /* * (non-Javadoc) - * - * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#openStreams() + * + * @see + * org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer + * #openStreams() */ protected void openStreams() throws IncomingFileTransferException { try { @@ -256,7 +293,11 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans getResponseHeaderValues(); fireReceiveStartEvent(); } catch (final Exception e) { - IncomingFileTransferException except = new IncomingFileTransferException(NLS.bind(Messages.UrlConnectionRetrieveFileTransfer_EXCEPTION_COULD_NOT_CONNECT, getRemoteFileURL().toString()), e); + IncomingFileTransferException except = new IncomingFileTransferException( + NLS + .bind( + Messages.UrlConnectionRetrieveFileTransfer_EXCEPTION_COULD_NOT_CONNECT, + getRemoteFileURL().toString()), e); hardClose(); throw except; } @@ -264,8 +305,10 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans /* * (non-Javadoc) - * - * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#hardClose() + * + * @see + * org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer + * #hardClose() */ protected void hardClose() { super.hardClose(); @@ -279,8 +322,10 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans /* * (non-Javadoc) - * - * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#doPause() + * + * @see + * org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer + * #doPause() */ protected boolean doPause() { if (isPaused() || !isConnected() || isDone()) @@ -291,8 +336,10 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans /* * (non-Javadoc) - * - * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#doResume() + * + * @see + * org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer + * #doResume() */ protected boolean doResume() { if (!isPaused() || isConnected()) @@ -302,8 +349,10 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans /* * (non-Javadoc) - * - * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#getAdapter(java.lang.Class) + * + * @see + * org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer + * #getAdapter(java.lang.Class) */ public Object getAdapter(Class adapter) { if (adapter == null) @@ -314,9 +363,11 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans } private void setupTimeouts() { - String existingTimeout = System.getProperty(JRE_CONNECT_TIMEOUT_PROPERTY); + String existingTimeout = System + .getProperty(JRE_CONNECT_TIMEOUT_PROPERTY); if (existingTimeout == null) { - System.setProperty(JRE_CONNECT_TIMEOUT_PROPERTY, DEFAULT_CONNECT_TIMEOUT); + System.setProperty(JRE_CONNECT_TIMEOUT_PROPERTY, + DEFAULT_CONNECT_TIMEOUT); } existingTimeout = System.getProperty(JRE_READ_TIMEOUT_PROPERTY); if (existingTimeout == null) { @@ -325,7 +376,8 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans } /** - * @return <code>true</code> if streams successfully, <code>false</code> otherwise. + * @return <code>true</code> if streams successfully, <code>false</code> + * otherwise. */ private boolean openStreamsForResume() { final URL theURL = getRemoteFileURL(); @@ -356,7 +408,11 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans private static final String CONTENT_ENCODING_GZIP = "gzip"; //$NON-NLS-1$ // private static final String CONTENT_ENCODING_DEFLATE = "deflate"; //$NON-NLS-1$ - private static final String CONTENT_ENCODING_ACCEPTED = CONTENT_ENCODING_GZIP; // + "," + CONTENT_ENCODING_DEFLATE; + private static final String CONTENT_ENCODING_ACCEPTED = CONTENT_ENCODING_GZIP; // + + + // "," + // + + // CONTENT_ENCODING_DEFLATE; private static class Compression { @@ -379,7 +435,8 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans private void setCompressionRequestHeader() { if (rangeSpecification == null) - urlConnection.setRequestProperty(ACCEPT_ENCODING, CONTENT_ENCODING_ACCEPTED); + urlConnection.setRequestProperty(ACCEPT_ENCODING, + CONTENT_ENCODING_ACCEPTED); } private Compression getCompressionResponseHeader() { @@ -389,8 +446,8 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans return Compression.NONE; } else if (encoding.equalsIgnoreCase(CONTENT_ENCODING_GZIP)) { return Compression.GZIP; - // } else if (encoding.equalsIgnoreCase(CONTENT_ENCODING_DEFLATE)) { - // return Compression.DEFLATE; + // } else if (encoding.equalsIgnoreCase(CONTENT_ENCODING_DEFLATE)) { + // return Compression.DEFLATE; } return Compression.NONE; } @@ -401,8 +458,8 @@ public class UrlConnectionRetrieveFileTransfer extends AbstractRetrieveFileTrans if (Compression.GZIP == type) { return new java.util.zip.GZIPInputStream(input); - // } else if (Compression.DEFLATE == type) { - // return new java.util.zip.InflaterInputStream(input); + // } else if (Compression.DEFLATE == type) { + // return new java.util.zip.InflaterInputStream(input); } return input; } diff --git a/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/util/PollingInputStream.java b/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/util/PollingInputStream.java new file mode 100644 index 000000000..31b49794f --- /dev/null +++ b/providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/util/PollingInputStream.java @@ -0,0 +1,241 @@ +/******************************************************************************* + * Copyright (c) 2000, 2007 IBM Corporation and others. + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * which accompanies this distribution, and is available at + * http://www.eclipse.org/legal/epl-v10.html + * + * Contributors: + * IBM Corporation - initial API and implementation + * EclipseSource - modification after copying to ECF filetransfer provider + *******************************************************************************/ +package org.eclipse.ecf.provider.filetransfer.util; + +import java.io.*; +import org.eclipse.core.runtime.*; +import org.eclipse.ecf.internal.provider.filetransfer.Activator; + +/** + * Polls a progress monitor periodically and handles timeouts over extended + * durations. For this class to be effective, a high numAttempts should be + * specified, and the underlying stream should time out frequently on reads + * (every second or so). + * + * Supports resuming partially completed operations after an + * InterruptedIOException if the underlying stream does. Check the + * bytesTransferred field to determine how much of the operation completed; + * conversely, at what point to resume. + */ +public class PollingInputStream extends FilterInputStream { + private int numAttempts; + private IProgressMonitor monitor; + private boolean cancellable; + + /** + * Creates a new polling input stream. + * + * @param in + * the underlying input stream + * @param numAttempts + * the number of attempts before issuing an + * InterruptedIOException, if 0, retries indefinitely until + * canceled + * @param monitor + * the progress monitor to be polled for cancellation + */ + public PollingInputStream(InputStream in, int numAttempts, + IProgressMonitor monitor) { + super(in); + this.numAttempts = numAttempts; + this.monitor = monitor; + this.cancellable = true; + } + + /** + * Wraps the underlying stream's method. It may be important to wait for an + * input stream to be closed because it holds an implicit lock on a system + * resource (such as a file) while it is open. Closing a stream may take + * time if the underlying stream is still servicing a previous request. + * + * @throws OperationCanceledException + * if the progress monitor is canceled + * @throws InterruptedIOException + * if the underlying operation times out numAttempts times + */ + public void close() throws InterruptedIOException { + int attempts = 0; + try { + readPendingInput(); + } catch (IOException e) { + // We shouldn't get an exception when we're getting the available + // input. + // If we do, just log it so we can close. + logError(e.getMessage(), e); + } finally { + boolean stop = false; + while (!stop) { + try { + if (in != null) + in.close(); + stop = true; + } catch (InterruptedIOException e) { + if (checkCancellation()) + throw new OperationCanceledException(); + if (++attempts == numAttempts) + throw new InterruptedIOException( + "Timeout while closing input stream"); + } catch (IOException e) { + // ignore it - see + // https://bugs.eclipse.org/bugs/show_bug.cgi?id=203423#c10 + } + } + } + } + + private void logError(String message, IOException e) { + Activator.getDefault().log( + new Status(IStatus.ERROR, Activator.PLUGIN_ID, IStatus.ERROR, + message, e)); + } + + /** + * Wraps the underlying stream's method. + * + * @return the next byte of data, or -1 if the end of the stream is reached. + * @throws OperationCanceledException + * if the progress monitor is canceled + * @throws InterruptedIOException + * if the underlying operation times out numAttempts times and + * no data was received, bytesTransferred will be zero + * @throws IOException + * if an i/o error occurs + */ + public int read() throws IOException { + int attempts = 0; + for (;;) { + if (checkCancellation()) + throw new OperationCanceledException(); + try { + return in.read(); + } catch (InterruptedIOException e) { + if (++attempts == numAttempts) + throw new InterruptedIOException( + "Timeout while reading input stream"); + } + } + } + + /** + * Wraps the underlying stream's method. + * + * @param buffer + * - the buffer into which the data is read. + * @param off + * - the start offset of the data. + * @param len + * - the maximum number of bytes read. + * @return the total number of bytes read into the buffer, or -1 if there is + * no more data because the end of the stream has been reached. + * @throws OperationCanceledException + * if the progress monitor is canceled + * @throws InterruptedIOException + * if the underlying operation times out numAttempts times and + * no data was received, bytesTransferred will be zero + * @throws IOException + * if an i/o error occurs + */ + public int read(byte[] buffer, int off, int len) throws IOException { + int attempts = 0; + for (;;) { + if (checkCancellation()) + throw new OperationCanceledException(); + try { + return in.read(buffer, off, len); + } catch (InterruptedIOException e) { + if (e.bytesTransferred != 0) + return e.bytesTransferred; // keep partial transfer + if (++attempts == numAttempts) + throw new InterruptedIOException( + "Timeout while reading input stream"); + } + } + } + + /** + * Wraps the underlying stream's method. + * + * @param count + * - the number of bytes to be skipped. + * @return the actual number of bytes skipped. + * @throws OperationCanceledException + * if the progress monitor is canceled + * @throws InterruptedIOException + * if the underlying operation times out numAttempts times and + * no data was received, bytesTransferred will be zero + * @throws IOException + * if an i/o error occurs + */ + public long skip(long count) throws IOException { + int attempts = 0; + for (;;) { + if (checkCancellation()) + throw new OperationCanceledException(); + try { + return in.skip(count); + } catch (InterruptedIOException e) { + if (e.bytesTransferred != 0) + return e.bytesTransferred; // keep partial transfer + if (++attempts == numAttempts) + throw new InterruptedIOException( + "Timeout while reading input stream"); + } + } + } + + /** + * Reads any pending input from the input stream so that the stream can + * savely be closed. + */ + protected void readPendingInput() throws IOException { + byte[] buffer = new byte[2048]; + while (true) { + int available = in.available(); + if (available < 1) + break; + if (available > buffer.length) + available = buffer.length; + if (in.read(buffer, 0, available) < 1) + break; + } + } + + /** + * Called to set whether cancellation will be checked by this stream. + * Turning cancellation checking off can be very useful for protecting + * critical portions of a protocol that shouldn't be interrupted. For + * example, it is often necessary to protect login sequences. + * + * @param cancellable + * a flag controlling whether this stream will check for + * cancellation. + */ + public void setIsCancellable(boolean cancellable) { + this.cancellable = cancellable; + } + + /** + * Checked whether the monitor for this stream has been cancelled. If the + * cancellable flag is <code>false</code> then the monitor is never + * cancelled. + * + * @return <code>true</code> if the monitor has been cancelled and + * <code>false</code> otherwise. + */ + private boolean checkCancellation() { + if (cancellable) { + return monitor.isCanceled(); + } else { + return false; + } + } +} |