org.eclipse.ecf.provider.filetransfer.httpclient.HttpClientRetrieveFileTransfer.java Source code

Java tutorial

Introduction

Here is the source code for org.eclipse.ecf.provider.filetransfer.httpclient.HttpClientRetrieveFileTransfer.java

Source

/*******************************************************************************
 * Copyright (c) 2004, 2011 Composent, Inc., IBM 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: 
 *  Composent, Inc. - initial API and implementation
 *  Maarten Meijer - bug 237936, added gzip encoded transfer default
 *  Henrich Kraemer - bug 263869, testHttpsReceiveFile fails using HTTP proxy
 *  Henrich Kraemer - bug 263613, [transport] Update site contacting / downloading is not cancelable
 *  Henrich Kraemer - Bug 297742 - [transport] Investigate how to maintain HTTP session  
 ******************************************************************************/
package org.eclipse.ecf.provider.filetransfer.httpclient;

import java.io.IOException;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.Socket;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import javax.net.SocketFactory;
import org.apache.commons.httpclient.Credentials;
import org.apache.commons.httpclient.Header;
import org.apache.commons.httpclient.HostConfiguration;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpConnection;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.HttpState;
import org.apache.commons.httpclient.HttpStatus;
import org.apache.commons.httpclient.HttpVersion;
import org.apache.commons.httpclient.NTCredentials;
import org.apache.commons.httpclient.UsernamePasswordCredentials;
import org.apache.commons.httpclient.auth.AuthScope;
import org.apache.commons.httpclient.auth.CredentialsProvider;
import org.apache.commons.httpclient.methods.GetMethod;
import org.apache.commons.httpclient.protocol.Protocol;
import org.apache.commons.httpclient.protocol.ProtocolSocketFactory;
import org.apache.commons.httpclient.protocol.SecureProtocolSocketFactory;
import org.apache.commons.httpclient.util.DateUtil;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.NullProgressMonitor;
import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Status;
import org.eclipse.ecf.core.identity.ID;
import org.eclipse.ecf.core.security.Callback;
import org.eclipse.ecf.core.security.CallbackHandler;
import org.eclipse.ecf.core.security.IConnectContext;
import org.eclipse.ecf.core.security.NameCallback;
import org.eclipse.ecf.core.security.ObjectCallback;
import org.eclipse.ecf.core.security.UnsupportedCallbackException;
import org.eclipse.ecf.core.util.Proxy;
import org.eclipse.ecf.core.util.ProxyAddress;
import org.eclipse.ecf.core.util.Trace;
import org.eclipse.ecf.filetransfer.FileTransferJob;
import org.eclipse.ecf.filetransfer.IFileRangeSpecification;
import org.eclipse.ecf.filetransfer.IFileTransferPausable;
import org.eclipse.ecf.filetransfer.IFileTransferRunnable;
import org.eclipse.ecf.filetransfer.IRetrieveFileTransferOptions;
import org.eclipse.ecf.filetransfer.IncomingFileTransferException;
import org.eclipse.ecf.filetransfer.InvalidFileRangeSpecificationException;
import org.eclipse.ecf.filetransfer.events.IFileTransferConnectStartEvent;
import org.eclipse.ecf.filetransfer.events.socket.ISocketEventSource;
import org.eclipse.ecf.filetransfer.events.socket.ISocketListener;
import org.eclipse.ecf.filetransfer.identity.IFileID;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.Activator;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.ConnectingSocketMonitor;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.ConnectionManagerHelper;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.DebugOptions;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.ECFHttpClientProtocolSocketFactory;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.ECFHttpClientSecureProtocolSocketFactory;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.HttpClientProxyCredentialProvider;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.ISSLSocketFactoryModifier;
import org.eclipse.ecf.internal.provider.filetransfer.httpclient.Messages;
import org.eclipse.ecf.provider.filetransfer.events.socket.SocketEventSource;
import org.eclipse.ecf.provider.filetransfer.identity.FileTransferID;
import org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer;
import org.eclipse.ecf.provider.filetransfer.retrieve.HttpHelper;
import org.eclipse.ecf.provider.filetransfer.util.JREProxyHelper;
import org.eclipse.ecf.provider.filetransfer.util.ProxySetupHelper;
import org.eclipse.osgi.util.NLS;

public class HttpClientRetrieveFileTransfer extends AbstractRetrieveFileTransfer {

    /**
     * gzip encoding wrapper for httpclient class. Copied from Mylyn project, bug 205708
     *
     */
    public class GzipGetMethod extends GetMethod {

        private static final String CONTENT_ENCODING = "Content-Encoding"; //$NON-NLS-1$
        private static final String ACCEPT_ENCODING = "Accept-encoding"; //$NON-NLS-1$
        private static final String CONTENT_ENCODING_GZIP = "gzip"; //$NON-NLS-1$

        private static final String CONTENT_ENCODING_ACCEPTED = CONTENT_ENCODING_GZIP;

        private boolean gzipReceived = false;

        public GzipGetMethod(String urlString) {
            super(urlString);
        }

        private boolean isZippedResponse() {
            // see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=269018
            boolean contentEncodingGzip = (null != this.getResponseHeader(CONTENT_ENCODING)
                    && this.getResponseHeader(CONTENT_ENCODING).getValue().equals(CONTENT_ENCODING_GZIP));
            Trace.trace(Activator.PLUGIN_ID,
                    "Content-Encoding: gzip header " + (contentEncodingGzip ? "PRESENT" : "ABSENT")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
            boolean hasGzSuffix = targetHasGzSuffix(remoteFileName);
            return contentEncodingGzip && !hasGzSuffix;
        }

        public int execute(HttpState state, HttpConnection conn) throws HttpException, IOException {
            Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(),
                    "GzipGetMethod.execute"); //$NON-NLS-1$
            // Insert accept-encoding header
            int result = super.execute(state, conn);
            // Code to deal with implications described on bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=261881
            switch (result) {
            case HttpStatus.SC_MOVED_TEMPORARILY:
            case HttpStatus.SC_MOVED_PERMANENTLY:
            case HttpStatus.SC_SEE_OTHER:
            case HttpStatus.SC_TEMPORARY_REDIRECT:
                Trace.trace(Activator.PLUGIN_ID,
                        "GzipGetMethod.execute.  Received redirect=" + result + ".  Removing gzip accept encoding"); //$NON-NLS-1$ //$NON-NLS-2$
                gzipReceived = false;
                removeRequestHeader(GzipGetMethod.ACCEPT_ENCODING);
            default:
            }
            // test what is sent back
            Trace.exiting(Activator.PLUGIN_ID, DebugOptions.METHODS_EXITING, this.getClass(),
                    "GzipGetMethod.execute", new Integer(result)); //$NON-NLS-1$
            return result;
        }

        public InputStream getResponseBodyAsUnzippedStream() throws IOException {
            gzipReceived = isZippedResponse();
            InputStream input = super.getResponseBodyAsStream();
            try {
                if (gzipReceived) {
                    Trace.trace(Activator.PLUGIN_ID, "Using gzip input stream to decode"); //$NON-NLS-1$
                    // extract on the fly
                    return new java.util.zip.GZIPInputStream(input);
                }
                Trace.trace(Activator.PLUGIN_ID, "Not using gzip input stream"); //$NON-NLS-1$
            } catch (IOException e) {
                Activator.getDefault().log(new Status(IStatus.WARNING, Activator.PLUGIN_ID, IStatus.WARNING,
                        "Exception creating gzip input stream", e)); //$NON-NLS-1$ 
                throw e;
            }
            return input;
        }

        private Object releaseLock = new Object();

        // This override is a workaround for 
        // https://bugs.eclipse.org/bugs/show_bug.cgi?id=279457
        // This makes GetMethod.releaseConnection non-reentrant,
        // as with reentrancy under some circumstances a NPE can be 
        // thrown with multithreaded access
        public void releaseConnection() {
            synchronized (releaseLock) {
                super.releaseConnection();
            }
        }
    }

    static final class HostConfigHelper {
        private ISocketEventSource source;
        private ISocketListener socketListener;
        private String targetURL;
        private String targetRelativePath;

        private HostConfiguration hostConfiguration;

        public HostConfigHelper(ISocketEventSource source, ISocketListener socketListener) {
            Assert.isNotNull(source);
            this.source = source;
            this.socketListener = socketListener;
            hostConfiguration = new HostConfiguration();
        }

        public HostConfiguration getHostConfiguration() {
            return hostConfiguration;
        }

        // drops the scheme server and port (e.g http://server:8080/a/b.html -> /a/b.html
        private static String getTargetRelativePathFromURL(String url) {
            // RFC 3986
            /* 
             *      
             *     URI    = scheme ":" hier-part [ "?" query ] [ "#" fragment ]
                
                  hier-part =  "//" authority path-abempty
                      / path-absolute
                      / path-rootless
                      / path-empty     
             *      
             *      path  = path-abempty    ; begins with "/" or is empty
                   / path-absolute   ; begins with "/" but not "//"
                   / path-noscheme   ; begins with a non-colon segment
                   / path-rootless   ; begins with a segment
                   / path-empty      ; zero characters
                
             * 
             */
            // This routine is supposed to remove authority information from the url
            // to make this a 'relative path' for
            // HttpClients method constructor (for example GetMethod(String uri)) as
            // ECF executes methods passing in a HostConfiguration which represents the
            // authority.
            final int colonSlashSlash = url.indexOf("://"); //$NON-NLS-1$
            if (colonSlashSlash < 0)
                return url;

            // '://' indicates there must be an authority.
            // the authority must not contain a '/' character.
            final int nextSlash = url.indexOf('/', colonSlashSlash + 3);
            if (nextSlash == -1) {
                // try root? or should it be empty?
                return ""; //$NON-NLS-1$ 
            }
            String relativeURL = url.substring(nextSlash); // include the slash
            // This is a workaround for multiple consecutive slashes after the authority.
            // HttpClient will parse this as another authority instead of using 
            // it as a path. In anticipation we add "//example.com" so that this will
            // be removed instead of the first path segment. 
            // See https://bugs.eclipse.org/bugs/show_bug.cgi?id=270749#c28 
            // for a full explanation
            if (relativeURL.startsWith("//")) { //$NON-NLS-1$
                final String host = "example.com"; //$NON-NLS-1$
                relativeURL = "//" + host + relativeURL; //$NON-NLS-1$

            }
            return relativeURL;
        }

        public void setTargetHostByURL(CredentialsProvider credProvider, String url) {
            this.targetURL = url;
            this.targetRelativePath = getTargetRelativePathFromURL(targetURL);
            String host = getHostFromURL(targetURL);
            int port = getPortFromURL(targetURL);

            if (HttpClientRetrieveFileTransfer.urlUsesHttps(targetURL)) {
                ISSLSocketFactoryModifier sslSocketFactoryModifier = Activator.getDefault()
                        .getSSLSocketFactoryModifier();
                if (sslSocketFactoryModifier == null) {
                    sslSocketFactoryModifier = new HttpClientDefaultSSLSocketFactoryModifier();
                }
                SecureProtocolSocketFactory psf = new ECFHttpClientSecureProtocolSocketFactory(
                        sslSocketFactoryModifier, source, socketListener);
                Protocol sslProtocol = new Protocol(HttpClientRetrieveFileTransfer.HTTPS,
                        (ProtocolSocketFactory) psf, HTTPS_PORT);

                Trace.trace(Activator.PLUGIN_ID, "retrieve host=" + host + ";port=" + port); //$NON-NLS-1$ //$NON-NLS-2$
                hostConfiguration.setHost(host, port, sslProtocol);
                hostConfiguration.getParams().setParameter(CredentialsProvider.PROVIDER, credProvider);

            } else {
                ProtocolSocketFactory psf = new ECFHttpClientProtocolSocketFactory(SocketFactory.getDefault(),
                        source, socketListener);
                Protocol protocol = new Protocol(HttpClientRetrieveFileTransfer.HTTP, psf, HTTP_PORT);
                Trace.trace(Activator.PLUGIN_ID, "retrieve host=" + host + ";port=" + port); //$NON-NLS-1$ //$NON-NLS-2$
                hostConfiguration.setHost(host, port, protocol);
                hostConfiguration.getParams().setParameter(CredentialsProvider.PROVIDER, credProvider);
            }
        }

        public String getTargetRelativePath() {
            return targetRelativePath;
        }

    }

    private static final String USERNAME_PREFIX = Messages.HttpClientRetrieveFileTransfer_Username_Prefix;

    // changing to 2 minutes (120000) as per bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=266246
    // 10/26/2009:  Added being able to set with system property with name org.eclipse.ecf.provider.filetransfer.httpclient.retrieve.connectTimeout
    // for https://bugs.eclipse.org/bugs/show_bug.cgi?id=292995
    protected static final int DEFAULT_CONNECTION_TIMEOUT = ConnectionManagerHelper.DEFAULT_CONNECTION_TIMEOUT;
    // changing to 2 minutes (120000) as per bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=266246
    // 10/26/2009:  Added being able to set with system property with name org.eclipse.ecf.provider.filetransfer.httpclient.retrieve.readTimeout
    // for https://bugs.eclipse.org/bugs/show_bug.cgi?id=292995
    protected static final int DEFAULT_READ_TIMEOUT = ConnectionManagerHelper.DEFAULT_READ_TIMEOUT;

    protected static final int HTTP_PORT = 80;

    protected static final int HTTPS_PORT = 443;

    protected static final int MAX_RETRY = 2;

    protected static final String HTTPS = Messages.FileTransferNamespace_Https_Protocol;

    protected static final String HTTP = Messages.FileTransferNamespace_Http_Protocol;

    protected static final String[] supportedProtocols = { HTTP, HTTPS };

    private static final String LAST_MODIFIED_HEADER = "Last-Modified"; //$NON-NLS-1$

    private GzipGetMethod getMethod = null;

    private HttpClient httpClient = null;

    private String username;

    private String password;

    private int responseCode = -1;
    private volatile boolean doneFired = false;

    private String remoteFileName;

    protected int httpVersion = 1;

    protected IFileID fileid = null;

    protected JREProxyHelper proxyHelper = null;

    private HostConfigHelper hostConfigHelper;
    private SocketEventSource socketEventSource;

    private ConnectingSocketMonitor connectingSockets;
    private FileTransferJob connectJob;

    public HttpClientRetrieveFileTransfer(HttpClient client) {
        this.httpClient = client;
        proxyHelper = new JREProxyHelper();
        connectingSockets = new ConnectingSocketMonitor(1);
        socketEventSource = new SocketEventSource() {
            public Object getAdapter(Class adapter) {
                if (adapter == null) {
                    return null;
                }
                if (adapter.isInstance(this)) {
                    return this;
                }
                return HttpClientRetrieveFileTransfer.this.getAdapter(adapter);
            }

        };

    }

    /* (non-Javadoc)
     * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#getRemoteFileName()
     */
    public String getRemoteFileName() {
        return remoteFileName;
    }

    public synchronized void cancel() {
        Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(), "cancel"); //$NON-NLS-1$
        if (isCanceled()) {
            return; // break job cancel recursion
        }
        setDoneCanceled(exception);
        boolean fireDoneEvent = true;
        if (connectJob != null) {
            Trace.trace(Activator.PLUGIN_ID, "calling connectJob.cancel()"); //$NON-NLS-1$
            connectJob.cancel();
        }
        synchronized (jobLock) {
            if (job != null) {
                // Its the transfer jobs responsibility to throw the event.
                fireDoneEvent = false;
                Trace.trace(Activator.PLUGIN_ID, "calling transfer job.cancel()"); //$NON-NLS-1$
                job.cancel();
            }
        }
        if (getMethod != null) {
            if (!getMethod.isAborted()) {
                Trace.trace(Activator.PLUGIN_ID, "calling getMethod.abort()"); //$NON-NLS-1$
                getMethod.abort();
            }
        }
        if (connectingSockets != null) {
            // this should unblock socket connect calls, if any
            for (Iterator iterator = connectingSockets.getConnectingSockets().iterator(); iterator.hasNext();) {
                Socket socket = (Socket) iterator.next();
                try {
                    Trace.trace(Activator.PLUGIN_ID, "Call socket.close() for socket=" + socket.toString()); //$NON-NLS-1$
                    socket.close();
                } catch (IOException e) {
                    Trace.catching(Activator.PLUGIN_ID, DebugOptions.EXCEPTIONS_CATCHING, this.getClass(), "cancel", //$NON-NLS-1$
                            e);
                }
            }
        }
        hardClose();
        if (fireDoneEvent) {
            fireTransferReceiveDoneEvent();
        }
        Trace.exiting(Activator.PLUGIN_ID, DebugOptions.METHODS_EXITING, this.getClass(), "cancel");//$NON-NLS-1$

    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#hardClose()
     */
    protected void hardClose() {
        super.hardClose();
        if (getMethod != null) {
            getMethod.releaseConnection();
            getMethod = null;
        }
        responseCode = -1;
        if (proxyHelper != null) {
            proxyHelper.dispose();
            proxyHelper = null;
        }
    }

    protected Credentials getFileRequestCredentials() throws UnsupportedCallbackException, IOException {
        if (connectContext == null)
            return null;
        final CallbackHandler callbackHandler = connectContext.getCallbackHandler();
        if (callbackHandler == null)
            return null;
        final NameCallback usernameCallback = new NameCallback(USERNAME_PREFIX);
        final ObjectCallback passwordCallback = new ObjectCallback();
        callbackHandler.handle(new Callback[] { usernameCallback, passwordCallback });
        username = usernameCallback.getName();
        password = (String) passwordCallback.getObject();
        return new UsernamePasswordCredentials(username, password);
    }

    protected void setupProxies() {
        // If it's been set directly (via ECF API) then this overrides platform settings
        if (proxy == null) {
            try {
                // give SOCKS priority see https://bugs.eclipse.org/bugs/show_bug.cgi?id=295030#c61
                proxy = ProxySetupHelper.getSocksProxy(getRemoteFileURL());
                if (proxy == null) {
                    proxy = ProxySetupHelper.getProxy(getRemoteFileURL().toExternalForm());
                }
            } catch (NoClassDefFoundError e) {
                // If the proxy API is not available a NoClassDefFoundError will be thrown here.
                // If that happens then we just want to continue on.
                Activator.logNoProxyWarning(e);
            }
        }
        if (proxy != null)
            setupProxy(proxy);
    }

    protected void setupAuthentication(String urlString) throws UnsupportedCallbackException, IOException {
        Credentials credentials = null;
        if (username == null) {
            credentials = getFileRequestCredentials();
        }

        if (credentials != null && username != null) {
            final AuthScope authScope = new AuthScope(getHostFromURL(urlString), getPortFromURL(urlString),
                    AuthScope.ANY_REALM);
            Trace.trace(Activator.PLUGIN_ID, "retrieve credentials=" + credentials); //$NON-NLS-1$
            httpClient.getState().setCredentials(authScope, credentials);
        }
    }

    protected void setupHostAndPort(CredentialsProvider credProvider, String urlString) {
        getHostConfiguration(); // creates hostConfigHelper if needed
        hostConfigHelper.setTargetHostByURL(credProvider, urlString);
    }

    protected void setRequestHeaderValues() throws InvalidFileRangeSpecificationException {
        final IFileRangeSpecification rangeSpec = getFileRangeSpecification();
        if (rangeSpec != null) {
            final long startPosition = rangeSpec.getStartPosition();
            final long endPosition = rangeSpec.getEndPosition();
            if (startPosition < 0)
                throw new InvalidFileRangeSpecificationException(
                        Messages.HttpClientRetrieveFileTransfer_RESUME_START_POSITION_LESS_THAN_ZERO, rangeSpec);
            if (endPosition != -1L && endPosition <= startPosition)
                throw new InvalidFileRangeSpecificationException(
                        Messages.HttpClientRetrieveFileTransfer_RESUME_ERROR_END_POSITION_LESS_THAN_START,
                        rangeSpec);
            String rangeHeader = "bytes=" + startPosition + "-" + ((endPosition == -1L) ? "" : ("" + endPosition)); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
            Trace.trace(Activator.PLUGIN_ID, "retrieve range header=" + rangeHeader); //$NON-NLS-1$
            setRangeHeader(rangeHeader);
        }
        // set max-age for cache control to 0 for bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=249990
        getMethod.addRequestHeader("Cache-Control", "max-age=0"); //$NON-NLS-1$//$NON-NLS-2$
        setRequestHeaderValuesFromOptions();
    }

    private void setRequestHeaderValuesFromOptions() {
        Map localOptions = getOptions();
        if (localOptions != null) {
            Object o = localOptions.get(IRetrieveFileTransferOptions.REQUEST_HEADERS);
            if (o != null && o instanceof Map) {
                Map requestHeaders = (Map) o;
                for (Iterator i = requestHeaders.keySet().iterator(); i.hasNext();) {
                    Object n = i.next();
                    Object v = requestHeaders.get(n);
                    if (n != null && n instanceof String && v != null && v instanceof String)
                        getMethod.addRequestHeader((String) n, (String) v);
                }
            }
        }
    }

    private void setRangeHeader(String value) {
        getMethod.addRequestHeader("Range", value); //$NON-NLS-1$
    }

    private boolean isHTTP11() {
        return (httpVersion >= 1);
    }

    public int getResponseCode() {
        if (responseCode != -1)
            return responseCode;
        HttpVersion version = getMethod.getEffectiveVersion();
        if (version == null) {
            responseCode = -1;
            httpVersion = 1;
            return responseCode;
        }
        httpVersion = version.getMinor();
        responseCode = getMethod.getStatusCode();
        return responseCode;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ecf.core.identity.IIdentifiable#getID()
     */
    public ID getID() {
        return fileid;
    }

    private long getLastModifiedTimeFromHeader() throws IOException {
        Header lastModifiedHeader = getMethod.getResponseHeader(LAST_MODIFIED_HEADER);
        if (lastModifiedHeader == null)
            throw new IOException(Messages.HttpClientRetrieveFileTransfer_INVALID_LAST_MODIFIED_TIME);

        String lastModifiedString = lastModifiedHeader.getValue();
        long lastModified = 0;
        if (lastModifiedString != null) {
            try {
                lastModified = DateUtil.parseDate(lastModifiedString).getTime();
            } catch (Exception e) {
                throw new IOException(
                        Messages.HttpClientRetrieveFileTransfer_EXCEPITION_INVALID_LAST_MODIFIED_FROM_SERVER);
            }
        }
        return lastModified;
    }

    protected void getResponseHeaderValues() throws IOException {
        if (getResponseCode() == -1)
            throw new IOException(
                    Messages.HttpClientRetrieveFileTransfer_INVALID_SERVER_RESPONSE_TO_PARTIAL_RANGE_REQUEST);
        Header lastModifiedHeader = getMethod.getResponseHeader(LAST_MODIFIED_HEADER);
        if (lastModifiedHeader != null) {
            setLastModifiedTime(getLastModifiedTimeFromHeader());
        }
        setFileLength(getMethod.getResponseContentLength());
        fileid = new FileTransferID(getRetrieveNamespace(), getRemoteFileURL());

        // Get content disposition header and get remote file name from it if possible.
        Header contentDispositionHeader = getMethod.getResponseHeader(HttpHelper.CONTENT_DISPOSITION_HEADER);
        if (contentDispositionHeader != null) {
            remoteFileName = HttpHelper
                    .getRemoteFileNameFromContentDispositionHeader(contentDispositionHeader.getValue());
        }
        // If still null, get the path from httpclient.getMethod()
        if (remoteFileName == null) {
            // No name could be extracted using Content-Disposition. Let's try the
            // path from the getMethod.
            String pathStr = getMethod.getPath();
            if (pathStr != null && pathStr.length() > 0) {
                IPath path = Path.fromPortableString(pathStr);
                if (path.segmentCount() > 0)
                    remoteFileName = path.lastSegment();
            }
            // If still null, use the input file name
            if (remoteFileName == null)
                // Last resort. Use the path of the initial URL request
                remoteFileName = super.getRemoteFileName();
        }
    }

    final class ECFCredentialsProvider extends HttpClientProxyCredentialProvider {

        protected Proxy getECFProxy() {
            return getProxy();
        }

        protected Credentials getNTLMCredentials(Proxy lp) {
            if (hasForceNTLMProxyOption())
                return HttpClientRetrieveFileTransfer.createNTLMCredentials(lp);
            return null;
        }

    }

    Proxy getProxy() {
        return proxy;
    }

    protected void setInputStream(InputStream ins) {
        remoteFileContents = ins;
    }

    protected InputStream wrapTransferReadInputStream(InputStream inputStream, IProgressMonitor monitor) {
        return inputStream;
    }

    protected boolean hasForceNTLMProxyOption() {
        Map localOptions = getOptions();
        if (localOptions != null && localOptions.get(HttpClientOptions.FORCE_NTLM_PROP) != null)
            return true;
        return (System.getProperties().getProperty(HttpClientOptions.FORCE_NTLM_PROP) != null);
    }

    protected int getSocketReadTimeout() {
        return ConnectionManagerHelper.getSocketReadTimeout(getOptions());
    }

    /**
     * @since 4.0
     */
    protected int getConnectTimeout() {
        return ConnectionManagerHelper.getConnectTimeout(getOptions());
    }

    private void initHttpClientConnectionManager() {
        Activator.getDefault().getConnectionManagerHelper().initConnectionManager(httpClient, getOptions());
    }

    /* (non-Javadoc)
     * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#openStreams()
     */
    protected void openStreams() throws IncomingFileTransferException {

        Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(), "openStreams"); //$NON-NLS-1$
        final String urlString = getRemoteFileURL().toString();
        this.doneFired = false;

        int code = -1;

        try {
            initHttpClientConnectionManager();
            setupAuthentication(urlString);

            CredentialsProvider credProvider = new ECFCredentialsProvider();
            setupHostAndPort(credProvider, urlString);

            getMethod = new GzipGetMethod(hostConfigHelper.getTargetRelativePath());
            getMethod.addRequestHeader("Connection", "Keep-Alive"); //$NON-NLS-1$ //$NON-NLS-2$
            getMethod.setFollowRedirects(true);
            // Define a CredentialsProvider - found that possibility while debugging in org.apache.commons.httpclient.HttpMethodDirector.processProxyAuthChallenge(HttpMethod)
            // Seems to be another way to select the credentials.
            getMethod.getParams().setParameter(CredentialsProvider.PROVIDER, credProvider);
            setRequestHeaderValues();

            Trace.trace(Activator.PLUGIN_ID, "retrieve=" + urlString); //$NON-NLS-1$
            // Set request header for possible gzip encoding, but only if
            // 1) The file range specification is null (we want the whole file)
            // 2) The target remote file does *not* end in .gz (see bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=280205)
            if (getFileRangeSpecification() == null && !targetHasGzSuffix(super.getRemoteFileName())) {
                Trace.trace(Activator.PLUGIN_ID, "Accept-Encoding: gzip added to request header"); //$NON-NLS-1$
                getMethod.setRequestHeader(GzipGetMethod.ACCEPT_ENCODING, GzipGetMethod.CONTENT_ENCODING_ACCEPTED);
            } else {
                Trace.trace(Activator.PLUGIN_ID, "Accept-Encoding NOT added to header"); //$NON-NLS-1$
            }

            fireConnectStartEvent();
            if (checkAndHandleDone()) {
                return;
            }

            connectingSockets.clear();
            // Actually execute get and get response code (since redirect is set to true, then
            // redirect response code handled internally
            if (connectJob == null) {
                performConnect(new NullProgressMonitor());
            } else {
                connectJob.schedule();
                connectJob.join();
                connectJob = null;
            }
            if (checkAndHandleDone()) {
                return;
            }

            code = responseCode;

            responseHeaders = getResponseHeaders();

            Trace.trace(Activator.PLUGIN_ID, "retrieve resp=" + code); //$NON-NLS-1$

            // Check for NTLM proxy in response headers 
            // This check is to deal with bug https://bugs.eclipse.org/bugs/show_bug.cgi?id=252002
            boolean ntlmProxyFound = NTLMProxyDetector.detectNTLMProxy(getMethod);
            if (ntlmProxyFound && !hasForceNTLMProxyOption())
                throw new IncomingFileTransferException(
                        "HttpClient Provider is not configured to support NTLM proxy authentication.", //$NON-NLS-1$
                        HttpClientOptions.NTLM_PROXY_RESPONSE_CODE);

            if (code == HttpURLConnection.HTTP_PARTIAL || code == HttpURLConnection.HTTP_OK) {
                getResponseHeaderValues();
                setInputStream(getMethod.getResponseBodyAsUnzippedStream());
                fireReceiveStartEvent();
            } else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException(NLS.bind("File not found: {0}", urlString), code); //$NON-NLS-1$
            } else if (code == HttpURLConnection.HTTP_UNAUTHORIZED) {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException(Messages.HttpClientRetrieveFileTransfer_Unauthorized, code);
            } else if (code == HttpURLConnection.HTTP_FORBIDDEN) {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException("Forbidden", code); //$NON-NLS-1$
            } else if (code == HttpURLConnection.HTTP_PROXY_AUTH) {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException(Messages.HttpClientRetrieveFileTransfer_Proxy_Auth_Required,
                        code);
            } else {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException(
                        NLS.bind(Messages.HttpClientRetrieveFileTransfer_ERROR_GENERAL_RESPONSE_CODE,
                                new Integer(code)),
                        code);
            }
        } catch (final Exception e) {
            Trace.throwing(Activator.PLUGIN_ID, DebugOptions.EXCEPTIONS_THROWING, this.getClass(), "openStreams", //$NON-NLS-1$
                    e);
            if (code == -1) {
                if (!isDone()) {
                    setDoneException(e);
                }
                fireTransferReceiveDoneEvent();
            } else {
                IncomingFileTransferException ex = (IncomingFileTransferException) ((e instanceof IncomingFileTransferException)
                        ? e
                        : new IncomingFileTransferException(
                                NLS.bind(Messages.HttpClientRetrieveFileTransfer_EXCEPTION_COULD_NOT_CONNECT,
                                        urlString),
                                e, code));
                throw ex;
            }
        }
        Trace.exiting(Activator.PLUGIN_ID, DebugOptions.METHODS_EXITING, this.getClass(), "openStreams"); //$NON-NLS-1$
    }

    private Map getResponseHeaders() {
        if (getMethod == null)
            return null;
        Header[] headers = getMethod.getResponseHeaders();
        Map result = null;
        if (headers != null && headers.length > 0) {
            result = new HashMap();
            for (int i = 0; i < headers.length; i++) {
                String name = headers[i].getName();
                String val = headers[i].getValue();
                if (name != null && val != null)
                    result.put(name, val);
            }
        }
        return Collections.unmodifiableMap(result);
    }

    private boolean checkAndHandleDone() {
        if (isDone()) {
            // for cancel the done event should have been fired always.
            if (!doneFired) {
                fireTransferReceiveDoneEvent();
            }
            return true;
        }
        return false;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ecf.filetransfer.IRetrieveFileTransferContainerAdapter#setConnectContextForAuthentication(org.eclipse.ecf.core.security.IConnectContext)
     */
    public void setConnectContextForAuthentication(IConnectContext connectContext) {
        super.setConnectContextForAuthentication(connectContext);
        this.username = null;
        this.password = null;
    }

    protected static String getHostFromURL(String url) {
        String result = url;
        final int colonSlashSlash = url.indexOf("://"); //$NON-NLS-1$
        if (colonSlashSlash < 0)
            return ""; //$NON-NLS-1$
        if (colonSlashSlash >= 0) {
            result = url.substring(colonSlashSlash + 3);
        }

        final int colonPort = result.indexOf(':');
        final int requestPath = result.indexOf('/');

        int substringEnd;

        if (colonPort > 0 && requestPath > 0)
            substringEnd = Math.min(colonPort, requestPath);
        else if (colonPort > 0)
            substringEnd = colonPort;
        else if (requestPath > 0)
            substringEnd = requestPath;
        else
            substringEnd = result.length();

        return result.substring(0, substringEnd);

    }

    protected static int getPortFromURL(String url) {
        final int colonSlashSlash = url.indexOf("://"); //$NON-NLS-1$
        if (colonSlashSlash < 0)
            return urlUsesHttps(url) ? HTTPS_PORT : HTTP_PORT;
        // This is wrong as if the url has no colonPort before '?' then it should return the default

        final int colonPort = url.indexOf(':', colonSlashSlash + 1);
        if (colonPort < 0)
            return urlUsesHttps(url) ? HTTPS_PORT : HTTP_PORT;
        // Make sure that the colonPort is not from some part of the rest of the URL
        int nextSlash = url.indexOf('/', colonSlashSlash + 3);
        if (nextSlash != -1 && colonPort > nextSlash)
            return urlUsesHttps(url) ? HTTPS_PORT : HTTP_PORT;

        final int requestPath = url.indexOf('/', colonPort + 1);

        int end;
        if (requestPath < 0)
            end = url.length();
        else
            end = requestPath;

        return Integer.parseInt(url.substring(colonPort + 1, end));
    }

    protected static boolean urlUsesHttps(String url) {
        url = url.trim();
        return url.startsWith(HTTPS);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ecf.internal.provider.filetransfer.AbstractRetrieveFileTransfer#supportsProtocol(java.lang.String)
     */
    public static boolean supportsProtocol(String protocolString) {
        for (int i = 0; i < supportedProtocols.length; i++)
            if (supportedProtocols[i].equalsIgnoreCase(protocolString))
                return true;
        return false;
    }

    protected boolean isConnected() {
        return (getMethod != null);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#doPause()
     */
    protected boolean doPause() {
        if (isPaused() || !isConnected() || isDone())
            return false;
        this.paused = true;
        return this.paused;
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#doResume()
     */
    protected boolean doResume() {
        if (!isPaused() || isConnected())
            return false;
        return openStreamsForResume();
    }

    protected void setResumeRequestHeaderValues() throws IOException {
        if (this.bytesReceived <= 0 || this.fileLength <= this.bytesReceived)
            throw new IOException(Messages.HttpClientRetrieveFileTransfer_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
        getMethod.addRequestHeader("Cache-Control", "max-age=0"); //$NON-NLS-1$//$NON-NLS-2$
        setRequestHeaderValuesFromOptions();
    }

    private boolean openStreamsForResume() {

        Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(), "openStreamsForResume"); //$NON-NLS-1$
        final String urlString = getRemoteFileURL().toString();
        this.doneFired = false;

        int code = -1;

        try {
            initHttpClientConnectionManager();

            CredentialsProvider credProvider = new ECFCredentialsProvider();
            setupAuthentication(urlString);

            setupHostAndPort(credProvider, urlString);

            getMethod = new GzipGetMethod(hostConfigHelper.getTargetRelativePath());
            getMethod.addRequestHeader("Connection", "Keep-Alive"); //$NON-NLS-1$ //$NON-NLS-2$
            getMethod.setFollowRedirects(true);
            // Define a CredentialsProvider - found that possibility while debugging in org.apache.commons.httpclient.HttpMethodDirector.processProxyAuthChallenge(HttpMethod)
            // Seems to be another way to select the credentials.
            getMethod.getParams().setParameter(CredentialsProvider.PROVIDER, credProvider);
            setResumeRequestHeaderValues();

            Trace.trace(Activator.PLUGIN_ID, "resume=" + urlString); //$NON-NLS-1$

            // Gzip encoding is not an option for resume
            fireConnectStartEvent();
            if (checkAndHandleDone()) {
                return false;
            }

            connectingSockets.clear();
            // Actually execute get and get response code (since redirect is set to true, then
            // redirect response code handled internally
            if (connectJob == null) {
                performConnect(new NullProgressMonitor());
            } else {
                connectJob.schedule();
                connectJob.join();
                connectJob = null;
            }
            if (checkAndHandleDone()) {
                return false;
            }

            code = responseCode;

            responseHeaders = getResponseHeaders();

            Trace.trace(Activator.PLUGIN_ID, "retrieve resp=" + code); //$NON-NLS-1$

            if (code == HttpURLConnection.HTTP_PARTIAL || code == HttpURLConnection.HTTP_OK) {
                getResumeResponseHeaderValues();
                setInputStream(getMethod.getResponseBodyAsUnzippedStream());
                this.paused = false;
                fireReceiveResumedEvent();
            } else if (code == HttpURLConnection.HTTP_NOT_FOUND) {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException(NLS.bind("File not found: {0}", urlString), code, //$NON-NLS-1$
                        responseHeaders);
            } else if (code == HttpURLConnection.HTTP_UNAUTHORIZED) {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException(Messages.HttpClientRetrieveFileTransfer_Unauthorized, code,
                        responseHeaders);
            } else if (code == HttpURLConnection.HTTP_FORBIDDEN) {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException("Forbidden", code, responseHeaders); //$NON-NLS-1$
            } else if (code == HttpURLConnection.HTTP_PROXY_AUTH) {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException(Messages.HttpClientRetrieveFileTransfer_Proxy_Auth_Required,
                        code, responseHeaders);
            } else {
                getMethod.releaseConnection();
                throw new IncomingFileTransferException(
                        NLS.bind(Messages.HttpClientRetrieveFileTransfer_ERROR_GENERAL_RESPONSE_CODE,
                                new Integer(code)),
                        code, responseHeaders);
            }
            Trace.exiting(Activator.PLUGIN_ID, DebugOptions.METHODS_EXITING, this.getClass(),
                    "openStreamsForResume", Boolean.TRUE); //$NON-NLS-1$
            return true;
        } catch (final Exception e) {
            Trace.catching(Activator.PLUGIN_ID, DebugOptions.EXCEPTIONS_CATCHING, this.getClass(),
                    "openStreamsForResume", e); //$NON-NLS-1$
            if (code == -1) {
                if (!isDone()) {
                    setDoneException(e);
                }
            } else {
                setDoneException((e instanceof IncomingFileTransferException) ? e
                        : new IncomingFileTransferException(
                                NLS.bind(Messages.HttpClientRetrieveFileTransfer_EXCEPTION_COULD_NOT_CONNECT,
                                        urlString),
                                e, code, responseHeaders));
            }
            fireTransferReceiveDoneEvent();
            Trace.exiting(Activator.PLUGIN_ID, DebugOptions.METHODS_EXITING, this.getClass(),
                    "openStreamsForResume", Boolean.FALSE); //$NON-NLS-1$
            return false;
        }
    }

    protected void getResumeResponseHeaderValues() throws IOException {
        if (getResponseCode() != HttpURLConnection.HTTP_PARTIAL)
            throw new IOException();
        if (lastModifiedTime != getLastModifiedTimeFromHeader())
            throw new IOException(
                    Messages.HttpClientRetrieveFileTransfer_EXCEPTION_FILE_MODIFIED_SINCE_LAST_ACCESS);
    }

    /*
     * (non-Javadoc)
     *
     * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#getAdapter(java.lang.Class)
     */
    public Object getAdapter(Class adapter) {
        if (adapter == null)
            return null;
        if (adapter.equals(IFileTransferPausable.class) && isHTTP11())
            return this;
        if (adapter.equals(ISocketEventSource.class))
            return this.socketEventSource;
        return super.getAdapter(adapter);
    }

    private HostConfiguration getHostConfiguration() {
        if (hostConfigHelper == null) {
            hostConfigHelper = new HostConfigHelper(socketEventSource, connectingSockets);
        }
        return hostConfigHelper.getHostConfiguration();
    }

    /* (non-Javadoc)
     * @see org.eclipse.ecf.provider.filetransfer.retrieve.AbstractRetrieveFileTransfer#setupProxy(org.eclipse.ecf.core.util.Proxy)
     */
    protected void setupProxy(Proxy proxy) {
        if (proxy.getType().equals(Proxy.Type.HTTP)) {
            final ProxyAddress address = proxy.getAddress();
            getHostConfiguration().setProxy(address.getHostName(), address.getPort());
        } else if (proxy.getType().equals(Proxy.Type.SOCKS)) {
            Trace.trace(Activator.PLUGIN_ID, "retrieve socksproxy=" + proxy.getAddress()); //$NON-NLS-1$
            proxyHelper.setupProxy(proxy);
        }
    }

    public static NTCredentials createNTLMCredentials(Proxy p) {
        if (p == null) {
            return null;
        }
        String un = getNTLMUserName(p);
        String domain = getNTLMDomainName(p);
        if (un == null || domain == null)
            return null;
        return new NTCredentials(un, p.getPassword(), p.getAddress().getHostName(), domain);
    }

    protected static String getNTLMDomainName(Proxy p) {
        String domainUsername = p.getUsername();
        if (domainUsername == null)
            return null;
        int slashloc = domainUsername.indexOf('\\');
        if (slashloc == -1)
            return null;
        return domainUsername.substring(0, slashloc);
    }

    protected static String getNTLMUserName(Proxy p) {
        String domainUsername = p.getUsername();
        if (domainUsername == null)
            return null;
        int slashloc = domainUsername.indexOf('\\');
        if (slashloc == -1)
            return null;
        return domainUsername.substring(slashloc + 1);
    }

    protected void fireConnectStartEvent() {
        Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(),
                "fireConnectStartEvent"); //$NON-NLS-1$ 
        // TODO: should the following be in super.fireReceiveStartEvent();
        listener.handleTransferEvent(new IFileTransferConnectStartEvent() {
            public IFileID getFileID() {
                return remoteFileID;
            }

            public void cancel() {
                HttpClientRetrieveFileTransfer.this.cancel();
            }

            public FileTransferJob prepareConnectJob(FileTransferJob j) {
                return HttpClientRetrieveFileTransfer.this.prepareConnectJob(j);
            }

            public void connectUsingJob(FileTransferJob j) {
                HttpClientRetrieveFileTransfer.this.connectUsingJob(j);
            }

            public String toString() {
                final StringBuffer sb = new StringBuffer("IFileTransferConnectStartEvent["); //$NON-NLS-1$
                sb.append(getFileID());
                sb.append("]"); //$NON-NLS-1$
                return sb.toString();
            }

            public Object getAdapter(Class adapter) {
                return HttpClientRetrieveFileTransfer.this.getAdapter(adapter);
            }
        });
    }

    protected String createConnectJobName() {
        return getRemoteFileURL().toString() + createRangeName()
                + Messages.HttpClientRetrieveFileTransfer_CONNECTING_JOB_NAME;
    }

    protected FileTransferJob prepareConnectJob(FileTransferJob cjob) {
        if (cjob == null) {
            // Create our own
            cjob = new FileTransferJob(createJobName());
        }
        cjob.setFileTransfer(this);
        cjob.setFileTransferRunnable(fileConnectRunnable);
        return cjob;
    }

    protected void connectUsingJob(FileTransferJob cjob) {
        Assert.isNotNull(cjob);
        this.connectJob = cjob;
    }

    private IFileTransferRunnable fileConnectRunnable = new IFileTransferRunnable() {
        public IStatus performFileTransfer(IProgressMonitor monitor) {
            return performConnect(monitor);
        }
    };

    private IStatus performConnect(IProgressMonitor monitor) {
        // there might be more ticks in the future perhaps for 
        // connect socket, certificate validation, send request, authenticate,
        int ticks = 1;
        monitor.beginTask(
                getRemoteFileURL().toString() + Messages.HttpClientRetrieveFileTransfer_CONNECTING_TASK_NAME,
                ticks);
        try {
            if (monitor.isCanceled())
                throw newUserCancelledException();
            responseCode = httpClient.executeMethod(getHostConfiguration(), getMethod);
            Trace.trace(Activator.PLUGIN_ID, "retrieve resp=" + responseCode); //$NON-NLS-1$
        } catch (final Exception e) {
            Trace.catching(Activator.PLUGIN_ID, DebugOptions.EXCEPTIONS_CATCHING, this.getClass(), "performConnect", //$NON-NLS-1$
                    e);
            if (!isDone()) {
                setDoneException(e);
            }
        } finally {
            monitor.done();
        }
        return Status.OK_STATUS;

    }

    protected void fireReceiveResumedEvent() {
        Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(),
                "fireReceiveResumedEvent len=" + fileLength + ";rcvd=" + bytesReceived); //$NON-NLS-1$ //$NON-NLS-2$
        super.fireReceiveResumedEvent();
    }

    protected void fireTransferReceiveDataEvent() {
        Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(),
                "fireTransferReceiveDataEvent len=" + fileLength + ";rcvd=" + bytesReceived); //$NON-NLS-1$ //$NON-NLS-2$
        super.fireTransferReceiveDataEvent();
    }

    protected void fireTransferReceiveDoneEvent() {
        Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(),
                "fireTransferReceiveDoneEvent len=" + fileLength + ";rcvd=" + bytesReceived); //$NON-NLS-1$ //$NON-NLS-2$
        this.doneFired = true;
        super.fireTransferReceiveDoneEvent();
    }

    protected void fireTransferReceivePausedEvent() {
        Trace.entering(Activator.PLUGIN_ID, DebugOptions.METHODS_ENTERING, this.getClass(),
                "fireTransferReceivePausedEvent len=" + fileLength + ";rcvd=" + bytesReceived); //$NON-NLS-1$ //$NON-NLS-2$
        super.fireTransferReceivePausedEvent();
    }

}