You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@manifoldcf.apache.org by kw...@apache.org on 2012/12/04 18:47:30 UTC

svn commit: r1417060 [1/4] - in /manifoldcf/trunk: ./ connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/ connectors/meridio/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/meridio/ connectors...

Author: kwright
Date: Tue Dec  4 17:47:28 2012
New Revision: 1417060

URL: http://svn.apache.org/viewvc?rev=1417060&view=rev
Log:
Final checkin of CONNECTORS-120.  Port the final connectors to httpcomponents: livelink (untested), meridio (untested), web connector (tested thoroughly).  Also further fixes for SharePoint.

Removed:
    manifoldcf/trunk/connectors/meridio/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/meridio/ConnectionConfig.java
    manifoldcf/trunk/connectors/meridio/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/meridio/MeridioSecureSocketFactory.java
Modified:
    manifoldcf/trunk/   (props changed)
    manifoldcf/trunk/CHANGES.txt
    manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java
    manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkConnector.java
    manifoldcf/trunk/connectors/meridio/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/meridio/CommonsHTTPSender.java
    manifoldcf/trunk/connectors/meridio/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/meridio/MeridioAuthority.java
    manifoldcf/trunk/connectors/meridio/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/meridio/MeridioConnector.java
    manifoldcf/trunk/connectors/meridio/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/meridio/meridiowrapper/MeridioWrapper.java
    manifoldcf/trunk/connectors/sharepoint/   (props changed)
    manifoldcf/trunk/connectors/sharepoint/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/sharepoint/CommonsHTTPSender.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/CookieManager.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/CookieSet.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/CredentialsDescription.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/DataCache.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/LoginCookies.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/PageCredentials.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/ThrottledFetcher.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/TrustsDescription.java
    manifoldcf/trunk/connectors/webcrawler/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/webcrawler/WebcrawlerConnector.java
    manifoldcf/trunk/connectors/webcrawler/pom.xml

Propchange: manifoldcf/trunk/
------------------------------------------------------------------------------
  Merged /manifoldcf/branches/CONNECTORS-120:r1411049-1411252,1411254-1416451
  Merged /manifoldcf/branches/CONNECTORS-120-1:r1416450-1417056

Modified: manifoldcf/trunk/CHANGES.txt
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/CHANGES.txt?rev=1417060&r1=1417059&r2=1417060&view=diff
==============================================================================
--- manifoldcf/trunk/CHANGES.txt (original)
+++ manifoldcf/trunk/CHANGES.txt Tue Dec  4 17:47:28 2012
@@ -3,6 +3,10 @@ $Id$
 
 ======================= 1.1-dev =====================
 
+CONNECTORS-120: Port ManifoldCF to httpcomponents 4.2.2, from legacy
+commons-httpclient 3.1 (mcf edition).
+(Karl Wright)
+
 CONNECTORS-574: Add more Web connector session login stages, and
 a session login test.
 (Karl Wright)

Modified: manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java?rev=1417060&r1=1417059&r2=1417060&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java (original)
+++ manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkAuthority.java Tue Dec  4 17:47:28 2012
@@ -31,11 +31,6 @@ import java.util.regex.*;
 
 import com.opentext.api.*;
 
-import org.apache.commons.httpclient.*;
-import org.apache.commons.httpclient.methods.*;
-import org.apache.commons.httpclient.params.*;
-
-
 /** This is the Livelink implementation of the IAuthorityConnector interface.
 * This is not based on Volant code, but has been developed by me at the behest of
 * James Maupin for use at Shell.

Modified: manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkConnector.java
URL: http://svn.apache.org/viewvc/manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkConnector.java?rev=1417060&r1=1417059&r2=1417060&view=diff
==============================================================================
--- manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkConnector.java (original)
+++ manifoldcf/trunk/connectors/livelink/connector/src/main/java/org/apache/manifoldcf/crawler/connectors/livelink/LivelinkConnector.java Tue Dec  4 17:47:28 2012
@@ -23,18 +23,53 @@ import org.apache.manifoldcf.agents.inte
 import org.apache.manifoldcf.crawler.interfaces.*;
 import org.apache.manifoldcf.crawler.system.Logging;
 import org.apache.manifoldcf.crawler.system.ManifoldCF;
+import org.apache.manifoldcf.core.common.XThreadInputStream;
 
 import java.io.*;
 import java.util.*;
 import java.net.*;
+import java.util.concurrent.TimeUnit;
 
 import com.opentext.api.*;
 
-import org.apache.commons.httpclient.*;
-import org.apache.commons.httpclient.methods.*;
-import org.apache.commons.httpclient.params.*;
-import org.apache.commons.httpclient.auth.*;
-import org.apache.commons.httpclient.protocol.*;
+import org.apache.http.conn.ClientConnectionManager;
+import org.apache.http.client.HttpClient;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpPost;
+import org.apache.http.NameValuePair;
+import org.apache.http.impl.conn.PoolingClientConnectionManager;
+import org.apache.http.conn.scheme.Scheme;
+import org.apache.http.conn.ssl.SSLSocketFactory;
+import org.apache.http.conn.ssl.BrowserCompatHostnameVerifier;
+import org.apache.http.HttpEntity;
+import org.apache.http.HttpResponse;
+import org.apache.http.auth.AuthScope;
+import org.apache.http.auth.NTCredentials;
+import org.apache.http.client.methods.HttpGet;
+import org.apache.http.client.methods.HttpRequestBase;
+import org.apache.http.impl.client.DefaultHttpClient;
+import org.apache.http.impl.client.AbstractHttpClient;
+import org.apache.http.impl.client.DefaultRedirectStrategy;
+import org.apache.http.util.EntityUtils;
+import org.apache.http.params.BasicHttpParams;
+import org.apache.http.params.HttpParams;
+import org.apache.http.params.CoreConnectionPNames;
+import org.apache.http.HttpStatus;
+import org.apache.http.HttpHost;
+import org.apache.http.Header;
+import org.apache.http.conn.params.ConnRoutePNames;
+import org.apache.http.message.BasicHeader;
+import org.apache.http.client.params.ClientPNames;
+import org.apache.http.client.params.HttpClientParams;
+import org.apache.http.message.BasicNameValuePair;
+import org.apache.http.protocol.HTTP;
+import org.apache.http.client.entity.UrlEncodedFormEntity;
+
+import org.apache.http.conn.ConnectTimeoutException;
+import org.apache.http.client.RedirectException;
+import org.apache.http.client.CircularRedirectException;
+import org.apache.http.NoHttpResponseException;
+import org.apache.http.HttpException;
 
 
 /** This is the Livelink implementation of the IRepositoryConnectr interface.
@@ -105,12 +140,11 @@ public class LivelinkConnector extends o
   // SSL support
   private String keystoreData = null;
   private IKeystoreManager keystoreManager = null;
-  private LivelinkSecureSocketFactory secureSocketFactory = null;
-  private ProtocolFactory myFactory = null;
 
   // Connection management
-  private MultiThreadedHttpConnectionManager connectionManager = null;
-
+  private ClientConnectionManager connectionManager = null;
+  private AbstractHttpClient httpClient = null;
+  
   // Base path for viewing
   private String viewBasePath = null;
 
@@ -348,26 +382,39 @@ public class LivelinkConnector extends o
       else
         serverPort = new Integer(serverPortString).intValue();
 
+      // Set up connection manager
+      PoolingClientConnectionManager localConnectionManager = new PoolingClientConnectionManager();
+      localConnectionManager.setMaxTotal(1);
       // Set up ssl if indicated
       keystoreData = params.getParameter(LiveLinkParameters.livelinkKeystore);
-      myFactory = new ProtocolFactory();
-
       if (keystoreData != null)
       {
         keystoreManager = KeystoreManagerFactory.make("",keystoreData);
-        secureSocketFactory = new LivelinkSecureSocketFactory(keystoreManager.getSecureSocketFactory());
-        Protocol myHttpsProtocol = new Protocol("https", (ProtocolSocketFactory)secureSocketFactory, 443);
-        myFactory.registerProtocol("https",myHttpsProtocol);
-        if (Logging.connectors.isDebugEnabled())
-        {
-          Logging.connectors.debug("Livelink: Created new secure protocol class instance; factory type is '"+myHttpsProtocol.getSocketFactory().getClass().getName()+"'");
-        }
+        SSLSocketFactory myFactory = new SSLSocketFactory(keystoreManager.getSecureSocketFactory(),
+          new BrowserCompatHostnameVerifier());
+        Scheme myHttpsProtocol = new Scheme("https", 443, myFactory);
+        localConnectionManager.getSchemeRegistry().register(myHttpsProtocol);
+      }
+      connectionManager = localConnectionManager;
+
+      // Create the httpclient
+      BasicHttpParams params = new BasicHttpParams();
+      params.setBooleanParameter(CoreConnectionPNames.TCP_NODELAY,true);
+      params.setBooleanParameter(CoreConnectionPNames.STALE_CONNECTION_CHECK,false);
+      params.setBooleanParameter(ClientPNames.ALLOW_CIRCULAR_REDIRECTS,true);
+      params.setIntParameter(CoreConnectionPNames.SO_TIMEOUT,60000);
+      params.setIntParameter(CoreConnectionPNames.CONNECTION_TIMEOUT,300000);
+      params.setBooleanParameter(ClientPNames.HANDLE_REDIRECTS,true);
+      DefaultHttpClient localHttpClient = new DefaultHttpClient(connectionManager,params);
+      localHttpClient.setRedirectStrategy(new DefaultRedirectStrategy());
+      // Set up authentication to use
+      if (ntlmDomain != null)
+      {
+        localHttpClient.getCredentialsProvider().setCredentials(AuthScope.ANY,
+          new NTCredentials(ntlmUsername,ntlmPassword,currentHost,ntlmDomain));
       }
-
-      // Set up connection manager
-      connectionManager = new MultiThreadedHttpConnectionManager();
-      connectionManager.getParams().setMaxTotalConnections(1);
-
+      httpClient = localHttpClient;
+      
       // System.out.println("Connection server object = "+llServer.toString());
 
       // Establish the actual connection
@@ -412,73 +459,26 @@ public class LivelinkConnector extends o
   // All methods below this line will ONLY be called if a connect() call succeeded
   // on this instance!
 
-  protected static class ExecuteMethodThread extends Thread
-  {
-    protected HttpClient client;
-    protected HttpMethodBase executeMethod;
-    protected HostConfiguration hostConfiguration;
-    protected Throwable exception = null;
-    protected int rval = 0;
-
-    public ExecuteMethodThread(HttpClient client, HostConfiguration hostConfiguration, HttpMethodBase executeMethod)
-    {
-      super();
-      setDaemon(true);
-      this.client = client;
-      this.hostConfiguration = hostConfiguration;
-      this.executeMethod = executeMethod;
-    }
 
-    public void run()
-    {
-      try
-      {
-        // Call the execute method appropriately
-        rval = client.executeMethod(hostConfiguration,executeMethod,null);
-      }
-      catch (Throwable e)
-      {
-        this.exception = e;
-      }
-    }
-
-    public Throwable getException()
-    {
-      return exception;
-    }
-
-    public int getResponse()
-    {
-      return rval;
-    }
-  }
-
-  protected static int executeMethodViaThread(HttpClient client, HostConfiguration hostConfiguration, HttpMethodBase executeMethod)
-    throws InterruptedException, IOException
+  protected static int executeMethodViaThread(HttpClient client, HttpRequestBase executeMethod)
+    throws InterruptedException, HttpException, IOException
   {
-    ExecuteMethodThread t = new ExecuteMethodThread(client,hostConfiguration,executeMethod);
+    ExecuteMethodThread t = new ExecuteMethodThread(client,executeMethod);
+    t.start();
     try
     {
-      t.start();
-      t.join();
-      Throwable thr = t.getException();
-      if (thr != null)
-      {
-        if (thr instanceof IOException)
-          throw (IOException)thr;
-        else if (thr instanceof RuntimeException)
-          throw (RuntimeException)thr;
-        else
-          throw (Error)thr;
-      }
-      return t.getResponse();
+      return t.getResponseCode();
     }
     catch (InterruptedException e)
     {
       t.interrupt();
-      // We need the caller to abandon any connections left around, so rethrow in a way that forces them to process the event properly.
       throw e;
     }
+    finally
+    {
+      t.abort();
+      t.finishUp();
+    }
   }
 
   /** Check status of connection.
@@ -498,71 +498,56 @@ public class LivelinkConnector extends o
       String ingestHttpAddress = ingestCgiPath;
 
       HttpClient client = getInitializedClient(contextMsg);
-
+      HttpGet method = new HttpGet(ingestHttpAddress);
       try
       {
-        // Set up fetch using our special stuff if it's https
-        GetMethod method = new GetMethod(ingestHttpAddress);
-        try
+        int statusCode = executeMethodViaThread(client,method);
+        switch (statusCode)
         {
-          method.getParams().setParameter("http.socket.timeout", new Integer(300000));
-          method.setFollowRedirects(true);
-
-          int statusCode = executeMethodViaThread(client,getHostConfiguration(contextMsg),method);
-          switch (statusCode)
-          {
-          case 502:
-            return "Fetch test had transient 502 error response";
+        case 502:
+          return "Fetch test had transient 502 error response";
 
-          case HttpStatus.SC_UNAUTHORIZED:
-            return "Fetch test returned UNAUTHORIZED (401) response; check the security credentials and configuration";
+        case HttpStatus.SC_UNAUTHORIZED:
+          return "Fetch test returned UNAUTHORIZED (401) response; check the security credentials and configuration";
 
-          case HttpStatus.SC_OK:
-            return super.check();
+        case HttpStatus.SC_OK:
+          return super.check();
 
-          default:
-            return "Fetch test returned an unexpected response code of "+Integer.toString(statusCode);
-          }
-        }
-        catch (InterruptedException e)
-        {
-          // Drop the connection on the floor
-          method = null;
-          throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
-        }
-        catch (java.net.SocketTimeoutException e)
-        {
-          return "Fetch test timed out reading from the Livelink HTTP Server: "+e.getMessage();
-        }
-        catch (java.net.SocketException e)
-        {
-          return "Fetch test received a socket error reading from Livelink HTTP Server: "+e.getMessage();
-        }
-        catch (javax.net.ssl.SSLHandshakeException e)
-        {
-          return "Fetch test was unable to set up a SSL connection to Livelink HTTP Server: "+e.getMessage();
-        }
-        catch (org.apache.commons.httpclient.ConnectTimeoutException e)
-        {
-          return "Fetch test connection timed out reading from Livelink HTTP Server: "+e.getMessage();
-        }
-        catch (InterruptedIOException e)
-        {
-          throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
-        }
-        catch (IOException e)
-        {
-          return "Fetch test had an IO failure: "+e.getMessage();
-        }
-        finally
-        {
-          if (method != null)
-            method.releaseConnection();
+        default:
+          return "Fetch test returned an unexpected response code of "+Integer.toString(statusCode);
         }
       }
-      catch (IllegalStateException e)
+      catch (InterruptedException e)
+      {
+        throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+      }
+      catch (java.net.SocketTimeoutException e)
+      {
+        return "Fetch test timed out reading from the Livelink HTTP Server: "+e.getMessage();
+      }
+      catch (java.net.SocketException e)
+      {
+        return "Fetch test received a socket error reading from Livelink HTTP Server: "+e.getMessage();
+      }
+      catch (javax.net.ssl.SSLHandshakeException e)
+      {
+        return "Fetch test was unable to set up a SSL connection to Livelink HTTP Server: "+e.getMessage();
+      }
+      catch (ConnectTimeoutException e)
+      {
+        return "Fetch test connection timed out reading from Livelink HTTP Server: "+e.getMessage();
+      }
+      catch (InterruptedIOException e)
+      {
+        throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+      }
+      catch (HttpException e)
       {
-        return "Fetch test had a state exception talking to Livelink HTTP Server: "+e.getMessage();
+        return "Fetch test had an HTTP exception: "+e.getMessage();
+      }
+      catch (IOException e)
+      {
+        return "Fetch test had an IO failure: "+e.getMessage();
       }
     }
     catch (ServiceInterruption e)
@@ -585,7 +570,7 @@ public class LivelinkConnector extends o
     throws ManifoldCFException
   {
     if (connectionManager != null)
-      connectionManager.closeIdleConnections(60000L);
+      connectionManager.closeIdleConnections(60000L,TimeUnit.MILLISECONDS);
   }
 
   /** Close the connection.  Call this before discarding the repository connector.
@@ -601,8 +586,6 @@ public class LivelinkConnector extends o
     LLAttributes = null;
     keystoreData = null;
     keystoreManager = null;
-    secureSocketFactory = null;
-    myFactory = null;
     ingestPortNumber = -1;
 
     serverName = null;
@@ -3690,64 +3673,6 @@ public class LivelinkConnector extends o
     }
   }
 
-  protected static class CloseThread extends Thread
-  {
-    protected InputStream is;
-    protected Throwable exception = null;
-
-    public CloseThread(InputStream is)
-    {
-      super();
-      setDaemon(true);
-      this.is = is;
-    }
-
-    public void run()
-    {
-      try
-      {
-        // Call the close method appropriately
-        is.close();
-      }
-      catch (Throwable e)
-      {
-        this.exception = e;
-      }
-    }
-
-    public Throwable getException()
-    {
-      return exception;
-    }
-  }
-
-  protected static void closeViaThread(InputStream is)
-    throws InterruptedException, IOException
-  {
-    CloseThread t = new CloseThread(is);
-    try
-    {
-      t.start();
-      t.join();
-      Throwable thr = t.getException();
-      if (thr != null)
-      {
-        if (thr instanceof IOException)
-          throw (IOException)thr;
-        else if (thr instanceof RuntimeException)
-          throw (RuntimeException)thr;
-        else
-          throw (Error)thr;
-      }
-    }
-    catch (InterruptedException e)
-    {
-      t.interrupt();
-      // We need the caller to abandon any connections left around, so rethrow in a way that forces them to process the event properly.
-      throw e;
-    }
-  }
-
   /**
   * Connects to the specified Livelink document using HTTP protocol
   * @param documentIdentifier is the document identifier (as far as the crawler knows).
@@ -3868,328 +3793,299 @@ public class LivelinkConnector extends o
     if (Logging.connectors.isInfoEnabled())
       Logging.connectors.info("Livelink: " + ingestHttpAddress);
 
+    long startTime = System.currentTimeMillis();
+    String resultCode = "OK";
+    String resultDescription = null;
+    Long readSize = null;
+
+    HttpGet method = new HttpGet(ingestHttpAddress);
+    ExecuteMethodThread methodThread = new ExecuteMethodThread(client,method);
+    methodThread.start();
     try
     {
-      long startTime = System.currentTimeMillis();
-      String resultCode = "OK";
-      String resultDescription = null;
-      Long readSize = null;
 
-      // Set up fetch using our special stuff if it's https
-      GetMethod method = new GetMethod(ingestHttpAddress);
-      try
+      int statusCode = methodThread.getResponseCode();
+      switch (statusCode)
       {
-        method.getParams().setParameter("http.socket.timeout", new Integer(300000));
-        method.setFollowRedirects(true);
-
+      case 500:
+      case 502:
+        Logging.connectors.warn("Livelink: Service interruption during fetch "+contextMsg+" with Livelink HTTP Server, retrying...");
+        throw new ServiceInterruption("Service interruption during fetch",new ManifoldCFException(Integer.toString(statusCode)+" error while fetching"),System.currentTimeMillis()+60000L,
+          System.currentTimeMillis()+600000L,-1,true);
+
+      case HttpStatus.SC_UNAUTHORIZED:
+        Logging.connectors.warn("Livelink: Document fetch unauthorized for "+ingestHttpAddress+" ("+contextMsg+")");
+        // Since we logged in, we should fail here if the ingestion user doesn't have access to the
+        // the document, but if we do, don't fail hard.
+        resultCode = "UNAUTHORIZED";
+        activities.deleteDocument(documentIdentifier,version);
+        return;
 
-        int statusCode = executeMethodViaThread(client,getHostConfiguration(contextMsg),method);
-        switch (statusCode)
+      case HttpStatus.SC_OK:
+        if (Logging.connectors.isDebugEnabled())
+          Logging.connectors.debug("Livelink: Created http document connection to Livelink "+contextMsg);
+        long dataSize = methodThread.getResponseContentLength();
+        // The above replaces this, which required another access:
+        // long dataSize = (long)value.toInteger("DataSize");
+        // A non-existent content length will cause a value of -1 to be returned.  This seems to indicate that the session login did not work right.
+        if (dataSize >= 0)
         {
-        case 500:
-        case 502:
-          Logging.connectors.warn("Livelink: Service interruption during fetch "+contextMsg+" with Livelink HTTP Server, retrying...");
-          throw new ServiceInterruption("Service interruption during fetch",new ManifoldCFException(Integer.toString(statusCode)+" error while fetching"),System.currentTimeMillis()+60000L,
-            System.currentTimeMillis()+600000L,-1,true);
-
-        case HttpStatus.SC_UNAUTHORIZED:
-          Logging.connectors.warn("Livelink: Document fetch unauthorized for "+ingestHttpAddress+" ("+contextMsg+")");
-          // Since we logged in, we should fail here if the ingestion user doesn't have access to the
-          // the document, but if we do, don't fail hard.
-          resultCode = "UNAUTHORIZED";
-          activities.deleteDocument(documentIdentifier,version);
-          return;
-
-        case HttpStatus.SC_OK:
           if (Logging.connectors.isDebugEnabled())
-            Logging.connectors.debug("Livelink: Created http document connection to Livelink "+contextMsg);
-          long dataSize = method.getResponseContentLength();
-          // The above replaces this, which required another access:
-          // long dataSize = (long)value.toInteger("DataSize");
-          // A non-existent content length will cause a value of -1 to be returned.  This seems to indicate that the session login did not work right.
-          if (dataSize >= 0)
+            Logging.connectors.debug("Livelink: Content length from livelink server "+contextMsg+"' = "+new Long(dataSize).toString());
+          if (activities.checkLengthIndexable(dataSize))
           {
-            if (Logging.connectors.isDebugEnabled())
-              Logging.connectors.debug("Livelink: Content length from livelink server "+contextMsg+"' = "+new Long(dataSize).toString());
-            if (activities.checkLengthIndexable(dataSize))
+            try
             {
+              InputStream is = methodThread.getSafeInputStream();
               try
               {
-                InputStream is = method.getResponseBodyAsStream();
-                try
-                {
-                  rd.setBinary(is,dataSize);
-
-                  activities.ingestDocument(documentIdentifier,version,viewHttpAddress,rd);
+                rd.setBinary(is,dataSize);
+                
+                activities.ingestDocument(documentIdentifier,version,viewHttpAddress,rd);
 
-                  if (Logging.connectors.isDebugEnabled())
-                    Logging.connectors.debug("Livelink: Ingesting done "+contextMsg);
+                if (Logging.connectors.isDebugEnabled())
+                  Logging.connectors.debug("Livelink: Ingesting done "+contextMsg);
 
-                }
-                finally
-                {
-                  // Close stream via thread, since otherwise this can hang
-                  closeViaThread(is);
-                }
-              }
-              catch (java.net.SocketTimeoutException e)
-              {
-                resultCode = "DATATIMEOUT";
-                resultDescription = e.getMessage();
-                currentTime = System.currentTimeMillis();
-                Logging.connectors.warn("Livelink: Livelink socket timed out ingesting from the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-                throw new ServiceInterruption("Socket timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
-              }
-              catch (java.net.SocketException e)
-              {
-                resultCode = "DATASOCKETERROR";
-                resultDescription = e.getMessage();
-                currentTime = System.currentTimeMillis();
-                Logging.connectors.warn("Livelink: Livelink socket error ingesting from the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-                throw new ServiceInterruption("Socket error: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
               }
-              catch (javax.net.ssl.SSLHandshakeException e)
+              finally
               {
-                resultCode = "DATASSLHANDSHAKEERROR";
-                resultDescription = e.getMessage();
-                currentTime = System.currentTimeMillis();
-                Logging.connectors.warn("Livelink: SSL handshake failed authenticating "+contextMsg+": "+e.getMessage(),e);
-                throw new ServiceInterruption("SSL handshake error: "+e.getMessage(),e,currentTime+60000L,currentTime+300000L,-1,true);
+                // Close stream via thread, since otherwise this can hang
+                is.close();
               }
-              catch (org.apache.commons.httpclient.ConnectTimeoutException e)
-              {
-                resultCode = "CONNECTTIMEOUT";
-                resultDescription = e.getMessage();
-                currentTime = System.currentTimeMillis();
-                Logging.connectors.warn("Livelink: Livelink socket timed out connecting to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-                throw new ServiceInterruption("Connect timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
-              }
-              catch (InterruptedException e)
-              {
-                throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
-              }
-              catch (InterruptedIOException e)
-              {
-                throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
-              }
-              catch (IOException e)
-              {
-                resultCode = "DATAEXCEPTION";
-                resultDescription = e.getMessage();
-                // Treat unknown error ingesting data as a transient condition
-                currentTime = System.currentTimeMillis();
-                Logging.connectors.warn("Livelink: IO exception ingesting "+contextMsg+": "+e.getMessage(),e);
-                throw new ServiceInterruption("IO exception ingesting "+contextMsg+": "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
-              }
-              readSize = new Long(dataSize);
             }
-            else
+            catch (java.net.SocketTimeoutException e)
+            {
+              resultCode = "DATATIMEOUT";
+              resultDescription = e.getMessage();
+              currentTime = System.currentTimeMillis();
+              Logging.connectors.warn("Livelink: Livelink socket timed out ingesting from the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+              throw new ServiceInterruption("Socket timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
+            }
+            catch (java.net.SocketException e)
+            {
+              resultCode = "DATASOCKETERROR";
+              resultDescription = e.getMessage();
+              currentTime = System.currentTimeMillis();
+              Logging.connectors.warn("Livelink: Livelink socket error ingesting from the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+              throw new ServiceInterruption("Socket error: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
+            }
+            catch (javax.net.ssl.SSLHandshakeException e)
             {
-              resultCode = "DOCUMENTTOOLONG";
-              activities.deleteDocument(documentIdentifier,version);
+              resultCode = "DATASSLHANDSHAKEERROR";
+              resultDescription = e.getMessage();
+              currentTime = System.currentTimeMillis();
+              Logging.connectors.warn("Livelink: SSL handshake failed authenticating "+contextMsg+": "+e.getMessage(),e);
+              throw new ServiceInterruption("SSL handshake error: "+e.getMessage(),e,currentTime+60000L,currentTime+300000L,-1,true);
             }
+            catch (ConnectTimeoutException e)
+            {
+              resultCode = "CONNECTTIMEOUT";
+              resultDescription = e.getMessage();
+              currentTime = System.currentTimeMillis();
+              Logging.connectors.warn("Livelink: Livelink socket timed out connecting to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+              throw new ServiceInterruption("Connect timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
+            }
+            catch (InterruptedException e)
+            {
+              throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+            }
+            catch (InterruptedIOException e)
+            {
+              throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+            }
+            catch (HttpException e)
+            {
+              resultCode = "HTTPEXCEPTION";
+              resultDescription = e.getMessage();
+              // Treat unknown error ingesting data as a transient condition
+              currentTime = System.currentTimeMillis();
+              Logging.connectors.warn("Livelink: HTTP exception ingesting "+contextMsg+": "+e.getMessage(),e);
+              throw new ServiceInterruption("HTTP exception ingesting "+contextMsg+": "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
+            }
+            catch (IOException e)
+            {
+              resultCode = "DATAEXCEPTION";
+              resultDescription = e.getMessage();
+              // Treat unknown error ingesting data as a transient condition
+              currentTime = System.currentTimeMillis();
+              Logging.connectors.warn("Livelink: IO exception ingesting "+contextMsg+": "+e.getMessage(),e);
+              throw new ServiceInterruption("IO exception ingesting "+contextMsg+": "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,false);
+            }
+            readSize = new Long(dataSize);
           }
           else
           {
-            resultCode = "SESSIONLOGINFAILED";
+            resultCode = "DOCUMENTTOOLONG";
             activities.deleteDocument(documentIdentifier,version);
           }
-          break;
-        case HttpStatus.SC_BAD_REQUEST:
-        case HttpStatus.SC_USE_PROXY:
-        case HttpStatus.SC_GONE:
-          resultCode = "ERROR "+Integer.toString(statusCode);
-          throw new ManifoldCFException("Unrecoverable request failure; error = "+Integer.toString(statusCode));
-        default:
-          resultCode = "UNKNOWN";
-          Logging.connectors.warn("Livelink: Attempt to retrieve document from '"+ingestHttpAddress+"' received a response of "+Integer.toString(statusCode)+"; retrying in one minute");
-          currentTime = System.currentTimeMillis();
-          throw new ServiceInterruption("Fetch failed; retrying in 1 minute",new ManifoldCFException("Fetch failed with unknown code "+Integer.toString(statusCode)),
-            currentTime+60000L,currentTime+600000L,-1,true);
         }
-      }
-      catch (InterruptedException e)
-      {
-        // Drop the connection on the floor
-        method = null;
-        throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
-      }
-      catch (java.net.SocketTimeoutException e)
-      {
-        Logging.connectors.warn("Livelink: Socket timed out reading from the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-        resultCode = "TIMEOUT";
-        resultDescription = e.getMessage();
-        currentTime = System.currentTimeMillis();
-        throw new ServiceInterruption("Socket timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
-      }
-      catch (java.net.SocketException e)
-      {
-        Logging.connectors.warn("Livelink: Socket error reading from Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-        resultCode = "SOCKETERROR";
-        resultDescription = e.getMessage();
-        currentTime = System.currentTimeMillis();
-        throw new ServiceInterruption("Socket error: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
-      }
-      catch (javax.net.ssl.SSLHandshakeException e)
-      {
-        currentTime = System.currentTimeMillis();
-        Logging.connectors.warn("Livelink: SSL handshake failed "+contextMsg+": "+e.getMessage(),e);
-        resultCode = "SSLHANDSHAKEERROR";
-        resultDescription = e.getMessage();
-        throw new ServiceInterruption("SSL handshake error: "+e.getMessage(),e,currentTime+60000L,currentTime+300000L,-1,true);
-      }
-      catch (org.apache.commons.httpclient.ConnectTimeoutException e)
-      {
-        Logging.connectors.warn("Livelink: Connect timed out reading from the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-        resultCode = "CONNECTTIMEOUT";
-        resultDescription = e.getMessage();
+        else
+        {
+          resultCode = "SESSIONLOGINFAILED";
+          activities.deleteDocument(documentIdentifier,version);
+        }
+        break;
+      case HttpStatus.SC_BAD_REQUEST:
+      case HttpStatus.SC_USE_PROXY:
+      case HttpStatus.SC_GONE:
+        resultCode = "ERROR "+Integer.toString(statusCode);
+        throw new ManifoldCFException("Unrecoverable request failure; error = "+Integer.toString(statusCode));
+      default:
+        resultCode = "UNKNOWN";
+        Logging.connectors.warn("Livelink: Attempt to retrieve document from '"+ingestHttpAddress+"' received a response of "+Integer.toString(statusCode)+"; retrying in one minute");
         currentTime = System.currentTimeMillis();
-        throw new ServiceInterruption("Connect timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
+        throw new ServiceInterruption("Fetch failed; retrying in 1 minute",new ManifoldCFException("Fetch failed with unknown code "+Integer.toString(statusCode)),
+          currentTime+60000L,currentTime+600000L,-1,true);
       }
-      catch (InterruptedIOException e)
-      {
-        throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
-      }
-      catch (IOException e)
-      {
-        resultCode = "EXCEPTION";
-        resultDescription = e.getMessage();
-        throw new ManifoldCFException("Exception getting response "+contextMsg+": "+e.getMessage(), e);
-      }
-      finally
+    }
+    catch (InterruptedException e)
+    {
+      // Drop the connection on the floor
+      methodThread.interrupt();
+      methodThread = null;
+      throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+    }
+    catch (java.net.SocketTimeoutException e)
+    {
+      Logging.connectors.warn("Livelink: Socket timed out reading from the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+      resultCode = "TIMEOUT";
+      resultDescription = e.getMessage();
+      currentTime = System.currentTimeMillis();
+      throw new ServiceInterruption("Socket timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
+    }
+    catch (java.net.SocketException e)
+    {
+      Logging.connectors.warn("Livelink: Socket error reading from Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+      resultCode = "SOCKETERROR";
+      resultDescription = e.getMessage();
+      currentTime = System.currentTimeMillis();
+      throw new ServiceInterruption("Socket error: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
+    }
+    catch (javax.net.ssl.SSLHandshakeException e)
+    {
+      currentTime = System.currentTimeMillis();
+      Logging.connectors.warn("Livelink: SSL handshake failed "+contextMsg+": "+e.getMessage(),e);
+      resultCode = "SSLHANDSHAKEERROR";
+      resultDescription = e.getMessage();
+      throw new ServiceInterruption("SSL handshake error: "+e.getMessage(),e,currentTime+60000L,currentTime+300000L,-1,true);
+    }
+    catch (ConnectTimeoutException e)
+    {
+      Logging.connectors.warn("Livelink: Connect timed out reading from the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+      resultCode = "CONNECTTIMEOUT";
+      resultDescription = e.getMessage();
+      currentTime = System.currentTimeMillis();
+      throw new ServiceInterruption("Connect timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
+    }
+    catch (InterruptedIOException e)
+    {
+      throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+    }
+    catch (HttpException e)
+    {
+      resultCode = "EXCEPTION";
+      resultDescription = e.getMessage();
+      throw new ManifoldCFException("Exception getting response "+contextMsg+": "+e.getMessage(), e);
+    }
+    catch (IOException e)
+    {
+      resultCode = "EXCEPTION";
+      resultDescription = e.getMessage();
+      throw new ManifoldCFException("Exception getting response "+contextMsg+": "+e.getMessage(), e);
+    }
+    finally
+    {
+      if (methodThread != null)
       {
-        if (method != null)
+        methodThread.abort();
+        activities.recordActivity(new Long(startTime),ACTIVITY_FETCH,readSize,Integer.toString(objID),resultCode,resultDescription,null);
+        try
+        {
+         methodThread.finishUp();
+        }
+        catch (InterruptedException e)
         {
-          method.releaseConnection();
-          activities.recordActivity(new Long(startTime),ACTIVITY_FETCH,readSize,Integer.toString(objID),resultCode,resultDescription,null);
+          throw new ManifoldCFException(e.getMessage(),e,ManifoldCFException.INTERRUPTED);
         }
       }
     }
-    catch (IllegalStateException e)
-    {
-      Logging.connectors.error("Livelink: State exception dealing with '"+ingestHttpAddress+"': "+e.getMessage(),e);
-      throw new ManifoldCFException("State exception dealing with '"+ingestHttpAddress+"': "+e.getMessage(),e);
-    }
-  }
-
-  /** Initialize the host configuration */
-  protected HostConfiguration getHostConfiguration(String contextMsg)
-  {
-    HostConfiguration clientConf = new HostConfiguration();
-    // clientConf.setLocalAddress(currentAddr);
-    clientConf.setParams(new HostParams());
-    clientConf.setHost(llServer.getHost(),ingestPortNumber,myFactory.getProtocol(ingestProtocol));
-    return clientConf;
   }
 
   /** Initialize a livelink client connection */
   protected HttpClient getInitializedClient(String contextMsg)
     throws ServiceInterruption, ManifoldCFException
   {
-    HttpClient client = new HttpClient(connectionManager);
-    client.getParams().setParameter(org.apache.commons.httpclient.params.HttpClientParams.PROTOCOL_FACTORY,myFactory);
-    client.getParams().setParameter(org.apache.commons.httpclient.params.HttpClientParams.ALLOW_CIRCULAR_REDIRECTS,new Boolean(true));
-
     long currentTime;
-
-    if (ntlmDomain != null)
-    {
-      // Set the NTLM credentials
-      if (Logging.connectors.isDebugEnabled())
-        Logging.connectors.debug("Livelink: Setting up NTLM credentials "+contextMsg);
-      client.getState().setCredentials(AuthScope.ANY,
-        new NTCredentials(ntlmUsername,ntlmPassword,currentHost,ntlmDomain));
-    }
-
     if (Logging.connectors.isDebugEnabled())
       Logging.connectors.debug("Livelink: Session authenticating via http "+contextMsg+"...");
+    HttpGet authget = new HttpGet(createLivelinkLoginURI());
     try
     {
-      GetMethod authget = new GetMethod(createLivelinkLoginURI());
-      try
-      {
-        authget.getParams().setParameter("http.socket.timeout", new Integer(60000));
-        authget.setFollowRedirects(true);
-        if (Logging.connectors.isDebugEnabled())
-          Logging.connectors.debug("Livelink: Created new GetMethod "+contextMsg+"; executing authentication method");
-        int statusCode = executeMethodViaThread(client,getHostConfiguration(contextMsg),authget);
-
-        if (statusCode == 502 || statusCode == 500)
-        {
-          Logging.connectors.warn("Livelink: Service interruption during authentication "+contextMsg+" with Livelink HTTP Server, retrying...");
-          currentTime = System.currentTimeMillis();
-          throw new ServiceInterruption("502 error during authentication",new ManifoldCFException("502 error while authenticating"),
-            currentTime+60000L,currentTime+600000L,-1,true);
-        }
-        if (statusCode != HttpStatus.SC_OK)
-        {
-          Logging.connectors.error("Livelink: Failed to authenticate "+contextMsg+" against Livelink HTTP Server; Status code: " + statusCode);
-          // Ok, so we didn't get in - simply do not ingest
-          if (statusCode == HttpStatus.SC_UNAUTHORIZED)
-            throw new ManifoldCFException("Session authorization failed with a 401 code; are credentials correct?");
-          else
-            throw new ManifoldCFException("Session authorization failed with code "+Integer.toString(statusCode));
-        }
-        if (Logging.connectors.isDebugEnabled())
-          Logging.connectors.debug("Livelink: Retrieving authentication response "+contextMsg+"");
-        authget.getResponseBodyAsStream();
-        if (Logging.connectors.isDebugEnabled())
-          Logging.connectors.debug("Livelink: Authentication response retrieved "+contextMsg+"");
+      if (Logging.connectors.isDebugEnabled())
+        Logging.connectors.debug("Livelink: Created new HttpGet "+contextMsg+"; executing authentication method");
+      int statusCode = executeMethodViaThread(httpClient,authget);
 
-      }
-      catch (InterruptedException e)
-      {
-        // Drop the connection on the floor
-        authget = null;
-        throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
-      }
-      catch (java.net.SocketTimeoutException e)
+      if (statusCode == 502 || statusCode == 500)
       {
+        Logging.connectors.warn("Livelink: Service interruption during authentication "+contextMsg+" with Livelink HTTP Server, retrying...");
         currentTime = System.currentTimeMillis();
-        Logging.connectors.warn("Livelink: Socket timed out authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-        throw new ServiceInterruption("Socket timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
+        throw new ServiceInterruption("502 error during authentication",new ManifoldCFException("502 error while authenticating"),
+          currentTime+60000L,currentTime+600000L,-1,true);
       }
-      catch (java.net.SocketException e)
+      if (statusCode != HttpStatus.SC_OK)
       {
-        currentTime = System.currentTimeMillis();
-        Logging.connectors.warn("Livelink: Socket error authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-        throw new ServiceInterruption("Socket error: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
-      }
-      catch (javax.net.ssl.SSLHandshakeException e)
-      {
-        currentTime = System.currentTimeMillis();
-        Logging.connectors.warn("Livelink: SSL handshake failed authenticating "+contextMsg+": "+e.getMessage(),e);
-        throw new ServiceInterruption("SSL handshake error: "+e.getMessage(),e,currentTime+60000L,currentTime+300000L,-1,true);
-      }
-      catch (org.apache.commons.httpclient.ConnectTimeoutException e)
-      {
-        currentTime = System.currentTimeMillis();
-        Logging.connectors.warn("Livelink: Connect timed out authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-        throw new ServiceInterruption("Connect timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
-      }
-      catch (InterruptedIOException e)
-      {
-        throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
-      }
-      catch (IOException e)
-      {
-        Logging.connectors.error("Livelink: IO exception when authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
-        throw new ManifoldCFException("Unable to communicate with the Livelink HTTP Server: "+e.getMessage(), e);
-
-      }
-      finally
-      {
-        if (authget != null)
-          authget.releaseConnection();
+        Logging.connectors.error("Livelink: Failed to authenticate "+contextMsg+" against Livelink HTTP Server; Status code: " + statusCode);
+        // Ok, so we didn't get in - simply do not ingest
+        if (statusCode == HttpStatus.SC_UNAUTHORIZED)
+          throw new ManifoldCFException("Session authorization failed with a 401 code; are credentials correct?");
+        else
+          throw new ManifoldCFException("Session authorization failed with code "+Integer.toString(statusCode));
       }
     }
-    catch (IllegalStateException e)
+    catch (InterruptedException e)
+    {
+      throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+    }
+    catch (java.net.SocketTimeoutException e)
+    {
+      currentTime = System.currentTimeMillis();
+      Logging.connectors.warn("Livelink: Socket timed out authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+      throw new ServiceInterruption("Socket timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
+    }
+    catch (java.net.SocketException e)
+    {
+      currentTime = System.currentTimeMillis();
+      Logging.connectors.warn("Livelink: Socket error authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+      throw new ServiceInterruption("Socket error: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
+    }
+    catch (javax.net.ssl.SSLHandshakeException e)
+    {
+      currentTime = System.currentTimeMillis();
+      Logging.connectors.warn("Livelink: SSL handshake failed authenticating "+contextMsg+": "+e.getMessage(),e);
+      throw new ServiceInterruption("SSL handshake error: "+e.getMessage(),e,currentTime+60000L,currentTime+300000L,-1,true);
+    }
+    catch (ConnectTimeoutException e)
+    {
+      currentTime = System.currentTimeMillis();
+      Logging.connectors.warn("Livelink: Connect timed out authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+      throw new ServiceInterruption("Connect timed out: "+e.getMessage(),e,currentTime+300000L,currentTime+6*3600000L,-1,true);
+    }
+    catch (InterruptedIOException e)
     {
-      Logging.connectors.error("Livelink: State exception dealing with '"+createLivelinkLoginURI()+"'",e);
-      throw new ManifoldCFException("State exception dealing with login URI: "+e.getMessage(),e);
+      throw new ManifoldCFException("Interrupted: "+e.getMessage(),e,ManifoldCFException.INTERRUPTED);
+    }
+    catch (HttpException e)
+    {
+      Logging.connectors.error("Livelink: HTTP exception when authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+      throw new ManifoldCFException("Unable to communicate with the Livelink HTTP Server: "+e.getMessage(), e);
+    }
+    catch (IOException e)
+    {
+      Logging.connectors.error("Livelink: IO exception when authenticating to the Livelink HTTP Server "+contextMsg+": "+e.getMessage(), e);
+      throw new ManifoldCFException("Unable to communicate with the Livelink HTTP Server: "+e.getMessage(), e);
     }
 
-    return client;
+    return httpClient;
   }
 
   /** Pack category and attribute */
@@ -6086,104 +5982,6 @@ public class LivelinkConnector extends o
     }
   }
 
-  /** HTTPClient secure socket factory, which implements SecureProtocolSocketFactory
-  */
-  protected static class LivelinkSecureSocketFactory implements SecureProtocolSocketFactory
-  {
-    /** This is the javax.net socket factory.
-    */
-    protected javax.net.ssl.SSLSocketFactory socketFactory;
-
-    /** Constructor */
-    public LivelinkSecureSocketFactory(javax.net.ssl.SSLSocketFactory socketFactory)
-    {
-      this.socketFactory = socketFactory;
-    }
-
-    public Socket createSocket(
-      String host,
-      int port,
-      InetAddress clientHost,
-      int clientPort)
-      throws IOException, UnknownHostException
-    {
-      if (Logging.connectors.isDebugEnabled())
-        Logging.connectors.debug("Livelink: Creating secure livelink connection to '"+host+"' on port "+Integer.toString(port));
-      return socketFactory.createSocket(
-        host,
-        port,
-        clientHost,
-        clientPort
-      );
-    }
-
-    public Socket createSocket(
-      final String host,
-      final int port,
-      final InetAddress localAddress,
-      final int localPort,
-      final HttpConnectionParams params
-    ) throws IOException, UnknownHostException, ConnectTimeoutException
-    {
-      if (Logging.connectors.isDebugEnabled())
-        Logging.connectors.debug("Livelink: Creating secure livelink connection to '"+host+"' on port "+Integer.toString(port));
-
-      if (params == null)
-      {
-        throw new IllegalArgumentException("Parameters may not be null");
-      }
-      int timeout = params.getConnectionTimeout();
-      if (timeout == 0)
-      {
-        return createSocket(host, port, localAddress, localPort);
-      }
-      else
-        throw new IllegalArgumentException("This implementation does not handle non-zero connection timeouts");
-    }
-
-    public Socket createSocket(String host, int port)
-      throws IOException, UnknownHostException
-    {
-      if (Logging.connectors.isDebugEnabled())
-        Logging.connectors.debug("Livelink: Creating secure livelink connection to '"+host+"' on port "+Integer.toString(port));
-      return socketFactory.createSocket(
-        host,
-        port
-      );
-    }
-
-    public Socket createSocket(
-      Socket socket,
-      String host,
-      int port,
-      boolean autoClose)
-      throws IOException, UnknownHostException
-    {
-      if (Logging.connectors.isDebugEnabled())
-        Logging.connectors.debug("Livelink: Creating secure livelink connection to '"+host+"' on port "+Integer.toString(port));
-      return socketFactory.createSocket(
-        socket,
-        host,
-        port,
-        autoClose
-      );
-    }
-
-    public boolean equals(Object obj)
-    {
-      if (obj == null || !(obj instanceof LivelinkSecureSocketFactory))
-        return false;
-      // Each object is unique
-      return super.equals(obj);
-    }
-
-    public int hashCode()
-    {
-      return super.hashCode();
-    }
-
-  }
-
   // Here's an interesting note.  All of the LAPI exceptions are subclassed off of RuntimeException.  This makes life
   // hell because there is no superclass exception to capture, and even tweaky server communication issues wind up throwing
   // uncaught RuntimeException's up the stack.
@@ -6285,6 +6083,262 @@ public class LivelinkConnector extends o
 
   }
 
+  /** This thread does the actual socket communication with the server.
+  * It's set up so that it can be abandoned at shutdown time.
+  *
+  * The way it works is as follows:
+  * - it starts the transaction
+  * - it receives the response, and saves that for the calling class to inspect
+  * - it transfers the data part to an input stream provided to the calling class
+  * - it shuts the connection down
+  *
+  * If there is an error, the sequence is aborted, and an exception is recorded
+  * for the calling class to examine.
+  *
+  * The calling class basically accepts the sequence above.  It starts the
+  * thread, and tries to get a response code.  If instead an exception is seen,
+  * the exception is thrown up the stack.
+  */
+  protected static class ExecuteMethodThread extends Thread
+  {
+    /** Client and method, all preconfigured */
+    protected final HttpClient httpClient;
+    protected final HttpRequestBase executeMethod;
+    
+    protected HttpResponse response = null;
+    protected Throwable responseException = null;
+    protected XThreadInputStream threadStream = null;
+    protected boolean streamCreated = false;
+    protected Throwable streamException = null;
+    protected boolean abortThread = false;
+
+    protected Throwable shutdownException = null;
+
+    protected Throwable generalException = null;
+    
+    public ExecuteMethodThread(HttpClient httpClient, HttpRequestBase executeMethod)
+    {
+      super();
+      setDaemon(true);
+      this.httpClient = httpClient;
+      this.executeMethod = executeMethod;
+    }
+
+    public void run()
+    {
+      try
+      {
+        try
+        {
+          // Call the execute method appropriately
+          synchronized (this)
+          {
+            if (!abortThread)
+            {
+              try
+              {
+                response = httpClient.execute(executeMethod);
+              }
+              catch (java.net.SocketTimeoutException e)
+              {
+                responseException = e;
+              }
+              catch (ConnectTimeoutException e)
+              {
+                responseException = e;
+              }
+              catch (InterruptedIOException e)
+              {
+                throw e;
+              }
+              catch (Throwable e)
+              {
+                responseException = e;
+              }
+              this.notifyAll();
+            }
+          }
+          
+          // Start the transfer of the content
+          if (responseException == null)
+          {
+            synchronized (this)
+            {
+              if (!abortThread)
+              {
+                try
+                {
+                  InputStream bodyStream = response.getEntity().getContent();
+                  if (bodyStream != null)
+                  {
+                    threadStream = new XThreadInputStream(bodyStream);
+                  }
+                  streamCreated = true;
+                }
+                catch (java.net.SocketTimeoutException e)
+                {
+                  streamException = e;
+                }
+                catch (ConnectTimeoutException e)
+                {
+                  streamException = e;
+                }
+                catch (InterruptedIOException e)
+                {
+                  throw e;
+                }
+                catch (Throwable e)
+                {
+                  streamException = e;
+                }
+                this.notifyAll();
+              }
+            }
+          }
+          
+          if (responseException == null && streamException == null)
+          {
+            if (threadStream != null)
+            {
+              // Stuff the content until we are done
+              threadStream.stuffQueue();
+            }
+          }
+          
+        }
+        finally
+        {
+          synchronized (this)
+          {
+            try
+            {
+              executeMethod.abort();
+            }
+            catch (Throwable e)
+            {
+              shutdownException = e;
+            }
+            this.notifyAll();
+          }
+        }
+      }
+      catch (Throwable e)
+      {
+        // We catch exceptions here that should ONLY be InterruptedExceptions, as a result of the thread being aborted.
+        this.generalException = e;
+      }
+    }
+
+    public int getResponseCode()
+      throws InterruptedException, IOException, HttpException
+    {
+      // Must wait until the response object is there
+      while (true)
+      {
+        synchronized (this)
+        {
+          checkException(responseException);
+          if (response != null)
+            return response.getStatusLine().getStatusCode();
+          wait();
+        }
+      }
+    }
+
+    public long getResponseContentLength()
+      throws InterruptedException, IOException, HttpException
+    {
+      String contentLength = getFirstHeader("Content-Length");
+      if (contentLength == null || contentLength.length() == 0)
+        return -1L;
+      return new Long(contentLength.trim()).longValue();
+    }
+    
+    public String getFirstHeader(String headerName)
+      throws InterruptedException, IOException, HttpException
+    {
+      // Must wait for the response object to appear
+      while (true)
+      {
+        synchronized (this)
+        {
+          checkException(responseException);
+          if (response != null)
+          {
+            Header h = response.getFirstHeader(headerName);
+            if (h == null)
+              return null;
+            return h.getValue();
+          }
+          wait();
+        }
+      }
+    }
+
+    public InputStream getSafeInputStream()
+      throws InterruptedException, IOException, HttpException
+    {
+      // Must wait until stream is created, or until we note an exception was thrown.
+      while (true)
+      {
+        synchronized (this)
+        {
+          if (responseException != null)
+            throw new IllegalStateException("Check for response before getting stream");
+          checkException(streamException);
+          if (streamCreated)
+            return threadStream;
+          wait();
+        }
+      }
+    }
+    
+    public void abort()
+    {
+      // This will be called during the finally
+      // block in the case where all is well (and
+      // the stream completed) and in the case where
+      // there were exceptions.
+      synchronized (this)
+      {
+        if (streamCreated)
+        {
+          if (threadStream != null)
+            threadStream.abort();
+        }
+        abortThread = true;
+      }
+    }
+    
+    public void finishUp()
+      throws InterruptedException
+    {
+      join();
+    }
+    
+    protected synchronized void checkException(Throwable exception)
+      throws IOException, HttpException
+    {
+      if (exception != null)
+      {
+        // Throw the current exception, but clear it, so no further throwing is possible on the same problem.
+        Throwable e = exception;
+        if (e instanceof IOException)
+          throw (IOException)e;
+        else if (e instanceof HttpException)
+          throw (HttpException)e;
+        else if (e instanceof RuntimeException)
+          throw (RuntimeException)e;
+        else if (e instanceof Error)
+          throw (Error)e;
+        else
+          throw new RuntimeException("Unhandled exception of type: "+e.getClass().getName(),e);
+      }
+    }
+
+  }
+
+
 }