Detect non-AD server at initialization time

and throw an InvalidConfigurationException upon detection.
This fixes b/7061071.
diff --git a/src/com/google/enterprise/adaptor/ad/AdServer.java b/src/com/google/enterprise/adaptor/ad/AdServer.java
index 5d5aaa1..de93e5b 100644
--- a/src/com/google/enterprise/adaptor/ad/AdServer.java
+++ b/src/com/google/enterprise/adaptor/ad/AdServer.java
@@ -16,6 +16,7 @@
 
 import com.google.common.annotations.VisibleForTesting;
 
+import com.google.enterprise.adaptor.InvalidConfigurationException;
 import com.google.enterprise.adaptor.StartupException;
 
 import java.io.IOException;
@@ -53,7 +54,8 @@
   // properties necessary for connection and reconnection
   private Method connectMethod;
   private final String hostName;
-  private int port;
+  @VisibleForTesting
+  int port;
   private String principal;
   private String password;
 
@@ -213,7 +215,7 @@
         "configurationNamingContext").get(0).toString();
   }
 
-  public void initialize() {
+  public void initialize() throws InvalidConfigurationException {
     try {
       ensureConnectionIsCurrent();
       sid = AdEntity.getTextSid((byte[]) get(
@@ -223,6 +225,15 @@
           "invocationID;binary", dsServiceName));
     } catch (NamingException e) {
       throw new RuntimeException(e);
+    } catch (NullPointerException npe) {
+      String reason = "non-AD LDAP server detected.  AD Adaptor must be run "
+          + "against an Active Directory domain controller.";
+      if ((port == 3268) || (port == 3269)) {
+        reason = "AD Adaptor must be run against the Domain Controller "
+            + "(typically, port 389 for HTTP or port 636 for SSL), not the "
+            + "Global Catalog.";
+      }
+      throw new InvalidConfigurationException(reason);
     }
 
     LOGGER.info("Successfully created an Initial LDAP context");
diff --git a/test/com/google/enterprise/adaptor/ad/AdServerTest.java b/test/com/google/enterprise/adaptor/ad/AdServerTest.java
index 349d0fc..66973d1 100644
--- a/test/com/google/enterprise/adaptor/ad/AdServerTest.java
+++ b/test/com/google/enterprise/adaptor/ad/AdServerTest.java
@@ -20,6 +20,9 @@
 import org.junit.Test;
 import org.junit.rules.ExpectedException;
 
+import com.google.enterprise.adaptor.InvalidConfigurationException;
+import com.google.enterprise.adaptor.StartupException;
+
 import javax.naming.*;
 import javax.naming.directory.*;
 import javax.naming.ldap.*;
@@ -131,6 +134,66 @@
   }
 
   @Test
+  public void testNonADInitialize() throws Exception {
+    MockLdapContext ldapContext = new MockLdapContext() {
+      @Override
+      public NamingEnumeration<SearchResult> search(String base, String filter,
+        SearchControls searchControls) throws NamingException {
+          throw new NullPointerException("non-AD server");
+      }
+    };
+    AdServer adServer = new AdServer("localhost", ldapContext);
+    try {
+      adServer.initialize();
+      fail("Did not catch expected InvalidConfigurationException.");
+    } catch (InvalidConfigurationException ice) {
+      assertTrue(ice.toString().contains("non-AD LDAP"));
+    }
+  }
+
+  @Test
+  // this test uses port 3268 -- the Global Catalog port -- to get a
+  // specific exception thrown.
+  public void testGlobalCatalogInitialize() throws Exception {
+    final MockLdapContext ldapContext = new MockLdapContext() {
+      @Override
+      public NamingEnumeration<SearchResult> search(String base, String filter,
+        SearchControls searchControls) throws NamingException {
+          throw new NullPointerException("non-AD server");
+      }
+    };
+    AdServer adServer = new AdServer("localhost", ldapContext);
+    try {
+      adServer.port = 3268;
+      adServer.initialize();
+      fail("Did not catch expected InvalidConfigurationException.");
+    } catch (InvalidConfigurationException ice) {
+      assertTrue(ice.toString().contains("not the Global Catalog"));
+    }
+  }
+
+  @Test
+  // this test uses port 3269 -- the SSL Global Catalog port -- to get the
+  // same specific exception thrown.
+  public void testSSLGlobalCatalogInitialize() throws Exception {
+    final MockLdapContext ldapContext = new MockLdapContext() {
+      @Override
+      public NamingEnumeration<SearchResult> search(String base, String filter,
+        SearchControls searchControls) throws NamingException {
+          throw new NullPointerException("non-AD server");
+      }
+    };
+    AdServer adServer = new AdServer("localhost", ldapContext);
+    try {
+      adServer.port = 3269;
+      adServer.initialize();
+      fail("Did not catch expected InvalidConfigurationException.");
+    } catch (InvalidConfigurationException ice) {
+      assertTrue(ice.toString().contains("not the Global Catalog"));
+    }
+  }
+
+  @Test
   /*
    * This tests a code path that the author doesn't think can actually happen
    *