diff --git a/projects/cadcAccessControl-Server/build.xml b/projects/cadcAccessControl-Server/build.xml
index 8382321e600dc7e0cd7b5570e2d7b773a7e075b5..214ba7c14fab9da7a04dea286bcefc97db9a6621 100644
--- a/projects/cadcAccessControl-Server/build.xml
+++ b/projects/cadcAccessControl-Server/build.xml
@@ -69,7 +69,7 @@
 
 
 <!DOCTYPE project>
-<project default="build" basedir=".">
+<project name="cadcAccessControl-Server" default="build" basedir=".">
     <property environment="env"/>
     <property file="local.build.properties" />
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java
index 15689805571ce6c7e8ca441ecfcc087548a7301a..10f4012b1c3ab9a146af9793aa64d3c846a0e67f 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/CreateGroupAction.java
@@ -96,8 +96,8 @@ public class CreateGroupAction extends GroupsAction
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = GroupReader.read(this.inputStream);
         Group newGroup = groupPersistence.addGroup(group);
-        this.response.setContentType("application/xml");
-        GroupWriter.write(newGroup, this.response.getOutputStream());
+        setContentType("application/xml");
+        GroupWriter.write(newGroup, getOutputStream());
 
         List<String> addedMembers = null;
         if ((newGroup.getUserMembers().size() > 0) || (newGroup.getGroupMembers().size() > 0))
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java
index 9854b25d4e785ac066a2d6de97ed2919ea634352..d3f758f257aa1d2ed3d85ca741c01cdaf3d8eebc 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupAction.java
@@ -87,8 +87,8 @@ public class GetGroupAction extends GroupsAction
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = groupPersistence.getGroup(this.groupName);
-        this.response.setContentType("application/xml");
-        GroupWriter.write(group, this.response.getOutputStream());
+        setContentType("application/xml");
+        GroupWriter.write(group, getOutputStream());
         return null;
     }
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupNamesAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupNamesAction.java
index 04f2b12e968b4ee2566101cb35a1f124bd43c79a..0ca3c2dde196b9daf914d5b18f77753e4db124d7 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupNamesAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GetGroupNamesAction.java
@@ -67,6 +67,8 @@
  ************************************************************************
  */package ca.nrc.cadc.ac.server.web;
 
+import java.io.OutputStreamWriter;
+import java.io.Writer;
 import java.util.Collection;
 
 import ca.nrc.cadc.ac.server.GroupPersistence;
@@ -88,21 +90,16 @@ public class GetGroupNamesAction extends GroupsAction
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Collection<String> groups = groupPersistence.getGroupNames();
-        getHttpServletResponse().setContentType("text/csv");
-        
-        CsvWriter writer = new CsvWriter(getHttpServletResponse().getWriter(), ',');
-        
-        for (String group : groups)
+        setContentType("text/csv");
+
+        final Writer writer = new OutputStreamWriter(getOutputStream());
+        final CsvWriter csvWriter = new CsvWriter(writer, ',');
+
+        for (final String group : groups)
         {
-            writer.write(group);
+            csvWriter.write(group);
         }
-        writer.endRecord();
+        csvWriter.endRecord();
         return null;
     }
-
-    protected HttpServletResponse getHttpServletResponse()
-    {
-        return this.response;
-    }
-
 }
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsAction.java
index 02f64926a9982e7ae88b89bed6c054087cb473a9..95c16dd61f701e570a0c95b67424926b3ab6e068 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsAction.java
@@ -69,6 +69,7 @@
 package ca.nrc.cadc.ac.server.web;
 
 import java.io.IOException;
+import java.io.OutputStream;
 import java.security.AccessControlException;
 import java.security.Principal;
 import java.security.PrivilegedActionException;
@@ -76,6 +77,7 @@ import java.security.PrivilegedExceptionAction;
 import java.util.List;
 
 import javax.security.auth.Subject;
+import javax.servlet.ServletResponse;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.log4j.Logger;
@@ -89,45 +91,54 @@ import ca.nrc.cadc.ac.server.GroupPersistence;
 import ca.nrc.cadc.ac.server.PluginFactory;
 import ca.nrc.cadc.ac.server.UserPersistence;
 import ca.nrc.cadc.net.TransientException;
+import ca.nrc.cadc.uws.server.SyncOutput;
+
 
 public abstract class GroupsAction
     implements PrivilegedExceptionAction<Object>
 {
     private static final Logger log = Logger.getLogger(GroupsAction.class);
     protected GroupLogInfo logInfo;
-    protected HttpServletResponse response;
+    private SyncOutput syncOutput;
 
     GroupsAction(GroupLogInfo logInfo)
     {
         this.logInfo = logInfo;
     }
 
-    public void doAction(Subject subject, HttpServletResponse response)
+    public void doAction(Subject subject, final HttpServletResponse response)
         throws IOException
     {
+        syncOutput = new SyncOutput()
+        {
+            @Override
+            public void setResponseCode(int code)
+            {
+                response.setStatus(code);
+            }
+
+            @Override
+            public void setHeader(String key, String value)
+            {
+                response.setHeader(key, value);
+            }
+
+            @Override
+            public OutputStream getOutputStream() throws IOException
+            {
+                return response.getOutputStream();
+            }
+        };
+
         try
         {
-            try
+            if (subject == null)
             {
-                this.response = response;
-
-                if (subject == null)
-                {
-                    run();
-                }
-                else
-                {
-                    Subject.doAs(subject, this);
-                }
+                run();
             }
-            catch (PrivilegedActionException e)
+            else
             {
-                Throwable cause = e.getCause();
-                if (cause != null)
-                {
-                    throw cause;
-                }
-                throw e;
+                runPrivileged(subject);
             }
         }
         catch (AccessControlException e)
@@ -203,27 +214,58 @@ public abstract class GroupsAction
         }
     }
 
+    private void runPrivileged(final Subject subject) throws Throwable
+    {
+        try
+        {
+            Subject.doAs(subject, this);
+        }
+        catch (PrivilegedActionException e)
+        {
+            final Throwable cause = e.getCause();
+            if (cause != null)
+            {
+                throw cause;
+            }
+            throw e;
+        }
+    }
+
+    protected final void setStatusCode(final int statusCode)
+    {
+        syncOutput.setResponseCode(statusCode);
+    }
+
+    protected final OutputStream getOutputStream() throws IOException
+    {
+        return syncOutput.getOutputStream();
+    }
+
+    protected final void setContentType(final String contentType)
+    {
+        syncOutput.setHeader("Content-Type", contentType);
+    }
+
+    protected final void setRedirectLocation(final String location)
+    {
+        syncOutput.setHeader("Location", location);
+    }
+
     private void sendError(int responseCode)
         throws IOException
     {
         sendError(responseCode, null);
     }
 
-    private void sendError(int responseCode, String message)
+    private void sendError(final int code, String message)
         throws IOException
     {
-        if (!this.response.isCommitted())
-        {
-            this.response.setContentType("text/plain");
-            if (message != null)
-            {
-                this.response.getWriter().write(message);
-            }
-            this.response.setStatus(responseCode);
-        }
-        else
+        setContentType("text/plain");
+        setStatusCode(code);
+
+        if (message != null)
         {
-            log.warn("Could not send error " + responseCode + " (" + message + ") because the response is already committed.");
+            getOutputStream().write(message.getBytes());
         }
     }
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java
index d776f56ce827c6ef8fb86e5ec4f862a226104179..0f978accbf65d3ce48a76c5e2f03553dca81f14e 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/GroupsActionFactory.java
@@ -69,6 +69,7 @@
 package ca.nrc.cadc.ac.server.web;
 
 import java.io.IOException;
+import java.net.URL;
 import java.net.URLDecoder;
 
 import javax.servlet.http.HttpServletRequest;
@@ -92,16 +93,19 @@ public class GroupsActionFactory
 
         if (path == null)
         {
-            path = new String();
+            path = "";
         }
+
         if (path.startsWith("/"))
         {
             path = path.substring(1);
         }
+
         if (path.endsWith("/"))
         {
             path = path.substring(0, path.length() - 1);
         }
+
         String[] segments = new String[0];
         if (StringUtil.hasText(path))
         {
@@ -133,7 +137,16 @@ public class GroupsActionFactory
             }
             else if (method.equals("POST"))
             {
-                action = new ModifyGroupAction(logInfo, groupName, request.getRequestURI(), request.getInputStream());
+                final URL requestURL =
+                        new URL(request.getRequestURL().toString());
+                final String redirectURI = requestURL.getProtocol() + "://"
+                                           + requestURL.getHost() + ":"
+                                           + requestURL.getPort()
+                                           + request.getContextPath()
+                                           + request.getServletPath()
+                                           + "/" + path;
+                action = new ModifyGroupAction(logInfo, groupName, redirectURI,
+                                               request.getInputStream());
             }
         }
         else if (segments.length == 3)
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java
index af5b564249e6c4ba4f268a8364c28e3527785923..1375832db2b535f1a219e66083c1e3447051e91e 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/ModifyGroupAction.java
@@ -70,6 +70,7 @@ package ca.nrc.cadc.ac.server.web;
 
 import ca.nrc.cadc.ac.Group;
 import ca.nrc.cadc.ac.GroupReader;
+import ca.nrc.cadc.ac.GroupWriter;
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.server.GroupPersistence;
 import java.io.InputStream;
@@ -85,7 +86,8 @@ public class ModifyGroupAction extends GroupsAction
     private final String request;
     private final InputStream inputStream;
 
-    ModifyGroupAction(GroupLogInfo logInfo, String groupName, String request, InputStream inputStream)
+    ModifyGroupAction(GroupLogInfo logInfo, String groupName,
+                      final String request, InputStream inputStream)
     {
         super(logInfo);
         this.groupName = groupName;
@@ -99,8 +101,8 @@ public class ModifyGroupAction extends GroupsAction
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = GroupReader.read(this.inputStream);
         Group oldGroup = groupPersistence.getGroup(this.groupName);
-        Group modifiedGroup = groupPersistence.modifyGroup(group);
-        
+        groupPersistence.modifyGroup(group);
+
         List<String> addedMembers = new ArrayList<String>();
         for (User member : group.getUserMembers())
         {
@@ -134,8 +136,10 @@ public class ModifyGroupAction extends GroupsAction
             deletedMembers = null;
         }
         logGroupInfo(group.getID(), deletedMembers, addedMembers);
-        
-        this.response.sendRedirect(request);
+
+        setStatusCode(303);
+        setRedirectLocation(request);
+
         return null;
     }
 
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java
index afbf97f64dd5d701898d16acf821a572b58d9e4a..cacb9a00c2efacc78ee92aa9d23ed35449fe38d9 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/AC.java
@@ -85,7 +85,7 @@ public class AC
     // Denotes a group readable by public
     public static final String PROPERTY_PUBLIC = "ivo://ivoa.net/gms#public";
     
-    public static final String GMS_SERVICE_URI = "ivo://cadc.nrc.ca/gms";
+    public static final String GMS_SERVICE_URI = "ivo://cadc.nrc.ca/canfargms";
     
     // Group URI attribute once the group name is appended
     public static final String GROUP_URI = "ivo://cadc.nrc.ca/gms#";
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
index 8061f48773eeb3913a4b8020aba06af37e703fed..70ef1830f10432669f5b0bbd27be53286fa8d2b4 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/GroupReader.java
@@ -119,7 +119,7 @@ public class GroupReader
      * @throws java.net.URISyntaxException
      */
     public static Group read(InputStream in)
-        throws ReaderException, IOException, URISyntaxException
+        throws ReaderException, IOException
     {
         if (in == null)
         {
@@ -147,7 +147,7 @@ public class GroupReader
      * @throws java.net.URISyntaxException
      */
     public static Group read(Reader reader)
-        throws ReaderException, IOException, URISyntaxException
+        throws ReaderException, IOException
     {
         if (reader == null)
         {
@@ -179,7 +179,7 @@ public class GroupReader
     }
 
     protected static Group parseGroup(Element groupElement)
-        throws URISyntaxException, ReaderException
+        throws ReaderException
     {
         String uri = groupElement.getAttributeValue("uri");
         if (uri == null)
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
index 5c9533a59f02d3a0d428cf60b2d8d900a00eba16..1515d37e34a7dbcf32e945872a2c4135aec483d8 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
@@ -68,12 +68,7 @@
  */
 package ca.nrc.cadc.ac.client;
 
-import java.io.BufferedReader;
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.Reader;
+import java.io.*;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -106,13 +101,11 @@ import ca.nrc.cadc.ac.Role;
 import ca.nrc.cadc.ac.UserNotFoundException;
 import ca.nrc.cadc.auth.AuthenticationUtil;
 import ca.nrc.cadc.auth.SSLUtil;
-import ca.nrc.cadc.net.HttpDownload;
-import ca.nrc.cadc.net.HttpPost;
-import ca.nrc.cadc.net.HttpUpload;
-import ca.nrc.cadc.net.NetUtil;
+import ca.nrc.cadc.net.*;
 
 import com.csvreader.CsvReader;
 
+
 /**
  * Client class for performing group searching and group actions
  * with the access control web service.
@@ -142,12 +135,7 @@ public class GMSClient
         }
         try
         {
-            URL testURL = new URL(baseURL);
-//            if (!testURL.getProtocol().equals("https"))
-//            {
-//                throw new IllegalArgumentException(
-//                        "URL must have HTTPS protocol");
-//            }
+            new URL(baseURL);
         }
         catch (MalformedURLException e)
         {
@@ -312,31 +300,47 @@ public class GMSClient
     public List<String> getGroupNames()
         throws AccessControlException, IOException
     {
-        URL getGroupNamesURL = new URL(this.baseURL + "/groups");
+        final URL getGroupNamesURL = new URL(this.baseURL + "/groups");
         log.debug("getGroupNames request to " + getGroupNamesURL.toString());
-        
-        HttpURLConnection conn = (HttpURLConnection) getGroupNamesURL.openConnection();
-        conn.setRequestMethod("GET");
 
-        SSLSocketFactory sf = getSSLSocketFactory();
-        if ((sf != null) && ((conn instanceof HttpsURLConnection)))
+        final List<String> groupNames = new ArrayList<String>();
+        final HttpDownload httpDownload =
+                new HttpDownload(getGroupNamesURL, new InputStreamWrapper()
         {
-            ((HttpsURLConnection) conn).setSSLSocketFactory(sf);
-        }
-        int responseCode = -1;
-        try
-        {
-            responseCode = conn.getResponseCode();
-        }
-        catch(Exception e)
-        {
-            throw new AccessControlException(e.getMessage());
-        }
-        log.debug("getGroupNames response " + responseCode);
-        
-        if (responseCode != 200)
+            @Override
+            public void read(final InputStream inputStream) throws IOException
+            {
+                try
+                {
+                    final CsvReader reader =
+                            new CsvReader(inputStream, ',',
+                                          Charset.forName("UTF-8"));
+                    if (reader.readRecord())
+                    {
+                        for (int i = 0; i < reader.getColumnCount(); i++)
+                        {
+                            groupNames.add(reader.get(i));
+                        }
+                    }
+                }
+                catch (Exception bug)
+                {
+                    log.error("Unexpected exception", bug);
+                    throw new RuntimeException(bug);
+                }
+            }
+        });
+
+        httpDownload.setSSLSocketFactory(getSSLSocketFactory());
+        httpDownload.run();
+
+        final Throwable error = httpDownload.getThrowable();
+
+        if (error != null)
         {
-            String errMessage = NetUtil.getErrorBody(conn);
+            final String errMessage = error.getMessage();
+            final int responseCode = httpDownload.getResponseCode();
+
             log.debug("getGroupNames response " + responseCode + ": " +
                       errMessage);
 
@@ -352,28 +356,10 @@ public class GMSClient
             throw new IOException("HttpResponse (" + responseCode + ") - " + errMessage);
         }
 
-        log.error("Content-Length: " + conn.getHeaderField("Content-Length"));
-        log.error("Content-Type: " + conn.getHeaderField("Content-Type"));
+        log.error("Content-Length: " + httpDownload.getContentLength());
+        log.error("Content-Type: " + httpDownload.getContentType());
 
-        try
-        {
-            List<String> groupNames = new ArrayList<String>();
-            CsvReader reader = new CsvReader(conn.getInputStream(), ',', Charset.forName("UTF-8"));
-            if (reader.readRecord())
-            {
-                for (int i = 0; i < reader.getColumnCount(); i++)
-                {
-                    groupNames.add(reader.get(i));
-                }
-            }
-            
-            return groupNames;
-        }
-        catch (Exception bug)
-        {
-            log.error("Unexpected exception", bug);
-            throw new RuntimeException(bug);
-        }
+        return groupNames;
     }
 
     /**
@@ -383,11 +369,11 @@ public class GMSClient
      * @return The group after update.
      * @throws IllegalArgumentException If cyclical membership is detected.
      * @throws GroupNotFoundException If the group was not found.
-     * @throws GroupNotFoundException If a member was not found.
+     * @throws UserNotFoundException If a member was not found.
      * @throws AccessControlException If unauthorized to perform this operation.
      * @throws java.io.IOException
      */
-    public Group updateGroup(Group group)
+    public void updateGroup(Group group)
         throws IllegalArgumentException, GroupNotFoundException, UserNotFoundException,
                AccessControlException, IOException
     {
@@ -402,7 +388,7 @@ public class GMSClient
         log.debug("updateGroup: " + groupXML);
 
         HttpPost transfer = new HttpPost(updateGroupURL, groupXML.toString(), 
-                                         "application/xml", true);
+                                         "application/xml", false);
 
         transfer.setSSLSocketFactory(getSSLSocketFactory());
         transfer.run();
@@ -410,11 +396,6 @@ public class GMSClient
         Throwable error = transfer.getThrowable();
         if (error != null)
         {
-            log.debug("updateGroup throwable", error);
-            if (transfer.getResponseCode() == 302)
-            {
-                return getGroup(group.getID());
-            }
             // transfer returns a -1 code for anonymous access.
             if ((transfer.getResponseCode() == -1) || 
                 (transfer.getResponseCode() == 401) || 
@@ -435,18 +416,6 @@ public class GMSClient
             }
             throw new IOException(error);
         }
-
-        String retXML = transfer.getResponseBody();
-        try
-        {
-            log.debug("updateGroup returned: " + retXML);
-            return GroupReader.read(retXML);
-        }
-        catch (Exception bug)
-        {
-            log.error("Unexpected exception", bug);
-            throw new RuntimeException(bug);
-        }
     }
 
     /**
@@ -465,7 +434,7 @@ public class GMSClient
         
         // reset the state of the cache
         clearCache();
-        
+
         HttpURLConnection conn = 
                 (HttpURLConnection) deleteGroupURL.openConnection();
         conn.setRequestMethod("DELETE");
@@ -476,7 +445,9 @@ public class GMSClient
             ((HttpsURLConnection) conn)
                     .setSSLSocketFactory(sf);
         }
-        int responseCode = -1;
+
+        final int responseCode;
+
         try
         {
             responseCode = conn.getResponseCode();
@@ -531,30 +502,17 @@ public class GMSClient
         // reset the state of the cache
         clearCache();
 
-        HttpURLConnection conn = 
-                (HttpURLConnection) addGroupMemberURL.openConnection();
-        conn.setRequestMethod("PUT");
+        final InputStream is = new ByteArrayInputStream(new byte[0]);
+        final HttpUpload httpUpload = new HttpUpload(is, addGroupMemberURL);
 
-        SSLSocketFactory sf = getSSLSocketFactory();
-        if ((sf != null) && ((conn instanceof HttpsURLConnection)))
-        {
-            ((HttpsURLConnection) conn)
-                    .setSSLSocketFactory(getSSLSocketFactory());
-        }
-        
-        // Try to handle anonymous access and throw AccessControlException 
-        int responseCode = -1;
-        try
-        {
-            responseCode = conn.getResponseCode();
-        }
-        catch (Exception ignore) {}
-    
-        if ((responseCode != 200) && (responseCode != 201))
+        httpUpload.setSSLSocketFactory(getSSLSocketFactory());
+        httpUpload.run();
+
+        final Throwable error = httpUpload.getThrowable();
+        if (error != null)
         {
-            String errMessage = NetUtil.getErrorBody(conn);
-            log.debug("addGroupMember response " + responseCode + ": " + 
-                      errMessage);
+            final int responseCode = httpUpload.getResponseCode();
+            final String errMessage = error.getMessage();
 
             if ((responseCode == -1) || 
                 (responseCode == 401) || 
@@ -580,7 +538,7 @@ public class GMSClient
      * @param targetGroupName The group in which to add the group member.
      * @param userID The user to add.
      * @throws GroupNotFoundException If the group was not found.
-     * @throws GroupNotFoundException If the member was not found.
+     * @throws UserNotFoundException If the member was not found.
      * @throws java.io.IOException
      * @throws AccessControlException If unauthorized to perform this operation.
      */
@@ -598,30 +556,17 @@ public class GMSClient
         // reset the state of the cache
         clearCache();
 
-        HttpURLConnection conn = 
-                (HttpURLConnection) addUserMemberURL.openConnection();
-        conn.setRequestMethod("PUT");
+        final InputStream is = new ByteArrayInputStream(new byte[0]);
+        final HttpUpload httpUpload = new HttpUpload(is, addUserMemberURL);
 
-        SSLSocketFactory sf = getSSLSocketFactory();
-        if ((sf != null) && ((conn instanceof HttpsURLConnection)))
-        {
-            ((HttpsURLConnection) conn)
-                    .setSSLSocketFactory(getSSLSocketFactory());
-        }
-        
-        // Try to handle anonymous access and throw AccessControlException 
-        int responseCode = -1;
-        try
-        {
-            responseCode = conn.getResponseCode();
-        }
-        catch (Exception ignore) {}
+        httpUpload.setSSLSocketFactory(getSSLSocketFactory());
+        httpUpload.run();
 
-        if ((responseCode != 200) && (responseCode != 201))
+        final Throwable error = httpUpload.getThrowable();
+        if (error != null)
         {
-            String errMessage = NetUtil.getErrorBody(conn);
-            log.debug("addUserMember response " + responseCode + ": " + 
-                      errMessage);
+            final int responseCode = httpUpload.getResponseCode();
+            final String errMessage = error.getMessage();
 
             if ((responseCode == -1) || 
                 (responseCode == 401) || 
@@ -729,7 +674,7 @@ public class GMSClient
                                           encodedUserID + "?idType=" + 
                                           userIDType);
 
-        log.debug("removeUserMember request to " + 
+        log.debug("removeUserMember request to " +
                   removeUserMemberURL.toString());
         
         // reset the state of the cache
@@ -813,9 +758,11 @@ public class GMSClient
         StringBuilder searchGroupURL = new StringBuilder(this.baseURL);
         searchGroupURL.append("/search?");
         
-        searchGroupURL.append("ID=" + URLEncoder.encode(id, "UTF-8"));
-        searchGroupURL.append("&IDTYPE=" + URLEncoder.encode(idType, "UTF-8"));
-        searchGroupURL.append("&ROLE=" + URLEncoder.encode(roleString, "UTF-8"));
+        searchGroupURL.append("ID=").append(URLEncoder.encode(id, "UTF-8"));
+        searchGroupURL.append("&IDTYPE=")
+                .append(URLEncoder.encode(idType, "UTF-8"));
+        searchGroupURL.append("&ROLE=")
+                .append(URLEncoder.encode(roleString, "UTF-8"));
         
         log.debug("getMemberships request to " + searchGroupURL.toString());
         ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -908,7 +855,7 @@ public class GMSClient
         List<Group> cachedGroups = getCachedGroups(userID, role);
         if (cachedGroups != null)
         {
-            int index = cachedGroups.indexOf(groupName);
+            int index = cachedGroups.indexOf(new Group(groupName));
             if (index != -1)
             {
                 return cachedGroups.get(index);
@@ -925,11 +872,14 @@ public class GMSClient
         
         StringBuilder searchGroupURL = new StringBuilder(this.baseURL);
         searchGroupURL.append("/search?");
-        
-        searchGroupURL.append("ID=" + URLEncoder.encode(id, "UTF-8"));
-        searchGroupURL.append("&IDTYPE=" + URLEncoder.encode(idType, "UTF-8"));
-        searchGroupURL.append("&ROLE=" + URLEncoder.encode(roleString, "UTF-8"));
-        searchGroupURL.append("&GROUPID=" + URLEncoder.encode(groupName, "UTF-8"));
+
+        searchGroupURL.append("ID=").append(URLEncoder.encode(id, "UTF-8"));
+        searchGroupURL.append("&IDTYPE=")
+                .append(URLEncoder.encode(idType, "UTF-8"));
+        searchGroupURL.append("&ROLE=")
+                .append(URLEncoder.encode(roleString, "UTF-8"));
+        searchGroupURL.append("&GROUPID=")
+                .append(URLEncoder.encode(groupName, "UTF-8"));
         
         log.debug("getMembership request to " + searchGroupURL.toString());
         ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -1108,7 +1058,7 @@ public class GMSClient
         {
             log.debug("Caching groups for " + userID + ", role " + role);
             
-            GroupMemberships groupCredentials = null;
+            final GroupMemberships groupCredentials;
             Set groupCredentialSet = subject.getPrivateCredentials(GroupMemberships.class);
             if ((groupCredentialSet != null) && 
                 (groupCredentialSet.size() == 1))
@@ -1133,13 +1083,9 @@ public class GMSClient
             return false;
         }
         
-        Set<Principal> subjectPrincipals = subject.getPrincipals();
-        Iterator<Principal> i = subjectPrincipals.iterator();
-        Principal next = null;
-        while (i.hasNext())
+        for (Principal subjectPrincipal : subject.getPrincipals())
         {
-            next = i.next();
-            if (next.equals(userID))
+            if (subjectPrincipal.equals(userID))
             {
                 return true;
             }