Add Startup exception to Adaptor init for permanent failure scenarios
Code Review : https://codereview.appspot.com/95310044/
diff --git a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
index 06e03fb..bc4cd0d 100644
--- a/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
+++ b/src/com/google/enterprise/adaptor/sharepoint/SharePointAdaptor.java
@@ -28,10 +28,12 @@
 import com.google.enterprise.adaptor.DocIdPusher;
 import com.google.enterprise.adaptor.GroupPrincipal;
 import com.google.enterprise.adaptor.IOHelper;
+import com.google.enterprise.adaptor.InvalidConfigurationException;
 import com.google.enterprise.adaptor.PollingIncrementalLister;
 import com.google.enterprise.adaptor.Principal;
 import com.google.enterprise.adaptor.Request;
 import com.google.enterprise.adaptor.Response;
+import com.google.enterprise.adaptor.StartupException;
 import com.google.enterprise.adaptor.UserPrincipal;
 import com.google.enterprise.adaptor.sharepoint.RareModificationCache.CachedList;
 import com.google.enterprise.adaptor.sharepoint.RareModificationCache.CachedVirtualServer;
@@ -88,6 +90,7 @@
 import java.io.IOException;
 import java.io.InputStream;
 import java.net.Authenticator;
+import java.net.ConnectException;
 import java.net.HttpURLConnection;
 import java.net.PasswordAuthentication;
 import java.net.URI;
@@ -511,20 +514,31 @@
     URL virtualServerUrl = new URL(virtualServer);
     ntlmAuthenticator.addPermitForHost(virtualServerUrl);
     scheduledExecutor = new ScheduledThreadPoolExecutor(1);
-   
-    
+    String authenticationType;
     if (useLiveAuthentication)  {
+      if ("".equals(username) || "".equals(password)) {
+        throw new InvalidConfigurationException("Adaptor is configured to "
+            + "use Live authentication. Please specify valid username "
+            + "and password.");
+      }
       SamlHandshakeManager manager = authenticationClientFactory
           .newLiveAuthentication(virtualServer, username, password);
       authenticationHandler = new SamlAuthenticationHandler.Builder(username,
           password, scheduledExecutor, manager).build();     
+      authenticationType = "Live";
     } else if (!"".equals(stsendpoint) && !"".equals(stsrealm)) {
+      if ("".equals(username) || "".equals(password)) {
+        throw new InvalidConfigurationException("Adaptor is configured to "
+            + "use ADFS authentication. Please specify valid username "
+            + "and password.");
+      }
       SamlHandshakeManager manager = authenticationClientFactory
           .newAdfsAuthentication(virtualServer, username, password, stsendpoint,
               stsrealm, config.getValue("sharepoint.sts.login"),
               config.getValue("sharepoint.sts.trustLocation"));
       authenticationHandler = new SamlAuthenticationHandler.Builder(username,
           password, scheduledExecutor, manager).build();            
+      authenticationType = "ADFS";
     } else {    
       AuthenticationSoap authenticationSoap = authenticationClientFactory
           .newSharePointFormsAuthentication(virtualServer, username, password);
@@ -532,10 +546,35 @@
       authenticationHandler = new SharePointFormsAuthenticationHandler
           .Builder(username, password, scheduledExecutor, authenticationSoap)
           .build();
+      authenticationType = "SharePoint";
+    }
+    
+    try {
+      log.log(Level.INFO, "Using {0} authentication.", authenticationType);
+      authenticationHandler.start();      
+    } catch (IOException ex) {
+      if (ex instanceof UnknownHostException) {
+       // This may be due to  DNS issue or transiant network error.
+       // Just rethrow excption and allow adaptor to retry.
+       throw ex; 
+      }
+      
+      if (ex instanceof ConnectException) {
+        // SharePoint might be down. Just rethrow exception and allow adaptor to
+        // retry.
+        throw ex;
+      }
+      String adfsWarning = "ADFS".equals(authenticationType) 
+          ? " Also verify if stsendpoint and stsrealm is specified correctly "
+          + "and ADFS environment is available." : "";
+      String warning = String.format(
+          "Failed to start adaptor using %s authentication."
+          + " Please verify adaptor configuration for SharePoint url,"
+          + " username and password.%s", authenticationType, adfsWarning);
+      throw new StartupException(warning, ex);
     }
    
     try {
-      authenticationHandler.start();
       executor = executorFactory.call();
       SiteAdaptor vsAdaptor = getSiteAdaptor(virtualServer, virtualServer);
       SiteDataClient virtualServerSiteDataClient =
diff --git a/src/com/google/enterprise/adaptor/sharepoint/SharePointUserProfileAdaptor.java b/src/com/google/enterprise/adaptor/sharepoint/SharePointUserProfileAdaptor.java
index db23326..31034b2 100644
--- a/src/com/google/enterprise/adaptor/sharepoint/SharePointUserProfileAdaptor.java
+++ b/src/com/google/enterprise/adaptor/sharepoint/SharePointUserProfileAdaptor.java
@@ -23,9 +23,11 @@
 import com.google.enterprise.adaptor.DocId;
 import com.google.enterprise.adaptor.DocIdPusher;
 import com.google.enterprise.adaptor.GroupPrincipal;
+import com.google.enterprise.adaptor.InvalidConfigurationException;
 import com.google.enterprise.adaptor.PollingIncrementalLister;
 import com.google.enterprise.adaptor.Request;
 import com.google.enterprise.adaptor.Response;
+import com.google.enterprise.adaptor.StartupException;
 import com.google.enterprise.adaptor.sharepoint.SamlAuthenticationHandler.SamlHandshakeManager;
 import com.microsoft.schemas.sharepoint.soap.authentication.AuthenticationSoap;
 
@@ -56,10 +58,12 @@
 import java.io.StringWriter;
 import java.io.UnsupportedEncodingException;
 import java.net.Authenticator;
+import java.net.ConnectException;
 import java.net.PasswordAuthentication;
 import java.net.URI;
 import java.net.URL;
 import java.net.URLEncoder;
+import java.net.UnknownHostException;
 import java.nio.charset.Charset;
 import java.rmi.RemoteException;
 import java.text.MessageFormat;
@@ -217,7 +221,7 @@
   }
 
   @Override
-  public void init(AdaptorContext context) throws IOException {
+  public void init(AdaptorContext context) throws Exception {
     context.setPollingIncrementalLister(this);
     Config config = context.getConfig();
 
@@ -265,18 +269,31 @@
     // Unfortunately, this is a JVM-wide modification.
     Authenticator.setDefault(ntlmAuthenticator);
      
+    String authenticationType;
     if (useLiveAuthentication)  {
+      if ("".equals(username) || "".equals(password)) {
+        throw new InvalidConfigurationException("Adaptor is configured to "
+            + "use Live authentication. Please specify valid username "
+            + "and password.");
+      }
       SamlHandshakeManager manager = authenticationClientFactory
           .newLiveAuthentication(virtualServer, username, password);
       authenticationHandler = new SamlAuthenticationHandler.Builder(username,
           password, scheduledExecutor, manager).build();     
+      authenticationType = "Live";
     } else if (!"".equals(stsendpoint) && !"".equals(stsrealm)) {
+      if ("".equals(username) || "".equals(password)) {
+        throw new InvalidConfigurationException("Adaptor is configured to "
+            + "use ADFS authentication. Please specify valid username "
+            + "and password.");
+      }
       SamlHandshakeManager manager = authenticationClientFactory
           .newAdfsAuthentication(virtualServer, username, password, stsendpoint,
               stsrealm, config.getValue("sharepoint.sts.login"),
               config.getValue("sharepoint.sts.trustLocation"));
       authenticationHandler = new SamlAuthenticationHandler.Builder(username,
           password, scheduledExecutor, manager).build();            
+      authenticationType = "ADFS";
     } else {    
       AuthenticationSoap authenticationSoap = authenticationClientFactory
           .newSharePointFormsAuthentication(virtualServer, username, password);
@@ -284,8 +301,33 @@
       authenticationHandler = new SharePointFormsAuthenticationHandler
           .Builder(username, password, scheduledExecutor, authenticationSoap)
           .build();
+      authenticationType = "SharePoint";
     }
-    authenticationHandler.start();
+    
+    try {
+      log.log(Level.INFO, "Using {0} authentication.", authenticationType);
+      authenticationHandler.start();      
+    } catch (IOException ex) {
+      if (ex instanceof UnknownHostException) {
+       // This may be due to  DNS issue or transiant network error.
+       // Just rethrow excption and allow adaptor to retry.
+       throw ex; 
+      }
+      
+      if (ex instanceof ConnectException) {
+        // SharePoint might be down. Just rethrow exception and allow adaptor to
+        // retry.
+        throw ex;
+      }
+      String adfsWarning = "ADFS".equals(authenticationType) 
+          ? " Also verify if stsendpoint and stsrealm is specified correctly "
+          + "and ADFS environment is available." : "";
+      String warning = String.format(
+          "Failed to start adaptor using %s authentication."
+          + " Please verify adaptor configuration for SharePoint url,"
+          + " username and password.%s", authenticationType, adfsWarning);
+      throw new StartupException(warning, ex);
+    }
     log.log(Level.FINEST, "Initializing User profile Service Client for {0}",
         virtualServer + USER_PROFILE_SERVICE_ENDPOINT);
     userProfileServiceClient = new UserProfileServiceClient(
diff --git a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
index d5cd8e3..ecc512a 100644
--- a/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/sharepoint/SharePointAdaptorTest.java
@@ -32,6 +32,7 @@
 import com.google.enterprise.adaptor.DocIdPusher;
 import com.google.enterprise.adaptor.GroupPrincipal;
 import com.google.enterprise.adaptor.IOHelper;
+import com.google.enterprise.adaptor.InvalidConfigurationException;
 import com.google.enterprise.adaptor.Metadata;
 import com.google.enterprise.adaptor.Principal;
 import com.google.enterprise.adaptor.UserPrincipal;
@@ -102,6 +103,7 @@
 import java.math.BigInteger;
 import java.net.URI;
 import java.net.URL;
+import java.net.UnknownHostException;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.Arrays;
@@ -421,6 +423,68 @@
   }
   
   @Test
+  public void testAdaptorInitAdfsUnknownHostException() throws Exception {
+    SoapFactory siteDataFactory = MockSoapFactory.blank()
+        .endpoint(VS_ENDPOINT, MockSiteData.blank()
+            .register(VS_CONTENT_EXCHANGE)
+            .register(CD_CONTENT_EXCHANGE))
+        .endpoint("http://localhost:1/_vti_bin/People.asmx",
+            new MockPeopleSoap())
+        .endpoint("http://localhost:1/_vti_bin/UserGroup.asmx",
+            new MockUserGroupSoap(null));
+    final MockSamlHandshakeManager samlManager 
+        = new MockSamlHandshakeManager("token", "cookie") {
+          @Override
+          public String requestToken() throws IOException{
+            throw new UnknownHostException("stsendpoint");
+          }
+    };
+    adaptor = new SharePointAdaptor(siteDataFactory,
+        new UnsupportedHttpClient(), executorFactory,
+        new MockAuthenticationClientFactoryAdfs() {
+          @Override
+          public SamlHandshakeManager newAdfsAuthentication(
+              String virtualServer, String username, String password,
+              String stsendpoint, String stsrelam, String login,
+              String trustlocation) throws IOException {
+            return samlManager;
+          } 
+        });
+    config.overrideKey("sharepoint.sts.endpoint", "https://stsendpoint");
+    config.overrideKey("sharepoint.sts.realm", "urn:sharepoint:com");
+    thrown.expect(IOException.class);
+    adaptor.init(new MockAdaptorContext(config, pusher));   
+    adaptor = null;
+  }
+  
+  @Test
+  public void testAdaptorInitAdfsWithBlankUsername() throws Exception {
+    adaptor = new SharePointAdaptor(initableSoapFactory,
+        new UnsupportedHttpClient(), executorFactory,
+        new MockAuthenticationClientFactoryForms());
+    config.overrideKey("sharepoint.sts.endpoint", "https://stsendpoint");
+    config.overrideKey("sharepoint.sts.realm", "urn:sharepoint:com");
+    config.overrideKey("sharepoint.usernamet", "");
+    config.overrideKey("sharepoint.password", "");
+    thrown.expect(InvalidConfigurationException.class);
+    adaptor.init(new MockAdaptorContext(config, pusher));
+    adaptor = null;
+  }
+  
+  @Test
+  public void testAdaptorInitLiveWithBlankUsername() throws Exception {
+    adaptor = new SharePointAdaptor(initableSoapFactory,
+        new UnsupportedHttpClient(), executorFactory,
+        new MockAuthenticationClientFactoryForms());
+    config.overrideKey("sharepoint.useLiveAuthentication", "true");
+    config.overrideKey("sharepoint.usernamet", "");
+    config.overrideKey("sharepoint.password", "");
+    thrown.expect(InvalidConfigurationException.class);
+    adaptor.init(new MockAdaptorContext(config, pusher));
+    adaptor = null;
+  }
+  
+  @Test
   public void testAdaptorInitWithMissingRelam() throws Exception {
     SoapFactory siteDataFactory = MockSoapFactory.blank()
         .endpoint(VS_ENDPOINT, MockSiteData.blank()
@@ -3071,7 +3135,7 @@
     }
     
     @Override
-    public String requestToken() {
+    public String requestToken() throws IOException {
       return token;
     }
     
diff --git a/test/com/google/enterprise/adaptor/sharepoint/SharePointUserProfileAdaptorTest.java b/test/com/google/enterprise/adaptor/sharepoint/SharePointUserProfileAdaptorTest.java
index 5df3fc0..d559698 100644
--- a/test/com/google/enterprise/adaptor/sharepoint/SharePointUserProfileAdaptorTest.java
+++ b/test/com/google/enterprise/adaptor/sharepoint/SharePointUserProfileAdaptorTest.java
@@ -24,6 +24,7 @@
 import com.google.enterprise.adaptor.Config;
 import com.google.enterprise.adaptor.DocId;
 import com.google.enterprise.adaptor.GroupPrincipal;
+import com.google.enterprise.adaptor.InvalidConfigurationException;
 
 import com.google.enterprise.adaptor.sharepoint.SamlAuthenticationHandler.SamlHandshakeManager;
 import com.google.enterprise.adaptor.sharepoint.SharePointUserProfileAdaptor.UserProfileServiceClient;
@@ -48,7 +49,9 @@
 
 import org.junit.Assume;
 import org.junit.Before;
+import org.junit.Rule;
 import org.junit.Test;
+import org.junit.rules.ExpectedException;
 
 import org.w3c.dom.DOMImplementation;
 
@@ -82,6 +85,8 @@
   private final Charset charset = Charset.forName("UTF-8");
   private AuthenticationClientFactory authenticationFactory 
       = new MockAuthenticationClientFactoryForms();
+  @Rule
+  public ExpectedException thrown = ExpectedException.none();
 
   @Before
   public void setup() {
@@ -124,9 +129,22 @@
     SharePointUserProfileAdaptor adaptor = new SharePointUserProfileAdaptor();
     adaptor.destroy();
   }
+  
+  @Test
+  public void testAdaptorInitWithLive() throws Exception {
+    AccumulatingDocIdPusher pusher = new AccumulatingDocIdPusher();
+    adaptor = new SharePointUserProfileAdaptor(
+        new MockUserProfileServiceFactoryImpl(null), authenticationFactory);
+    config.overrideKey("sharepoint.useLiveAuthentication", "true");
+    config.overrideKey("sharepoint.usernamet", "");
+    config.overrideKey("sharepoint.password", "");
+    thrown.expect(InvalidConfigurationException.class);
+    adaptor.init(new MockAdaptorContext(config, pusher));
+    adaptor = null;
+  }
 
   @Test
-  public void testBlankCredentialsOnWindows() throws IOException {
+  public void testBlankCredentialsOnWindows() throws Exception {
     Assume.assumeTrue(System.getProperty("os.name").contains("Windows"));
     Config adaptorConfig = new Config();
     new SharePointUserProfileAdaptor().initConfig(adaptorConfig);
@@ -143,7 +161,7 @@
   }
 
   @Test
-  public void testGetDocIds() throws IOException, InterruptedException {
+  public void testGetDocIds() throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(null);
     ArrayOfPropertyData profile = new ArrayOfPropertyData();
@@ -178,8 +196,7 @@
   }
 
   @Test
-  public void testGetDocIdsNoProfiles()
-      throws IOException, InterruptedException {
+  public void testGetDocIdsNoProfiles() throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(null);
     adaptor = new SharePointUserProfileAdaptor(serviceFactory,
@@ -315,7 +332,7 @@
   }
 
   @Test
-  public void testGetDocContentNotFound() throws IOException {
+  public void testGetDocContentNotFound() throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(null);
     adaptor = new SharePointUserProfileAdaptor(serviceFactory,
@@ -333,7 +350,7 @@
   }
 
   @Test
-  public void testGetDocContentInvalidDocId() throws IOException {
+  public void testGetDocContentInvalidDocId() throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(null);
     ArrayOfPropertyData profile = new ArrayOfPropertyData();
@@ -356,8 +373,7 @@
   }
 
   @Test
-  public void testGetModifiedDocIdsWithEmptyChangeToken()
-      throws InterruptedException, IOException {
+  public void testGetModifiedDocIdsWithEmptyChangeToken() throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(
             "change token on mock repository");
@@ -374,8 +390,7 @@
   }
 
   @Test
-  public void testGetModifiedDocIdsWithNoChange()
-      throws InterruptedException, IOException {
+  public void testGetModifiedDocIdsWithNoChange() throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(
             "same current token");
@@ -392,8 +407,7 @@
   }
 
   @Test
-  public void testGetModifiedDocIdsDiffrentTokenNoChange()
-      throws InterruptedException, IOException {
+  public void testGetModifiedDocIdsDiffrentTokenNoChange() throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(
             "new token");
@@ -410,8 +424,7 @@
   }
 
   @Test
-  public void testGetModifiedDocIdsWithChange()
-      throws InterruptedException, IOException {
+  public void testGetModifiedDocIdsWithChange() throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(
             "new token");
@@ -448,8 +461,7 @@
   }
 
   @Test
-  public void testGetModifiedDocIdsInvalidToken()
-      throws InterruptedException, IOException {
+  public void testGetModifiedDocIdsInvalidToken()throws Exception {
     MockUserProfileServiceFactoryImpl serviceFactory =
         new MockUserProfileServiceFactoryImpl(
             "sp token");