Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorslewis2009-02-10 01:14:19 -0500
committerslewis2009-02-10 01:14:19 -0500
commitb9540b82d23f04ade2769f579455f37aa22f2d9f (patch)
treecd569fdd795f559c411c17514005f13c836a5a07 /providers/bundles/org.eclipse.ecf.provider.filetransfer
parentd43164023ddaa6b9a9a55f438f900a793f42daa7 (diff)
downloadorg.eclipse.ecf-b9540b82d23f04ade2769f579455f37aa22f2d9f.tar.gz
org.eclipse.ecf-b9540b82d23f04ade2769f579455f37aa22f2d9f.tar.xz
org.eclipse.ecf-b9540b82d23f04ade2769f579455f37aa22f2d9f.zip
Addition of PollingInputStream and changes to use in AbstractRetrieveFileTransfer for addressing bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=263613v20090209-2200
Diffstat (limited to 'providers/bundles/org.eclipse.ecf.provider.filetransfer')
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/AbstractRetrieveFileTransfer.java14
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/retrieve/UrlConnectionRetrieveFileTransfer.java159
-rw-r--r--providers/bundles/org.eclipse.ecf.provider.filetransfer/src/org/eclipse/ecf/provider/filetransfer/util/PollingInputStream.java241
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;
+ }
+ }
+}

Back to the top