Refactoring, style fixes
diff --git a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/AuthNResponseHandler.java b/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/AuthNResponseHandler.java
deleted file mode 100644
index 0ff4144..0000000
--- a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/AuthNResponseHandler.java
+++ /dev/null
@@ -1,100 +0,0 @@
-package com.google.enterprise.adaptor.examples.HelloWorldConnector;
-
-// Copyright 2014 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import com.google.enterprise.adaptor.AdaptorContext;
-import com.google.enterprise.adaptor.AuthnAuthority.Callback;
-import com.google.enterprise.adaptor.Session;
-
-import com.sun.net.httpserver.HttpExchange;
-import com.sun.net.httpserver.HttpHandler;
-
-import java.io.IOException;
-import java.io.UnsupportedEncodingException;
-import java.net.URLDecoder;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.logging.Logger;
-
-/**
- * 
- * This code does three things:
- * <ol>
- * <li>Retrieve the previously stored callback object.
- * <li>Retrieve the userid from the query parameters. 
- * Again, this is just to illustrate the point - it can be a userid from 
- * another service. 
- * It also skips the user authentication process and goes directly to step 
- * three.
- * <li>Constructs a AuthnIdentity object, 
- * and pass on to callback.userAuthenticated(). 
- * You can set both userid and groups on the identity object.
- */
-public class AuthNResponseHandler implements HttpHandler {
-  private static final Logger log =
-      Logger.getLogger(AuthNResponseHandler.class.getName());
-  private AdaptorContext context;
-  private Callback callback;
-  
-  public AuthNResponseHandler(AdaptorContext adaptorContext) {
-    context = adaptorContext; 
-  }
-
-  @Override
-  public void handle(HttpExchange ex) throws IOException {
-    log.info("handle");
-   
-    callback = getCallback(ex);
-    if (callback == null) {
-      return;
-    }
-    
-    Map<String, String> parameters = 
-        extractQueryParams(ex.getRequestURI().toString());
-    if (parameters.size() == 0 || null == parameters.get("userid")) {
-      log.warning("missing userid");
-      callback.userAuthenticated(ex, null);
-      return;
-    }
-    String userid = parameters.get("userid");
-    MyAuthnIdentity identity = new MyAuthnIdentity(userid);
-    callback.userAuthenticated(ex, identity);
-  }
-  
-  private Callback getCallback(HttpExchange ex) {
-    Session session = context.getUserSession(ex, false);
-    Callback callback = (Callback)session.getAttribute("callback");
-    if (callback == null) {
-      log.warning("Something is wrong, callback object is misssing");
-    }
-    return callback;
-  }
-
-  public Map<String, String> extractQueryParams(String request) {
-    String query = request.substring(request.lastIndexOf("?") + 1);
-    String params[] = query.split("&");
-    Map<String, String> paramMap = new HashMap<String, String>();
-    try {
-      for (int i = 0; i < params.length; ++i) {
-        String param[] = params[i].split("%2F=");
-        paramMap.put(URLDecoder.decode(param[0], "UTF-8"),
-            URLDecoder.decode(param[1], "UTF-8"));
-      }
-    } catch (UnsupportedEncodingException e) {
-      log.warning(e.getMessage());
-    }
-    return paramMap;
-  }
-}
diff --git a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/HelloWorldAuthenticator.java b/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/HelloWorldAuthenticator.java
deleted file mode 100644
index cb2c5ae..0000000
--- a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/HelloWorldAuthenticator.java
+++ /dev/null
@@ -1,90 +0,0 @@
-package com.google.enterprise.adaptor.examples.HelloWorldConnector;
-
-// Copyright 2014 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-//import com.google.enterprise.adaptor.*;
-import com.google.enterprise.adaptor.AdaptorContext;
-import com.google.enterprise.adaptor.AuthnAuthority;
-import com.google.enterprise.adaptor.AuthnIdentity;
-import com.google.enterprise.adaptor.AuthzAuthority;
-import com.google.enterprise.adaptor.AuthzStatus;
-import com.google.enterprise.adaptor.DocId;
-
-import com.sun.net.httpserver.Headers;
-import com.sun.net.httpserver.HttpExchange;
-
-import java.io.IOException;
-import java.io.OutputStream;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.logging.Logger;
-
-/**
- * Simple AuthN/AuthZ implementation
- */
-public class HelloWorldAuthenticator implements AuthnAuthority, AuthzAuthority {
-
-  private static final Logger log =
-      Logger.getLogger(HelloWorldAuthenticator.class.getName());
-
-  AdaptorContext context;
-
-  public HelloWorldAuthenticator(AdaptorContext adaptorContext) {
-    if (adaptorContext == null) {
-      throw new NullPointerException();
-    } else {
-      context = adaptorContext;
-    }
-  }
-
-  @Override
-  public void authenticateUser(HttpExchange exchange, Callback callback)
-      throws IOException {
-
-    log.info("redirect");
-    context.getUserSession(exchange, true).setAttribute("callback",
-        callback);
-
-    Headers responseHeaders = exchange.getResponseHeaders();
-    responseHeaders.set("Content-Type", "text/html");
-    exchange.sendResponseHeaders(200, 0);
-    OutputStream os = exchange.getResponseBody();
-    String str = "<html><body><form action=\"/google-response\" method=Get>"
-        + "<input type=text name=userid/>"
-        + "<input type=password name=password/>"
-        + "<input type=submit value=submit></form></body></html>";
-    os.write(str.getBytes());
-    os.flush();
-    os.close();
-    exchange.close();
-  }
-
-  @Override
-  public Map<DocId, AuthzStatus> isUserAuthorized(AuthnIdentity userIdentity,
-      Collection<DocId> ids) throws IOException {
-
-    HashMap<DocId, AuthzStatus> authorizedDocs =
-        new HashMap<DocId, AuthzStatus>();
-
-    for (Iterator<DocId> iterator = ids.iterator(); iterator.hasNext();) {
-      DocId docId = iterator.next();
-      // if authorized
-      authorizedDocs.put(docId, AuthzStatus.PERMIT);
-    }
-    return authorizedDocs;
-  }
-}
diff --git a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/MyAuthnIdentity.java b/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/MyAuthnIdentity.java
deleted file mode 100644
index 1243488..0000000
--- a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/MyAuthnIdentity.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package com.google.enterprise.adaptor.examples.HelloWorldConnector;
-
-// Copyright 2014 Google Inc. All Rights Reserved.
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-//      http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-import com.google.enterprise.adaptor.AuthnIdentity;
-import com.google.enterprise.adaptor.GroupPrincipal;
-import com.google.enterprise.adaptor.UserPrincipal;
-
-import java.util.Collection;
-import java.util.Set;
-import java.util.TreeSet;
-
-/**
- * Stub of AuthnIdentity
- */
-public class MyAuthnIdentity implements AuthnIdentity {
-
-  private UserPrincipal user;
-  private Set<GroupPrincipal> groups;
-
-  // Constructor  with user only
-  public MyAuthnIdentity(String uid) {
-    this.user = new UserPrincipal(uid);
-  }
-
-  //Constructor with user & single group
-  public MyAuthnIdentity(String uid, String gid) {
-    this.user = new UserPrincipal(uid);
-    this.groups = new TreeSet<GroupPrincipal>();
-    this.groups.add(new GroupPrincipal(gid));
-  }
-
-  // Constructor with user & groups
-  public MyAuthnIdentity(String uid, Collection<String> gids) {
-    this.user = new UserPrincipal(uid);
-    this.groups = new TreeSet<GroupPrincipal>();
-    for (String n : gids) {
-      this.groups.add(new GroupPrincipal(n));
-    }
-  }
-
-  @Override
-  public UserPrincipal getUser() {
-    return user;
-  }
-
-  @Override
-  public String getPassword() {
-    return null;
-  }
-
-  @Override
-  public Set<GroupPrincipal> getGroups() {
-    return groups;
-  }
-}
diff --git a/src/com/google/enterprise/adaptor/examples/helloworldconnector/HelloWorldAuthenticator.java b/src/com/google/enterprise/adaptor/examples/helloworldconnector/HelloWorldAuthenticator.java
new file mode 100644
index 0000000..985b112
--- /dev/null
+++ b/src/com/google/enterprise/adaptor/examples/helloworldconnector/HelloWorldAuthenticator.java
@@ -0,0 +1,177 @@
+package com.google.enterprise.adaptor.examples.helloworldconnector;
+
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+//import com.google.enterprise.adaptor.*;
+import com.google.enterprise.adaptor.AdaptorContext;
+import com.google.enterprise.adaptor.AuthnAuthority;
+import com.google.enterprise.adaptor.AuthnIdentity;
+import com.google.enterprise.adaptor.AuthzAuthority;
+import com.google.enterprise.adaptor.AuthzStatus;
+import com.google.enterprise.adaptor.DocId;
+import com.google.enterprise.adaptor.Session;
+
+import com.sun.net.httpserver.Headers;
+import com.sun.net.httpserver.HttpExchange;
+import com.sun.net.httpserver.HttpHandler;
+
+import java.io.IOException;
+import java.io.OutputStream;
+import java.io.UnsupportedEncodingException;
+import java.net.URLDecoder;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.logging.Logger;
+
+/**
+ * Simple AuthN/AuthZ implementation
+ */
+class HelloWorldAuthenticator implements AuthnAuthority, AuthzAuthority, HttpHandler {
+
+  private static final Logger log =
+      Logger.getLogger(HelloWorldAuthenticator.class.getName());
+
+  private AdaptorContext context;
+  private Callback callback;
+
+
+  public HelloWorldAuthenticator(AdaptorContext adaptorContext) {
+    if (adaptorContext == null) {
+      throw new NullPointerException();
+    }
+    context = adaptorContext;
+  }
+
+  @Override
+  public void authenticateUser(HttpExchange exchange, Callback callback)
+      throws IOException {
+
+    log.entering("HelloWorldAuthenticator", "redirect");
+    context.getUserSession(exchange, true).setAttribute("callback",
+        callback);
+
+    Headers responseHeaders = exchange.getResponseHeaders();
+    responseHeaders.set("Content-Type", "text/html");
+    exchange.sendResponseHeaders(200, 0);
+    OutputStream os = exchange.getResponseBody();
+    String str = "<html><body><form action=\"/google-response\" method=Get>"
+        + "<input type=text name=userid/>"
+        + "<input type=password name=password/>"
+        + "<input type=submit value=submit></form></body></html>";
+    os.write(str.getBytes());
+    os.flush();
+    os.close();
+    exchange.close();
+  }
+
+  @Override
+  public Map<DocId, AuthzStatus> isUserAuthorized(AuthnIdentity userIdentity,
+      Collection<DocId> ids) throws IOException {
+
+    HashMap<DocId, AuthzStatus> authorizedDocs =
+        new HashMap<DocId, AuthzStatus>();
+
+    for (Iterator<DocId> iterator = ids.iterator(); iterator.hasNext();) {
+      DocId docId = iterator.next();
+      // if authorized
+      authorizedDocs.put(docId, AuthzStatus.PERMIT);
+    }
+    return authorizedDocs;
+  }
+
+  /**
+   * Handle the form submit from /samlip<br>
+   * If all goes well, this should result in an Authenticated user for the
+   * session
+   */
+  @Override
+  public void handle(HttpExchange ex) throws IOException {
+    log.entering("AuthNResponseHandler", "handle");
+
+    callback = getCallback(ex);
+    if (callback == null) {
+      return;
+    }
+
+    Map<String, String> parameters =
+        extractQueryParams(ex.getRequestURI().toString());
+    if (parameters.size() == 0 || null == parameters.get("userid")) {
+      log.warning("missing userid");
+      callback.userAuthenticated(ex, null);
+      return;
+    }
+    String userid = parameters.get("userid");
+    SimpleAuthnIdentity identity = new SimpleAuthnIdentity(userid);
+    callback.userAuthenticated(ex, identity);
+  }
+
+  // Return a 200 with simple response in body
+  private void sendResponseMessage(String message, HttpExchange ex)
+      throws IOException {
+    OutputStream os = ex.getResponseBody();
+    ex.sendResponseHeaders(200, 0);
+    os.write(message.getBytes());
+    os.flush();
+    os.close();
+    ex.close();
+  }
+
+  // Return the Callback method,
+  // or print error if the handler wasn't called correctly
+  private Callback getCallback(HttpExchange ex) throws IOException {
+    Session session = context.getUserSession(ex, false);
+    if (session == null) {
+      log.warning("No Session");
+      sendResponseMessage("No Session", ex);
+      return null;
+    }
+    Callback callback = (Callback) session.getAttribute("callback");
+    if (callback == null) {
+      log.warning("Something is wrong, callback object is misssing");
+      sendResponseMessage("No Callback Specified", ex);
+    }
+    return callback;
+  }
+
+  // Parse user/password/group params
+  private Map<String, String> extractQueryParams(String request) {
+    Map<String, String> paramMap = new HashMap<String, String>();
+    int queryIndex = request.lastIndexOf("?");
+
+    if (queryIndex == -1) {
+      return paramMap;
+    }
+    String query = request.substring(queryIndex + 1);
+    String params[] = query.split("&", 4);
+    if (query.equals("")) {
+      return paramMap;
+    }
+    try {
+      for (int i = 0; i < params.length; ++i) {
+        String param[] = params[i].split("%2F=", 2);
+        paramMap.put(URLDecoder.decode(param[0], "UTF-8"),
+            URLDecoder.decode(param[1], "UTF-8"));
+      }
+    } catch (UnsupportedEncodingException e) {
+      log.warning("Request parameters may not have been properly encoded: "
+          + e.getMessage());
+    } catch (ArrayIndexOutOfBoundsException e) {
+      log.warning("Wrong number of parameters specified: " + e.getMessage());
+    }
+    return paramMap;
+  }
+}
diff --git a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/HelloWorldConnector.java b/src/com/google/enterprise/adaptor/examples/helloworldconnector/HelloWorldConnector.java
similarity index 85%
rename from src/com/google/enterprise/adaptor/examples/HelloWorldConnector/HelloWorldConnector.java
rename to src/com/google/enterprise/adaptor/examples/helloworldconnector/HelloWorldConnector.java
index 186d804..90cae45 100644
--- a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/HelloWorldConnector.java
+++ b/src/com/google/enterprise/adaptor/examples/helloworldconnector/HelloWorldConnector.java
@@ -1,4 +1,4 @@
-package com.google.enterprise.adaptor.examples.HelloWorldConnector;
+package com.google.enterprise.adaptor.examples.helloworldconnector;
 
 // Copyright 2014 Google Inc. All Rights Reserved.
 //
@@ -38,7 +38,7 @@
 import java.util.logging.Logger;
 
 /**
- * Demonstrates what code is necessary for putting public content onto a GSA.
+ * Demonstrates what code is necessary for putting content onto a GSA.
  * The key operations are:
  * <ol>
  * <li>providing document ids either by Lister or Graph Traversal
@@ -59,19 +59,19 @@
         new HelloWorldAuthenticator(context);
     context.setAuthnAuthority(authenticator);
     context.setAuthzAuthority(authenticator);
-    context.createHttpContext("/google-response",
-        new AuthNResponseHandler(context));
+    context.createHttpContext("/google-response",authenticator);
   }
 
   /**
    * This example shows how to use both the Lister & Graph Traversal.
    * The root document ("") is a virtual doc which will contain a list of
    * links to other docs when returned by the Retriever.
-   * If you aren't using Graph Traversal, all docid's would be pushed in
-   * like 1001 and 1002
+   * If you aren't using Graph Traversal, all docids would be pushed in
+   * here, like 1001 and 1002 are.
    */
   @Override
   public void getDocIds(DocIdPusher pusher) throws InterruptedException {
+    log.entering("HelloWorldConnector", "getDocIds");
     ArrayList<DocId> mockDocIds = new ArrayList<DocId>();
     // push docids
     mockDocIds.add(new DocId(""));
@@ -97,10 +97,11 @@
   /** Gives the bytes of a document referenced with id. */
   @Override
   public void getDocContent(Request req, Response resp) throws IOException {
+    log.entering("HelloWorldConnector", "getDocContent");
     DocId id = req.getDocId();
     log.info("DocId '" + id.getUniqueId() + "'");
 
-    // Hard-coded list of our doc id's
+    // Hard-coded list of our doc ids
     if ("".equals(id.getUniqueId())) {
       // this is a the root folder, write some URLs
       Writer writer = new OutputStreamWriter(resp.getOutputStream());
@@ -126,16 +127,20 @@
       writer.close();
     } else if ("1001".equals(id.getUniqueId())) {
       // Example with If-Modified-Since
-      Date lastModifiedDate = req.getLastAccessTime();
-      if (req.hasChangedSinceLastAccess(lastModifiedDate) ||
-          lastModifiedDate == null) {
-        log.info("First Access or no LastModified");
+      // Set lastModifiedDate to 10 minutes ago
+      Date lastModifiedDate = new Date(System.currentTimeMillis() - 600000);
+      if (req.hasChangedSinceLastAccess(lastModifiedDate)) {
+        if (req.getLastAccessTime() == null) {
+          log.info("Requested docid 1001 with No If-Modified-Since");
+        } else {
+          log.info("Requested docid 1001 with If-Modified-Since < 10 minutes");
+        }
         resp.setLastModified(new Date());
         Writer writer = new OutputStreamWriter(resp.getOutputStream());
         writer.write("Menu 1001 says latte");
         writer.close();
       } else {
-        log.info("Not Modified");
+        log.info("Docid 1001 Not Modified");
         resp.respondNotModified();
       }
     } else if ("1002".equals(id.getUniqueId())) {
@@ -149,24 +154,24 @@
         Writer writer = new OutputStreamWriter(resp.getOutputStream());
         writer.write("Menu 1003 says machiato");
         writer.close();
-        provideBodyOfDoc1003 = !provideBodyOfDoc1003;
       } else {
         resp.respondNotFound();
-        provideBodyOfDoc1003 = !provideBodyOfDoc1003;
       }
+      provideBodyOfDoc1003 = !provideBodyOfDoc1003;
     } else if ("1004".equals(id.getUniqueId())) {
       // doc with metdata & different display URL
-        resp.addMetadata("flavor", "vanilla");
-        resp.addMetadata("flavor", "hazel nuts");
-        resp.addMetadata("taste", "strawberry");
-        try {
-          resp.setDisplayUrl(new URI("http://fake.com/a"));
-        } catch (URISyntaxException e) {
-          log.info(e.getMessage());
-        }
-        Writer writer = new OutputStreamWriter(resp.getOutputStream());
-        writer.write("Menu 1004 says espresso");
-        writer.close();
+      resp.addMetadata("flavor", "vanilla");
+      resp.addMetadata("flavor", "hazel nuts");
+      resp.addMetadata("taste", "strawberry");
+
+      try {
+        resp.setDisplayUrl(new URI("http://fake.com/a"));
+      } catch (URISyntaxException e) {
+        log.info(e.getMessage());
+      }
+      Writer writer = new OutputStreamWriter(resp.getOutputStream());
+      writer.write("Menu 1004 says espresso");
+      writer.close();
     } else if ("1005".equals(id.getUniqueId())) {
       // doc with ACLs
       ArrayList<Principal> permits = new ArrayList<Principal>();
@@ -233,10 +238,10 @@
       denies.add(new GroupPrincipal("group5", "Default"));
 
       resp.setAcl(new Acl.Builder()
-      .setEverythingCaseInsensitive()
-      .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
-      .setInheritFrom(new DocId("1007"), "Whatever")
-      .setDenies(denies).build());
+          .setEverythingCaseInsensitive()
+          .setInheritanceType(Acl.InheritanceType.PARENT_OVERRIDES)
+          .setInheritFrom(new DocId("1007"), "Whatever")
+          .setDenies(denies).build());
       Writer writer = new OutputStreamWriter(resp.getOutputStream());
       writer.write("Menu 1008 says coffee");
       writer.close();
diff --git a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/MetadataAddition.java b/src/com/google/enterprise/adaptor/examples/helloworldconnector/MetadataAddition.java
similarity index 64%
rename from src/com/google/enterprise/adaptor/examples/HelloWorldConnector/MetadataAddition.java
rename to src/com/google/enterprise/adaptor/examples/helloworldconnector/MetadataAddition.java
index 8541998..e9980ac 100644
--- a/src/com/google/enterprise/adaptor/examples/HelloWorldConnector/MetadataAddition.java
+++ b/src/com/google/enterprise/adaptor/examples/helloworldconnector/MetadataAddition.java
@@ -1,4 +1,4 @@
-package com.google.enterprise.adaptor.examples.HelloWorldConnector;
+package com.google.enterprise.adaptor.examples.helloworldconnector;
 
 // Copyright 2014 Google Inc. All Rights Reserved.
 //
@@ -25,9 +25,22 @@
 import java.util.logging.Logger;
 
 /**
- * Example transform which will add values to metadata "taste" if the document
- * has this metadata.
+ * Example transform which will add values to metadata key "taste" if the 
+ * document already has existing metdata key "taste"
+ * <p>
+ * A simple transformation can be added to the adaptor-config.properties file.
+ * This example combines "Mango" and "peach" with any existing "taste"
+ * metadata.  If the document does not have a meta taste key, no values will
+ * be added.
+ * <p>
+ * <code>
+ * transform.pipeline=step1<br>
+ * transform.pipeline.step1.taste=Mango,peach<br>
+ * transform.pipeline.step1.factoryMethod=com.google.enterprise.adaptor.examples.HelloWorldConnector.MetadataAddition.load<br>
+ * </code>
+ * <p>
  */
+
 public class MetadataAddition implements DocumentTransform {
   private static final Logger log = Logger.getLogger(MetadataAddition.class
       .getName());
@@ -38,15 +51,25 @@
     if (null == values) {
       throw new NullPointerException();
     }
-    String valueArray[] = values.split(",");
+    String valueArray[] = values.split(",", 0);
     valuesToAdd = new HashSet<String>(Arrays.asList(valueArray));
-}
+  }
 
-  /** Makes transform from config file with "taste". */
+  /**
+   * Called as <code>transfordm.pipeline.<stepX>.factoryMethod for this
+   * transformation pipline as specified in adaptor-config.properties.
+   * <p>
+   * This method simply returns a new object with the additional
+   * metadata as specified as values for step1.taste
+   */
   public static MetadataAddition load(Map<String, String> cfg) {
     return new MetadataAddition(cfg.get(META_TASTE));
   }
 
+  /**
+   * Here we check to see if the current doc contains a "taste" key
+   * and if so, add the additional values from the config file
+   */
   @Override
   public void transform(Metadata metadata, Map<String, String> params) {
     Set<String> values = metadata.getAllValues(META_TASTE);
@@ -65,9 +88,4 @@
     combined.addAll(s2);
     return combined;
   }
-
-  @Override
-  public String toString() {
-    return "MetadataAddition(meta_taste=" + META_TASTE + ")";
-  }
 }
diff --git a/src/com/google/enterprise/adaptor/examples/helloworldconnector/SimpleAuthnIdentity.java b/src/com/google/enterprise/adaptor/examples/helloworldconnector/SimpleAuthnIdentity.java
new file mode 100644
index 0000000..932e59b
--- /dev/null
+++ b/src/com/google/enterprise/adaptor/examples/helloworldconnector/SimpleAuthnIdentity.java
@@ -0,0 +1,85 @@
+package com.google.enterprise.adaptor.examples.helloworldconnector;
+
+// Copyright 2014 Google Inc. All Rights Reserved.
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+import com.google.enterprise.adaptor.AuthnIdentity;
+import com.google.enterprise.adaptor.GroupPrincipal;
+import com.google.enterprise.adaptor.UserPrincipal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Set;
+import java.util.TreeSet;
+
+/**
+ * Simple implementation of AuthnIdentity
+ */
+class SimpleAuthnIdentity implements AuthnIdentity {
+
+  private UserPrincipal user;
+  private Set<GroupPrincipal> groups;
+
+  public SimpleAuthnIdentity(String uid) throws NullPointerException {
+    if (uid == null) {
+      throw(new NullPointerException("Null user not allowed"));
+    }
+    this.user = new UserPrincipal(uid);
+  }
+
+  //Constructor with user & single group
+  public SimpleAuthnIdentity(String uid, String gid)
+      throws NullPointerException {
+    this(uid);
+    this.groups = new TreeSet<GroupPrincipal>();
+    if (gid != null && !"".equals(gid)) {
+      this.groups.add(new GroupPrincipal(gid));
+    }
+    this.groups =
+        (Set<GroupPrincipal>) Collections.unmodifiableCollection(this.groups);
+  }
+
+  // Constructor with user & groups
+  public SimpleAuthnIdentity(String uid, Collection<String> gids)
+      throws NullPointerException {
+    this(uid);
+    this.groups = new TreeSet<GroupPrincipal>();
+    for (String n : gids) {
+      if (n != null && !"".equals(n)) {
+        this.groups.add(new GroupPrincipal(n));
+      }
+    }
+    this.groups =
+        (Set<GroupPrincipal>) Collections.unmodifiableCollection(this.groups);
+  }
+
+  @Override
+  public UserPrincipal getUser() {
+    return user;
+  }
+
+  /**
+   * Returns null in this example since we don't do anything with the
+   * password, but getPassword() must be implemented for AuthnIdentity
+   */
+  @Override
+  public String getPassword() {
+    return null;
+  }
+
+  @Override
+  public Set<GroupPrincipal> getGroups() {
+    return groups;
+  }
+}