diff --git a/projects/cadcAccessControl-Server/build.xml b/projects/cadcAccessControl-Server/build.xml
index b3856f6dbb9aa7a1a3238e04662ba734567a1a96..39cce90c8e2f2e8c6a52777adad23ec353c5d8f0 100644
--- a/projects/cadcAccessControl-Server/build.xml
+++ b/projects/cadcAccessControl-Server/build.xml
@@ -148,8 +148,7 @@
         <pathelement path="${jars}:${testingJars}"/>
       </classpath>
       <sysproperty key="ca.nrc.cadc.util.PropertiesReader.dir" value="test"/>
-      <test name="ca.nrc.cadc.ac.server.ldap.LdapUserDAOTest" />
-      <test name="ca.nrc.cadc.ac.server.ldap.LdapGroupDAOTest" />
+      <test name="ca.nrc.cadc.ac.server.web.users.GetUserListActionTest" />
       <formatter type="plain" usefile="false" />
     </junit>
   </target>
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java
index 901413c5049db106dff1f4f01fcafce32050eb93..4da9df744f39564fc7bbd3343aa089b9505008e9 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/GroupPersistence.java
@@ -72,8 +72,6 @@ import java.security.AccessControlException;
 import java.security.Principal;
 import java.util.Collection;
 
-import com.unboundid.ldap.sdk.DN;
-
 import ca.nrc.cadc.ac.Group;
 import ca.nrc.cadc.ac.GroupAlreadyExistsException;
 import ca.nrc.cadc.ac.GroupNotFoundException;
@@ -81,7 +79,7 @@ import ca.nrc.cadc.ac.Role;
 import ca.nrc.cadc.ac.UserNotFoundException;
 import ca.nrc.cadc.net.TransientException;
 
-public abstract interface GroupPersistence<T extends Principal>
+public interface GroupPersistence<T extends Principal>
 {
     /**
      * Get all group names.
@@ -90,7 +88,7 @@ public abstract interface GroupPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public Collection<String> getGroupNames()
+    Collection<String> getGroupNames()
             throws TransientException, AccessControlException;
     
     /**
@@ -104,28 +102,10 @@ public abstract interface GroupPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract Group getGroup(String groupID)
+    Group getGroup(String groupID)
         throws GroupNotFoundException, TransientException,
                AccessControlException;
 
-    
-    /**
-     * Get all groups the user, specified by userID, belongs to. 
-     * 
-     * @param userID The userID.
-     * @param isAdmin return only admin Groups when true, else return non-admin
-     *                Groups.
-     * 
-     * @return Collection of group DN.
-     * 
-     * @throws UserNotFoundException  when the user is not found.
-     * @throws TransientException If an temporary, unexpected problem occurred.
-     * @throws AccessControlException If the operation is not permitted.
-     */
-    Collection<DN> getUserGroups(T userID, boolean isAdmin)
-        throws UserNotFoundException, TransientException,
-               AccessControlException;
-
     /**
      * Creates the group.
      *
@@ -141,7 +121,7 @@ public abstract interface GroupPersistence<T extends Principal>
      * @throws GroupNotFoundException if one of the groups in group members or
      * group admins does not exist in the server.
      */
-    public abstract Group addGroup(Group group)
+    Group addGroup(Group group)
         throws GroupAlreadyExistsException, TransientException,
                AccessControlException, UserNotFoundException, 
                GroupNotFoundException;
@@ -155,7 +135,7 @@ public abstract interface GroupPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract void deleteGroup(String groupID)
+    void deleteGroup(String groupID)
         throws GroupNotFoundException, TransientException,
                AccessControlException;
 
@@ -171,7 +151,7 @@ public abstract interface GroupPersistence<T extends Principal>
      * @throws AccessControlException If the operation is not permitted.
      * @throws UserNotFoundException If owner or group members not valid users.
      */
-    public abstract Group modifyGroup(Group group)
+    Group modifyGroup(Group group)
         throws GroupNotFoundException, TransientException,
                AccessControlException, UserNotFoundException;
 
@@ -190,8 +170,7 @@ public abstract interface GroupPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public abstract Collection<Group> getGroups(T userID, Role role, 
-                                                String groupID)
+    Collection<Group> getGroups(T userID, Role role, String groupID)
         throws UserNotFoundException, GroupNotFoundException,
                TransientException, AccessControlException;
     
@@ -207,7 +186,7 @@ public abstract interface GroupPersistence<T extends Principal>
      * @throws TransientException If an temporary, unexpected problem occurred.
      * @throws AccessControlException If the operation is not permitted.
      */
-    public boolean isMember(T userID, String groupID)
+    boolean isMember(T userID, String groupID)
         throws UserNotFoundException, TransientException,
                AccessControlException;
   
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java
index 77c635c5ea45a18901cee811781aa48a497ebed5..71ddf32316e6de250ab5ef5b9a9c2afdb26657b6 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupDAO.java
@@ -311,7 +311,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         {
             final Filter filter = Filter.createPresenceFilter("cn");
             final String [] attributes = new String[] {"cn", "nsaccountlock"};
-            final List<String> groupNames = new ArrayList<String>();
+            final Collection<String> groupNames = new ArrayList<String>();
             final long begin = System.currentTimeMillis();
 
             final SearchResult searchResult =
@@ -472,7 +472,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
             User<X500Principal> owner;
             try
             {
-                owner = userPersist.getMember(groupOwner);
+                owner = userPersist.getX500User(groupOwner);
             }
             catch (UserNotFoundException e)
             {
@@ -504,7 +504,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
                             User<X500Principal> user;
                             try
                             {
-                                user = userPersist.getMember(memberDN);
+                                user = userPersist.getX500User(memberDN);
                             }
                             catch (UserNotFoundException e)
                             {
@@ -972,9 +972,9 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         }
 
         Group group = new Group(searchResult.getAttributeValue("cn"),
-                                userPersist.getMember(
-                                        new DN(searchResult.getAttributeValue(
-                                                "owner"))));
+                                userPersist.getX500User(
+                                    new DN(searchResult.getAttributeValue(
+                                        "owner"))));
         group.description = searchResult.getAttributeValue("description");
         return group;
     }
@@ -1076,7 +1076,7 @@ public class LdapGroupDAO<T extends Principal> extends LdapDAO
         try
         {
             User<X500Principal> subjectUser = 
-                    userPersist.getMember(getSubjectDN());
+                    userPersist.getX500User(getSubjectDN());
             if (subjectUser.equals(owner))
             {
                 return true;
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java
index b45f6015ed4899a961b4c6fc4356f6f561550d75..e66dc2e5960f289be4050c3d87429b0a3f93c38d 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapGroupPersistence.java
@@ -74,8 +74,6 @@ import java.util.Collection;
 
 import org.apache.log4j.Logger;
 
-import com.unboundid.ldap.sdk.DN;
-
 import ca.nrc.cadc.ac.Group;
 import ca.nrc.cadc.ac.GroupAlreadyExistsException;
 import ca.nrc.cadc.ac.GroupNotFoundException;
@@ -146,14 +144,6 @@ public class LdapGroupPersistence<T extends Principal>
             }
         }
     }
-    
-    public Collection<DN> getUserGroups(T userID, boolean isAdmin)
-            throws UserNotFoundException, TransientException,
-                   AccessControlException
-    {
-        return (new LdapUserPersistence<T>()).getUserGroups(userID, isAdmin);
-    }
-
 
     public Group addGroup(Group group)
         throws GroupAlreadyExistsException, TransientException, 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
index e7bf2dc988d1e82e371f101ff36afae836ab13d2..772c6531a7cdfc147731f570795620cbde4dd3c6 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAO.java
@@ -359,8 +359,8 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
                 .getName());
             addAttribute(attributes, LDAP_DISTINGUISHED_NAME, userDN
                 .toNormalizedString());
-            addAttribute(attributes, LADP_USER_PASSWORD, userRequest
-                    .getPassword());
+            addAttribute(attributes, LADP_USER_PASSWORD,
+                String.valueOf(userRequest.getPassword()));
 
             for (UserDetails details : user.details)
             {
@@ -831,7 +831,7 @@ public class LdapUserDAO<T extends Principal> extends LdapDAO
      * @throws UserNotFoundException
      * @throws LDAPException
      */
-    User<X500Principal> getMember(DN userDN)
+    User<X500Principal> getX500User(DN userDN)
             throws UserNotFoundException, LDAPException
     {
         Filter filter =
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ACSearchRunner.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ACSearchRunner.java
index a6409ad49a03dfa8167385d66256577d3ae235f3..f576ec6ba47de6cf4cff2ee3e8fb394d142e3a32 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ACSearchRunner.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ACSearchRunner.java
@@ -74,7 +74,7 @@ import ca.nrc.cadc.ac.UserNotFoundException;
 import ca.nrc.cadc.ac.server.GroupPersistence;
 import ca.nrc.cadc.ac.server.PluginFactory;
 import ca.nrc.cadc.ac.server.RequestValidator;
-import ca.nrc.cadc.ac.xml.GroupsWriter;
+import ca.nrc.cadc.ac.xml.GroupListWriter;
 import ca.nrc.cadc.auth.AuthenticationUtil;
 import ca.nrc.cadc.auth.HttpPrincipal;
 import ca.nrc.cadc.net.TransientException;
@@ -236,7 +236,8 @@ public class ACSearchRunner implements JobRunner
                 groups = new ArrayList<Group>();
             }
             syncOut.setResponseCode(HttpServletResponse.SC_OK);
-            GroupsWriter.write(groups, syncOut.getOutputStream());
+            GroupListWriter groupListWriter = new GroupListWriter();
+            groupListWriter.write(groups, syncOut.getOutputStream());
             
             // Mark the Job as completed.
 //            jobUpdater.setPhase(job.getID(), ExecutionPhase.EXECUTING, 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupAction.java
similarity index 97%
rename from projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsAction.java
rename to projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupAction.java
index 78cda24d63fceed6ea5a68d7f1fcc8145fa85bf3..94f05dc89ac85a976022c4aea581abe0c4d5675f 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupAction.java
@@ -91,15 +91,14 @@ import ca.nrc.cadc.ac.server.PluginFactory;
 import ca.nrc.cadc.ac.server.UserPersistence;
 import ca.nrc.cadc.net.TransientException;
 
-public abstract class GroupsAction
-    implements PrivilegedExceptionAction<Object>
+public abstract class AbstractGroupAction implements PrivilegedExceptionAction<Object>
 {
-    private static final Logger log = Logger.getLogger(GroupsAction.class);
+    private static final Logger log = Logger.getLogger(AbstractGroupAction.class);
     protected GroupLogInfo logInfo;
     protected HttpServletRequest request;
     protected HttpServletResponse response;
 
-    GroupsAction()
+    public AbstractGroupAction()
     {
     }
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AddGroupMemberAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AddGroupMemberAction.java
index 12dff65bc553e6ac6f711e77ddc73aaa65b70f40..0f5d204b5a0d8d70845f6ec58241e49d2a3f3008 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AddGroupMemberAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AddGroupMemberAction.java
@@ -74,7 +74,7 @@ import ca.nrc.cadc.ac.server.GroupPersistence;
 import java.util.ArrayList;
 import java.util.List;
 
-public class AddGroupMemberAction extends GroupsAction
+public class AddGroupMemberAction extends AbstractGroupAction
 {
     private final String groupName;
     private final String groupMemberName;
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AddUserMemberAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AddUserMemberAction.java
index bf39ee26d1225841677806914312106d38525442..30c4b8be94e9931f4e5b073bcddde315ccffcc98 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AddUserMemberAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/AddUserMemberAction.java
@@ -77,7 +77,7 @@ import java.security.Principal;
 import java.util.ArrayList;
 import java.util.List;
 
-public class AddUserMemberAction extends GroupsAction
+public class AddUserMemberAction extends AbstractGroupAction
 {
     private final String groupName;
     private final String userID;
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/CreateGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/CreateGroupAction.java
index 91a058d3bed9f6fbe377631e85c118aeb193c0b8..e87f6c04b290f0565c6cdbd8449c7a10f7a579dc 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/CreateGroupAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/CreateGroupAction.java
@@ -78,7 +78,7 @@ import ca.nrc.cadc.ac.server.GroupPersistence;
 import ca.nrc.cadc.ac.xml.GroupReader;
 import ca.nrc.cadc.ac.xml.GroupWriter;
 
-public class CreateGroupAction extends GroupsAction
+public class CreateGroupAction extends AbstractGroupAction
 {
     private final InputStream inputStream;
 
@@ -91,10 +91,12 @@ public class CreateGroupAction extends GroupsAction
     public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
-        Group group = GroupReader.read(this.inputStream);
+        GroupReader groupReader = new GroupReader();
+        Group group = groupReader.read(this.inputStream);
         Group newGroup = groupPersistence.addGroup(group);
         this.response.setContentType("application/xml");
-        GroupWriter.write(newGroup, this.response.getOutputStream());
+        GroupWriter groupWriter = new GroupWriter();
+        groupWriter.write(newGroup, this.response.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/groups/DeleteGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/DeleteGroupAction.java
index 579f4b7e99600fb958e91de27d4b8183a1de0ca0..52928fbd95aa5c2cffe90c663d41a1ed4b55ce51 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/DeleteGroupAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/DeleteGroupAction.java
@@ -74,7 +74,7 @@ import ca.nrc.cadc.ac.Group;
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.server.GroupPersistence;
 
-public class DeleteGroupAction extends GroupsAction
+public class DeleteGroupAction extends AbstractGroupAction
 {
     private final String groupName;
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GetGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GetGroupAction.java
index adaf8d029a5e6fa0201c8985fa55d6de1584ba9c..fba96b0a95dba184fec076a628c6704e166efcec 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GetGroupAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GetGroupAction.java
@@ -71,7 +71,7 @@ import ca.nrc.cadc.ac.Group;
 import ca.nrc.cadc.ac.server.GroupPersistence;
 import ca.nrc.cadc.ac.xml.GroupWriter;
 
-public class GetGroupAction extends GroupsAction
+public class GetGroupAction extends AbstractGroupAction
 {
     private final String groupName;
 
@@ -86,7 +86,9 @@ 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());
+
+        GroupWriter groupWriter = new GroupWriter();
+        groupWriter.write(group, this.response.getOutputStream());
     }
 
 }
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GetGroupNamesAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GetGroupNamesAction.java
index fcf2cc4b8e863de961a9c648511b591e9e112eba..f03fadaf37dd19070a6a70023aa404ea52eba752 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GetGroupNamesAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GetGroupNamesAction.java
@@ -76,7 +76,7 @@ import org.apache.log4j.Logger;
 
 import ca.nrc.cadc.ac.server.GroupPersistence;
 
-public class GetGroupNamesAction extends GroupsAction
+public class GetGroupNamesAction extends AbstractGroupAction
 {
 
     private static final Logger log = Logger.getLogger(GetGroupNamesAction.class);
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsServlet.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupServlet.java
similarity index 97%
rename from projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsServlet.java
rename to projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupServlet.java
index fb5413bbfb2c8b4eb3b8920cb7c1b8a1a8054ebc..15c647da252b22807cd13695cf2f9700d8f0d7e8 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsServlet.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupServlet.java
@@ -86,11 +86,10 @@ import ca.nrc.cadc.util.StringUtil;
  *
  * @author majorb
  */
-public class GroupsServlet extends HttpServlet
+public class GroupServlet extends HttpServlet
 {
-
     private static final long serialVersionUID = 7854660717655869213L;
-    private static final Logger log = Logger.getLogger(GroupsServlet.class);
+    private static final Logger log = Logger.getLogger(GroupServlet.class);
 
     /**
      * Create a GroupAction and run the action safely.
@@ -106,7 +105,7 @@ public class GroupsServlet extends HttpServlet
             Subject subject = AuthenticationUtil.getSubject(request);
             logInfo.setSubject(subject);
 
-            GroupsAction action = factory.createAction(request);
+            AbstractGroupAction action = factory.createAction(request);
 
             action.setLogInfo(logInfo);
             action.setHttpServletRequest(request);
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionFactory.java
index 87da08e15210467f9dc84030ec954c8dd5c31cc1..5cf7a054c70568d75fdddfa964a2a6334eb1c401 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionFactory.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionFactory.java
@@ -90,17 +90,17 @@ public abstract class GroupsActionFactory
 {
     private static final Logger log = Logger.getLogger(GroupsActionFactory.class);
 
-    public abstract GroupsAction createAction(HttpServletRequest request)
+    public abstract AbstractGroupAction createAction(HttpServletRequest request)
         throws IllegalArgumentException, IOException;
 
     public static GroupsActionFactory httpGetFactory()
     {
         return new GroupsActionFactory()
         {
-            public GroupsAction createAction(HttpServletRequest request)
+            public AbstractGroupAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
-                GroupsAction action = null;
+                AbstractGroupAction action = null;
                 String path = request.getPathInfo();
                 log.debug("path: " + path);
 
@@ -130,10 +130,10 @@ public abstract class GroupsActionFactory
     {
         return new GroupsActionFactory()
         {
-            public GroupsAction createAction(HttpServletRequest request)
+            public AbstractGroupAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
-                GroupsAction action = null;
+                AbstractGroupAction action = null;
                 String path = request.getPathInfo();
                 log.debug("path: " + path);
 
@@ -174,10 +174,10 @@ public abstract class GroupsActionFactory
     {
         return new GroupsActionFactory()
         {
-            public GroupsAction createAction(HttpServletRequest request)
+            public AbstractGroupAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
-                GroupsAction action = null;
+                AbstractGroupAction action = null;
                 String path = request.getPathInfo();
                 log.debug("path: " + path);
 
@@ -222,10 +222,10 @@ public abstract class GroupsActionFactory
     {
         return new GroupsActionFactory()
         {
-            public GroupsAction createAction(HttpServletRequest request)
+            public AbstractGroupAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
-                GroupsAction action = null;
+                AbstractGroupAction action = null;
                 String path = request.getPathInfo();
                 log.debug("path: " + path);
 
@@ -269,7 +269,7 @@ public abstract class GroupsActionFactory
     {
         return new GroupsActionFactory()
         {
-            public GroupsAction createAction(HttpServletRequest request)
+            public AbstractGroupAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
                 // http head not supported
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ModifyGroupAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ModifyGroupAction.java
index 82f262e535b07b27469dd68c1b047293d87b7c75..4bd268ee248b6c6ba7630d800e6b18d2eadaf1b9 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ModifyGroupAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/ModifyGroupAction.java
@@ -77,7 +77,7 @@ import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.server.GroupPersistence;
 import ca.nrc.cadc.ac.xml.GroupReader;
 
-public class ModifyGroupAction extends GroupsAction
+public class ModifyGroupAction extends AbstractGroupAction
 {
     private final String groupName;
     private final String request;
@@ -94,7 +94,8 @@ public class ModifyGroupAction extends GroupsAction
     public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
-        Group group = GroupReader.read(this.inputStream);
+        GroupReader groupReader = new GroupReader();
+        Group group = groupReader.read(this.inputStream);
         Group oldGroup = groupPersistence.getGroup(this.groupName);
         groupPersistence.modifyGroup(group);
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/RemoveGroupMemberAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/RemoveGroupMemberAction.java
index 26352aa7610d252343dd530f04b7a158fbc7a07c..e1b5848bdcab28c79d103972de9aab461568e82b 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/RemoveGroupMemberAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/RemoveGroupMemberAction.java
@@ -74,7 +74,7 @@ import ca.nrc.cadc.ac.server.GroupPersistence;
 import java.util.ArrayList;
 import java.util.List;
 
-public class RemoveGroupMemberAction extends GroupsAction
+public class RemoveGroupMemberAction extends AbstractGroupAction
 {
     private final String groupName;
     private final String groupMemberName;
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/RemoveUserMemberAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/RemoveUserMemberAction.java
index f7bcc91f6ae471b78a75b638d91630a8ec467acb..d04fe4a6cfe0172eeb574caa82c92ce750e80c53 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/RemoveUserMemberAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/RemoveUserMemberAction.java
@@ -77,7 +77,7 @@ import java.security.Principal;
 import java.util.ArrayList;
 import java.util.List;
 
-public class RemoveUserMemberAction extends GroupsAction
+public class RemoveUserMemberAction extends AbstractGroupAction
 {
     private final String groupName;
     private final String userID;
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/AbstractUserAction.java
similarity index 88%
rename from projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java
rename to projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/AbstractUserAction.java
index e8bd10aa22f33658b707101c4b0cdbe5f3499e6f..1c7f0e126dc41e6718bc543ed6562889abe8266d 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/AbstractUserAction.java
@@ -72,28 +72,34 @@ import ca.nrc.cadc.ac.PersonalDetails;
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.UserNotFoundException;
 import ca.nrc.cadc.ac.UserRequest;
+import ca.nrc.cadc.ac.json.JsonUserListWriter;
+import ca.nrc.cadc.ac.json.JsonUserReader;
+import ca.nrc.cadc.ac.json.JsonUserRequestReader;
+import ca.nrc.cadc.ac.json.JsonUserWriter;
 import ca.nrc.cadc.ac.server.PluginFactory;
 import ca.nrc.cadc.ac.server.UserPersistence;
 import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.ac.xml.UserListWriter;
+import ca.nrc.cadc.ac.xml.UserReader;
+import ca.nrc.cadc.ac.xml.UserRequestReader;
+import ca.nrc.cadc.ac.xml.UserWriter;
 import ca.nrc.cadc.net.TransientException;
 import org.apache.log4j.Logger;
 
-import javax.security.auth.Subject;
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
 import java.io.Writer;
 import java.security.AccessControlException;
 import java.security.Principal;
-import java.security.PrivilegedActionException;
 import java.security.PrivilegedExceptionAction;
 import java.util.Iterator;
 import java.util.Map;
 import java.util.Set;
 
-public abstract class UsersAction implements PrivilegedExceptionAction<Object>
+public abstract class AbstractUserAction implements PrivilegedExceptionAction<Object>
 {
-    private static final Logger log = Logger.getLogger(UsersAction.class);
+    private static final Logger log = Logger.getLogger(AbstractUserAction.class);
     static final String DEFAULT_CONTENT_TYPE = "text/xml";
     static final String JSON_CONTENT_TYPE = "application/json";
 
@@ -101,7 +107,7 @@ public abstract class UsersAction implements PrivilegedExceptionAction<Object>
     protected HttpServletResponse response;
     protected String acceptedContentType = DEFAULT_CONTENT_TYPE;
 
-    UsersAction()
+    AbstractUserAction()
     {
     }
 
@@ -225,12 +231,13 @@ public abstract class UsersAction implements PrivilegedExceptionAction<Object>
 
         if (acceptedContentType.equals(DEFAULT_CONTENT_TYPE))
         {
-            userRequest = ca.nrc.cadc.ac.xml.UserRequestReader.read(inputStream);
+            UserRequestReader requestReader = new UserRequestReader();
+            userRequest = requestReader.read(inputStream);
         }
         else if (acceptedContentType.equals(JSON_CONTENT_TYPE))
         {
-            userRequest =
-                    ca.nrc.cadc.ac.json.UserRequestReader.read(inputStream);
+            JsonUserRequestReader requestReader = new JsonUserRequestReader();
+            userRequest = requestReader.read(inputStream);
         }
         else
         {
@@ -258,11 +265,13 @@ public abstract class UsersAction implements PrivilegedExceptionAction<Object>
 
         if (acceptedContentType.equals(DEFAULT_CONTENT_TYPE))
         {
-            user = ca.nrc.cadc.ac.xml.UserReader.read(inputStream);
+            UserReader userReader = new UserReader();
+            user = userReader.read(inputStream);
         }
         else if (acceptedContentType.equals(JSON_CONTENT_TYPE))
         {
-            user = ca.nrc.cadc.ac.json.UserReader.read(inputStream);
+            JsonUserReader userReader = new JsonUserReader();
+            user = userReader.read(inputStream);
         }
         else
         {
@@ -288,11 +297,13 @@ public abstract class UsersAction implements PrivilegedExceptionAction<Object>
 
         if (acceptedContentType.equals(DEFAULT_CONTENT_TYPE))
         {
-            ca.nrc.cadc.ac.xml.UserWriter.write(user, writer);
+            UserWriter userWriter = new UserWriter();
+            userWriter.write(user, writer);
         }
         else if (acceptedContentType.equals(JSON_CONTENT_TYPE))
         {
-            ca.nrc.cadc.ac.json.UserWriter.write(user, writer);
+            JsonUserWriter userWriter = new JsonUserWriter();
+            userWriter.write(user, writer);
         }
     }
 
@@ -309,11 +320,13 @@ public abstract class UsersAction implements PrivilegedExceptionAction<Object>
 
         if (acceptedContentType.equals(DEFAULT_CONTENT_TYPE))
         {
-            ca.nrc.cadc.ac.xml.UsersWriter.write(users, writer);
+            UserListWriter userListWriter = new UserListWriter();
+            userListWriter.write(users, writer);
         }
         else if (acceptedContentType.equals(JSON_CONTENT_TYPE))
         {
-            ca.nrc.cadc.ac.json.UsersWriter.write(users, writer);
+            JsonUserListWriter userListWriter = new JsonUserListWriter();
+            userListWriter.write(users, writer);
         }
     }
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/CreateUserAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/CreateUserAction.java
index 4fe9bd125b8799624ac0d47f7f7c7709b388364d..82b6bcf6bd4f1fa5e5fcac2a244399fa5cea03fd 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/CreateUserAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/CreateUserAction.java
@@ -78,7 +78,7 @@ import javax.servlet.http.HttpServletResponse;
 import java.security.Principal;
 
 
-public class CreateUserAction extends UsersAction
+public class CreateUserAction extends AbstractUserAction
 {
     private final InputStream inputStream;
 
@@ -91,15 +91,11 @@ public class CreateUserAction extends UsersAction
 
     public void doAction() throws Exception
     {
-        final UserPersistence<Principal> userPersistence =
-                getUserPersistence();
-        final UserRequest<Principal> userRequest =
-                readUserRequest(this.inputStream);
-        final User<Principal> newUser =
-                userPersistence.addUser(userRequest);
+        final UserPersistence<Principal> userPersistence = getUserPersistence();
+        final UserRequest<Principal> userRequest = readUserRequest(this.inputStream);
+        final User<Principal> newUser = userPersistence.addUser(userRequest);
 
         response.setStatus(HttpServletResponse.SC_CREATED);
-
         logUserInfo(newUser.getUserID().getName());
     }
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/DeleteUserAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/DeleteUserAction.java
index f24cb87195687d8002e7b86bf4dac19778333a24..aa74d299c1798e2799a66e60606a3def3097cc78 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/DeleteUserAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/DeleteUserAction.java
@@ -72,7 +72,7 @@ import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.server.UserPersistence;
 import java.security.Principal;
 
-public class DeleteUserAction extends UsersAction
+public class DeleteUserAction extends AbstractUserAction
 {
     private final Principal userID;
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserAction.java
index b060426997c6bd6e363194db6ab400551782de69..8d95d94616bc1c032081faa526779c9451f00319 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserAction.java
@@ -74,7 +74,7 @@ import ca.nrc.cadc.ac.server.UserPersistence;
 import java.security.Principal;
 
 
-public class GetUserAction extends UsersAction
+public class GetUserAction extends AbstractUserAction
 {
     private final Principal userID;
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUsersAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserListAction.java
similarity index 96%
rename from projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUsersAction.java
rename to projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserListAction.java
index 3f366b34ae89038fc7ab3ac7d4e47e4e655eda79..c601bd33737758fa0739e6ed6064c16392cc1cde 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUsersAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/GetUserListAction.java
@@ -75,20 +75,18 @@ import org.apache.log4j.Logger;
 import ca.nrc.cadc.ac.server.UserPersistence;
 
 
-public class GetUsersAction extends UsersAction
+public class GetUserListAction extends AbstractUserAction
 {
 
-    private static final Logger log = Logger.getLogger(GetUsersAction.class);
+    private static final Logger log = Logger.getLogger(GetUserListAction.class);
 
-    GetUsersAction()
+    GetUserListAction()
     {
-        super();
     }
 
     public void doAction() throws Exception
     {
         final UserPersistence userPersistence = getUserPersistence();
-
         writeUsers(userPersistence.getUsers());
     }
 }
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/ModifyUserAction.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/ModifyUserAction.java
index 055aa1ee24f1bb95ab15a6921407ad2dce4f6df3..4e7ebe372ba2a3c0feb149274bf8bbfcbb38b58f 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/ModifyUserAction.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/ModifyUserAction.java
@@ -81,7 +81,7 @@ import java.util.Iterator;
 import java.util.Set;
 
 
-public class ModifyUserAction extends UsersAction
+public class ModifyUserAction extends AbstractUserAction
 {
     private final InputStream inputStream;
 
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersActionFactory.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserActionFactory.java
similarity index 85%
rename from projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersActionFactory.java
rename to projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserActionFactory.java
index 49d53403005a5433e7600281635b3d936cfcc310..ae8ec41014d2f0ed8d67a7bc50a664c270de1542 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersActionFactory.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserActionFactory.java
@@ -71,16 +71,12 @@ package ca.nrc.cadc.ac.server.web.users;
 import ca.nrc.cadc.ac.IdentityType;
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.server.web.WebUtil;
-import ca.nrc.cadc.ac.server.web.groups.GroupsAction;
-import ca.nrc.cadc.ac.server.web.groups.GroupsActionFactory;
 import ca.nrc.cadc.auth.CookiePrincipal;
 import ca.nrc.cadc.auth.HttpPrincipal;
 import ca.nrc.cadc.auth.NumericPrincipal;
 import ca.nrc.cadc.auth.OpenIdPrincipal;
-import ca.nrc.cadc.util.StringUtil;
 
 import java.io.IOException;
-import java.io.InputStream;
 import java.security.Principal;
 import javax.security.auth.x500.X500Principal;
 import javax.servlet.http.HttpServletRequest;
@@ -88,22 +84,21 @@ import javax.servlet.http.HttpServletRequest;
 import org.apache.log4j.Logger;
 
 
-public abstract class UsersActionFactory
+public abstract class UserActionFactory
 {
-    private static final Logger log = Logger
-            .getLogger(UsersActionFactory.class);
+    private static final Logger log = Logger.getLogger(UserActionFactory.class);
 
-    public abstract UsersAction createAction(HttpServletRequest request)
-            throws IllegalArgumentException, IOException;
+    public abstract AbstractUserAction createAction(HttpServletRequest request)
+        throws IllegalArgumentException, IOException;
 
-    public static UsersActionFactory httpGetFactory()
+    public static UserActionFactory httpGetFactory()
     {
-        return new UsersActionFactory()
+        return new UserActionFactory()
         {
-            public UsersAction createAction(HttpServletRequest request)
+            public AbstractUserAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
-                UsersAction action = null;
+                AbstractUserAction action = null;
                 String path = request.getPathInfo();
                 log.debug("path: " + path);
 
@@ -111,7 +106,7 @@ public abstract class UsersActionFactory
 
                 if (segments.length == 0)
                 {
-                    action = new GetUsersAction();
+                    action = new GetUserListAction();
                 }
                 else if (segments.length == 1)
                 {
@@ -130,14 +125,14 @@ public abstract class UsersActionFactory
         };
     }
 
-    public static UsersActionFactory httpPutFactory()
+    public static UserActionFactory httpPutFactory()
     {
-        return new UsersActionFactory()
+        return new UserActionFactory()
         {
-            public UsersAction createAction(HttpServletRequest request)
+            public AbstractUserAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
-                UsersAction action = null;
+                AbstractUserAction action = null;
                 String path = request.getPathInfo();
                 log.debug("path: " + path);
 
@@ -159,14 +154,14 @@ public abstract class UsersActionFactory
         };
     }
 
-    public static UsersActionFactory httpPostFactory()
+    public static UserActionFactory httpPostFactory()
     {
-        return new UsersActionFactory()
+        return new UserActionFactory()
         {
-            public UsersAction createAction(HttpServletRequest request)
+            public AbstractUserAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
-                UsersAction action = null;
+                AbstractUserAction action = null;
                 String path = request.getPathInfo();
                 log.debug("path: " + path);
 
@@ -188,14 +183,14 @@ public abstract class UsersActionFactory
         };
     }
 
-    public static UsersActionFactory httpDeleteFactory()
+    public static UserActionFactory httpDeleteFactory()
     {
-        return new UsersActionFactory()
+        return new UserActionFactory()
         {
-            public UsersAction createAction(HttpServletRequest request)
+            public AbstractUserAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
-                UsersAction action = null;
+                AbstractUserAction action = null;
                 String path = request.getPathInfo();
                 log.debug("path: " + path);
 
@@ -218,11 +213,11 @@ public abstract class UsersActionFactory
         };
     }
 
-    public static UsersActionFactory httpHeadFactory()
+    public static UserActionFactory httpHeadFactory()
     {
-        return new UsersActionFactory()
+        return new UserActionFactory()
         {
-            public UsersAction createAction(HttpServletRequest request)
+            public AbstractUserAction createAction(HttpServletRequest request)
                 throws IllegalArgumentException, IOException
             {
                 // http head not supported
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersServlet.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserServlet.java
similarity index 89%
rename from projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersServlet.java
rename to projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserServlet.java
index eae260e1945108f0c84a31e300d01aaabc783dcf..2f2ee87959923bf690cc69d6d42f1f82b30e1b78 100644
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UsersServlet.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/users/UserServlet.java
@@ -76,21 +76,21 @@ import javax.servlet.http.HttpServlet;
 import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
-import ca.nrc.cadc.ac.UserAlreadyExistsException;
 import ca.nrc.cadc.util.StringUtil;
 import org.apache.log4j.Logger;
 
 import ca.nrc.cadc.auth.AuthenticationUtil;
 
-public class UsersServlet extends HttpServlet
+public class UserServlet extends HttpServlet
 {
-    private static final Logger log = Logger.getLogger(UsersServlet.class);
 
+    private static final long serialVersionUID = 5289130885807305288L;
+    private static final Logger log = Logger.getLogger(UserServlet.class);
 
     /**
      * Create a UserAction and run the action safely.
      */
-    private void doAction(UsersActionFactory factory, HttpServletRequest request, HttpServletResponse response)
+    private void doAction(UserActionFactory factory, HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
         long start = System.currentTimeMillis();
@@ -102,7 +102,7 @@ public class UsersServlet extends HttpServlet
             Subject subject = AuthenticationUtil.getSubject(request);
             logInfo.setSubject(subject);
 
-            UsersAction action = factory.createAction(request);
+            AbstractUserAction action = factory.createAction(request);
 
             action.setLogInfo(logInfo);
             action.setResponse(response);
@@ -162,35 +162,35 @@ public class UsersServlet extends HttpServlet
     public void doGet(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(UsersActionFactory.httpGetFactory(), request, response);
+        doAction(UserActionFactory.httpGetFactory(), request, response);
     }
 
     @Override
     public void doPost(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(UsersActionFactory.httpGetFactory(), request, response);
+        doAction(UserActionFactory.httpGetFactory(), request, response);
     }
 
     @Override
     public void doDelete(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(UsersActionFactory.httpDeleteFactory(), request, response);
+        doAction(UserActionFactory.httpDeleteFactory(), request, response);
     }
 
     @Override
     public void doPut(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(UsersActionFactory.httpPutFactory(), request, response);
+        doAction(UserActionFactory.httpPutFactory(), request, response);
     }
 
     @Override
     public void doHead(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(UsersActionFactory.httpHeadFactory(), request, response);
+        doAction(UserActionFactory.httpHeadFactory(), request, response);
     }
 
     /**
@@ -204,13 +204,13 @@ public class UsersServlet extends HttpServlet
         final String requestedContentType = request.getHeader("Accept");
 
         if (StringUtil.hasText(requestedContentType)
-            && requestedContentType.contains(UsersAction.JSON_CONTENT_TYPE))
+            && requestedContentType.contains(AbstractUserAction.JSON_CONTENT_TYPE))
         {
-            return UsersAction.JSON_CONTENT_TYPE;
+            return AbstractUserAction.JSON_CONTENT_TYPE;
         }
         else
         {
-            return UsersAction.DEFAULT_CONTENT_TYPE;
+            return AbstractUserAction.DEFAULT_CONTENT_TYPE;
         }
     }
 }
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java
index 68c0c6468659b82000c82bc37e13c0cd273aa1fa..4226915aa1437ea3c8242be4f80753f6ee9d77e9 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/ldap/LdapUserDAOTest.java
@@ -149,7 +149,7 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
         expected.details.add(new PersonalDetails("foo", "bar"));
 
         final UserRequest<HttpPrincipal> userRequest =
-                new UserRequest<HttpPrincipal>(expected, "123456");
+                new UserRequest<HttpPrincipal>(expected, "123456".toCharArray());
 
         Subject subject = new Subject();
         subject.getPrincipals().add(testUser.getUserID());
@@ -311,7 +311,7 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
             {
                 try
                 {
-                    User<X500Principal> actual = getUserDAO().getMember(new DN(testUserDN));
+                    User<X500Principal> actual = getUserDAO().getX500User(new DN(testUserDN));
                     check(testUser, actual);
                     return null;
                 }
@@ -334,7 +334,7 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
             {
                 try
                 {
-                    User<X500Principal> actual = getUserDAO().getMember(new DN(testUserDN));
+                    User<X500Principal> actual = getUserDAO().getX500User(new DN(testUserDN));
                     check(testUser, actual);
                     return null;
                 }
@@ -415,8 +415,8 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
         // Create a test user with a known password
         final User<HttpPrincipal> testUser2;
         final String username = createUserID();
-        final String password = "foo";
-        final String newPassword = "bar";
+        final char[] password = "foo".toCharArray();
+        final char[] newPassword = "bar".toCharArray();
 
         HttpPrincipal principal = new HttpPrincipal(username);
         testUser2 = new User<HttpPrincipal>(principal);
@@ -454,7 +454,7 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
             {
                 try
                 {
-                    getUserDAO().doLogin(username, password);
+                    getUserDAO().doLogin(username, String.valueOf(password));
                 }
                 catch (Exception e)
                 {
@@ -473,7 +473,8 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
                 try
                 {
                     final LdapUserDAO<HttpPrincipal> userDAO = getUserDAO();
-                    userDAO.setPassword(testUser2, password, newPassword);
+                    userDAO.setPassword(testUser2, String.valueOf(password),
+                                        String.valueOf(newPassword));
                     fail("should throw exception if subject and user are not the same");
                 }
                 catch (Exception ignore){}
@@ -491,7 +492,8 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
                 try
                 {
                     final LdapUserDAO<HttpPrincipal> userDAO = getUserDAO();
-                    userDAO.setPassword(testUser2, password, newPassword);
+                    userDAO.setPassword(testUser2, String.valueOf(password),
+                                        String.valueOf(newPassword));
                 }
                 catch (Exception e)
                 {
@@ -510,7 +512,7 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
             {
                 try
                 {
-                    getUserDAO().doLogin(username, password);
+                    getUserDAO().doLogin(username, String.valueOf(password));
                 }
                 catch (Exception e)
                 {
@@ -528,7 +530,7 @@ public class LdapUserDAOTest extends AbstractLdapDAOTest
         // Create a test user
         final User<HttpPrincipal> testUser2;
         final String username = createUserID();
-        final String password = "foo";
+        final char[] password = "foo".toCharArray();
 
         HttpPrincipal principal = new HttpPrincipal(username);
         testUser2 = new User<HttpPrincipal>(principal);
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupActionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..502e314d2992ee758465b10252986e13cc486220
--- /dev/null
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AbstractGroupActionTest.java
@@ -0,0 +1,248 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web.groups;
+
+import ca.nrc.cadc.ac.GroupAlreadyExistsException;
+import ca.nrc.cadc.ac.GroupNotFoundException;
+import ca.nrc.cadc.ac.MemberAlreadyExistsException;
+import ca.nrc.cadc.ac.MemberNotFoundException;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.net.TransientException;
+import ca.nrc.cadc.util.Log4jInit;
+
+import java.io.PrintWriter;
+import java.io.StringWriter;
+import java.security.AccessControlException;
+import javax.servlet.http.HttpServletResponse;
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+import org.easymock.EasyMock;
+import org.junit.BeforeClass;
+import org.junit.Test;
+import static org.junit.Assert.*;
+
+/**
+ *
+ * @author jburke
+ */
+public class AbstractGroupActionTest
+{
+    private final static Logger log = Logger.getLogger(AbstractGroupActionTest.class);
+
+    @BeforeClass
+    public static void setUpClass()
+    {
+        Log4jInit.setLevel("ca.nrc.cadc.ac", Level.INFO);
+    }
+
+    @Test
+    public void testDoActionAccessControlException() throws Exception
+    {
+        String message = "Permission Denied";
+        int responseCode = 403;
+        Exception e = new AccessControlException("");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionIllegalArgumentException() throws Exception
+    {
+        String message = "message";
+        int responseCode = 400;
+        Exception e = new IllegalArgumentException("message");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionMemberNotFoundException() throws Exception
+    {
+        String message = "Member not found: foo";
+        int responseCode = 404;
+        Exception e = new MemberNotFoundException("foo");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionGroupNotFoundException() throws Exception
+    {
+        String message = "Group not found: foo";
+        int responseCode = 404;
+        Exception e = new GroupNotFoundException("foo");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionUserNotFoundException() throws Exception
+    {
+        String message = "User not found: foo";
+        int responseCode = 404;
+        Exception e = new UserNotFoundException("foo");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionMemberAlreadyExistsException() throws Exception
+    {
+        String message = "Member already exists: foo";
+        int responseCode = 409;
+        Exception e = new MemberAlreadyExistsException("foo");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionGroupAlreadyExistsException() throws Exception
+    {
+        String message = "Group already exists: foo";
+        int responseCode = 409;
+        Exception e = new GroupAlreadyExistsException("foo");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionUnsupportedOperationException() throws Exception
+    {
+        String message = "Not yet implemented.";
+        int responseCode = 501;
+        Exception e = new UnsupportedOperationException();
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionTransientException() throws Exception
+    {
+        try
+        {
+            HttpServletResponse response = EasyMock.createMock(HttpServletResponse.class);
+            EasyMock.expect(response.isCommitted()).andReturn(Boolean.FALSE);
+            response.setContentType("text/plain");
+            EasyMock.expectLastCall().once();
+            EasyMock.expect(response.getWriter()).andReturn(new PrintWriter(new StringWriter()));
+            EasyMock.expectLastCall().once();
+            response.setStatus(503);
+            EasyMock.expectLastCall().once();
+            EasyMock.replay(response);
+
+            GroupsActionImpl action = new GroupsActionImpl();
+            action.setException(new TransientException("foo"));
+            action.doAction();
+        }
+        catch (Throwable t)
+        {
+            log.error(t.getMessage(), t);
+            fail("unexpected error: " + t.getMessage());
+        }
+    }
+
+    private void testDoAction(String message, int responseCode, Exception e)
+        throws Exception
+    {
+        try
+        {
+            HttpServletResponse response = EasyMock.createMock(HttpServletResponse.class);
+            EasyMock.expect(response.isCommitted()).andReturn(Boolean.FALSE);
+            response.setContentType("text/plain");
+            EasyMock.expectLastCall().once();
+            EasyMock.expect(response.getWriter()).andReturn(new PrintWriter(new StringWriter()));
+            EasyMock.expectLastCall().once();
+            response.setStatus(responseCode);
+            EasyMock.expectLastCall().once();
+            EasyMock.replay(response);
+
+            GroupsActionImpl action = new GroupsActionImpl();
+            action.setException(e);
+            action.doAction();
+        }
+        catch (Throwable t)
+        {
+            log.error(t.getMessage(), t);
+            fail("unexpected error: " + t.getMessage());
+        }
+    }
+
+    public class GroupsActionImpl extends AbstractGroupAction
+    {
+        Exception exception;
+
+        public GroupsActionImpl()
+        {
+            super();
+        }
+
+        public void doAction() throws Exception
+        {
+            throw exception;
+        }
+
+        public void setException(Exception e)
+        {
+            this.exception = e;
+        }
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GroupActionFactoryTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GroupActionFactoryTest.java
index ffa77fa364cb09ddf24256f90afcf2adc4131878..0aad33de8b38c44848aba054f3561874cea02a80 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GroupActionFactoryTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GroupActionFactoryTest.java
@@ -94,7 +94,7 @@ public class GroupActionFactoryTest
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName/groupMembers/groupToAdd");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpPutFactory().createAction(request);
+            AbstractGroupAction action = GroupsActionFactory.httpPutFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof AddGroupMemberAction);
         }
@@ -114,7 +114,7 @@ public class GroupActionFactoryTest
             EasyMock.expect(request.getPathInfo()).andReturn("groupName/userMembers/userToAdd");
             EasyMock.expect(request.getParameter("idType")).andReturn("x509");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpPutFactory().createAction(request);
+            AbstractGroupAction action = GroupsActionFactory.httpPutFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof AddUserMemberAction);
         }
@@ -134,7 +134,8 @@ public class GroupActionFactoryTest
             EasyMock.expect(request.getPathInfo()).andReturn("");
             EasyMock.expect(request.getInputStream()).andReturn(null);
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpPutFactory().createAction(request);
+
+            AbstractGroupAction action = GroupsActionFactory.httpPutFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof CreateGroupAction);
         }
@@ -153,7 +154,8 @@ public class GroupActionFactoryTest
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
+
+            AbstractGroupAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof DeleteGroupAction);
         }
@@ -172,7 +174,7 @@ public class GroupActionFactoryTest
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpGetFactory().createAction(request);
+            AbstractGroupAction action = GroupsActionFactory.httpGetFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof GetGroupAction);
         }
@@ -191,7 +193,7 @@ public class GroupActionFactoryTest
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpGetFactory().createAction(request);
+            AbstractGroupAction action = GroupsActionFactory.httpGetFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof GetGroupNamesAction);
         }
@@ -217,7 +219,7 @@ public class GroupActionFactoryTest
             EasyMock.expect(request.getServletPath()).andReturn("");
             EasyMock.expect(request.getInputStream()).andReturn(null);
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpPostFactory().createAction(request);
+            AbstractGroupAction action = GroupsActionFactory.httpPostFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof ModifyGroupAction);
         }
@@ -236,7 +238,7 @@ public class GroupActionFactoryTest
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName/groupMembers/groupToRenove");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
+            AbstractGroupAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof RemoveGroupMemberAction);
         }
@@ -256,7 +258,7 @@ public class GroupActionFactoryTest
             EasyMock.expect(request.getPathInfo()).andReturn("groupName/userMembers/userToRemove");
             EasyMock.expect(request.getParameter("idType")).andReturn("x509");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
+            AbstractGroupAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof RemoveUserMemberAction);
         }
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/AbstractUserActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/AbstractUserActionTest.java
new file mode 100644
index 0000000000000000000000000000000000000000..4885e98e5687b878074e5bd6efd850ba0cdb98ad
--- /dev/null
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/AbstractUserActionTest.java
@@ -0,0 +1,194 @@
+/*
+ ************************************************************************
+ *******************  CANADIAN ASTRONOMY DATA CENTRE  *******************
+ **************  CENTRE CANADIEN DE DONNÉES ASTRONOMIQUES  **************
+ *
+ *  (c) 2014.                            (c) 2014.
+ *  Government of Canada                 Gouvernement du Canada
+ *  National Research Council            Conseil national de recherches
+ *  Ottawa, Canada, K1A 0R6              Ottawa, Canada, K1A 0R6
+ *  All rights reserved                  Tous droits réservés
+ *
+ *  NRC disclaims any warranties,        Le CNRC dénie toute garantie
+ *  expressed, implied, or               énoncée, implicite ou légale,
+ *  statutory, of any kind with          de quelque nature que ce
+ *  respect to the software,             soit, concernant le logiciel,
+ *  including without limitation         y compris sans restriction
+ *  any warranty of merchantability      toute garantie de valeur
+ *  or fitness for a particular          marchande ou de pertinence
+ *  purpose. NRC shall not be            pour un usage particulier.
+ *  liable in any event for any          Le CNRC ne pourra en aucun cas
+ *  damages, whether direct or           être tenu responsable de tout
+ *  indirect, special or general,        dommage, direct ou indirect,
+ *  consequential or incidental,         particulier ou général,
+ *  arising from the use of the          accessoire ou fortuit, résultant
+ *  software.  Neither the name          de l'utilisation du logiciel. Ni
+ *  of the National Research             le nom du Conseil National de
+ *  Council of Canada nor the            Recherches du Canada ni les noms
+ *  names of its contributors may        de ses  participants ne peuvent
+ *  be used to endorse or promote        être utilisés pour approuver ou
+ *  products derived from this           promouvoir les produits dérivés
+ *  software without specific prior      de ce logiciel sans autorisation
+ *  written permission.                  préalable et particulière
+ *                                       par écrit.
+ *
+ *  This file is part of the             Ce fichier fait partie du projet
+ *  OpenCADC project.                    OpenCADC.
+ *
+ *  OpenCADC is free software:           OpenCADC est un logiciel libre ;
+ *  you can redistribute it and/or       vous pouvez le redistribuer ou le
+ *  modify it under the terms of         modifier suivant les termes de
+ *  the GNU Affero General Public        la “GNU Affero General Public
+ *  License as published by the          License” telle que publiée
+ *  Free Software Foundation,            par la Free Software Foundation
+ *  either version 3 of the              : soit la version 3 de cette
+ *  License, or (at your option)         licence, soit (à votre gré)
+ *  any later version.                   toute version ultérieure.
+ *
+ *  OpenCADC is distributed in the       OpenCADC est distribué
+ *  hope that it will be useful,         dans l’espoir qu’il vous
+ *  but WITHOUT ANY WARRANTY;            sera utile, mais SANS AUCUNE
+ *  without even the implied             GARANTIE : sans même la garantie
+ *  warranty of MERCHANTABILITY          implicite de COMMERCIALISABILITÉ
+ *  or FITNESS FOR A PARTICULAR          ni d’ADÉQUATION À UN OBJECTIF
+ *  PURPOSE.  See the GNU Affero         PARTICULIER. Consultez la Licence
+ *  General Public License for           Générale Publique GNU Affero
+ *  more details.                        pour plus de détails.
+ *
+ *  You should have received             Vous devriez avoir reçu une
+ *  a copy of the GNU Affero             copie de la Licence Générale
+ *  General Public License along         Publique GNU Affero avec
+ *  with OpenCADC.  If not, see          OpenCADC ; si ce n’est
+ *  <http://www.gnu.org/licenses/>.      pas le cas, consultez :
+ *                                       <http://www.gnu.org/licenses/>.
+ *
+ *  $Revision: 4 $
+ *
+ ************************************************************************
+ */
+package ca.nrc.cadc.ac.server.web.users;
+
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.net.TransientException;
+import ca.nrc.cadc.util.Log4jInit;
+
+import java.io.*;
+import java.security.AccessControlException;
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.Logger;
+
+import static org.easymock.EasyMock.*;
+import org.junit.BeforeClass;
+import org.junit.Test;
+
+/**
+ * @author jburke
+ */
+public class AbstractUserActionTest
+{
+    private final static Logger log = Logger.getLogger(AbstractUserActionTest.class);
+
+    @BeforeClass
+    public static void setUpClass()
+    {
+        Log4jInit.setLevel("ca.nrc.cadc.ac", Level.INFO);
+    }
+
+    @Test
+    public void testDoActionAccessControlException() throws Exception
+    {
+        String message = "Permission Denied";
+        int responseCode = 403;
+        Exception e = new AccessControlException("");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionIllegalArgumentException() throws Exception
+    {
+        String message = "message";
+        int responseCode = 400;
+        Exception e = new IllegalArgumentException("message");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionUserNotFoundException() throws Exception
+    {
+        String message = "User not found: foo";
+        int responseCode = 404;
+        Exception e = new UserNotFoundException("foo");
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionUnsupportedOperationException() throws Exception
+    {
+        String message = "Not yet implemented.";
+        int responseCode = 501;
+        Exception e = new UnsupportedOperationException();
+        testDoAction(message, responseCode, e);
+    }
+
+    @Test
+    public void testDoActionTransientException() throws Exception
+    {
+        HttpServletResponse response = createMock(HttpServletResponse.class);
+        expect(response.isCommitted()).andReturn(Boolean.FALSE);
+        response.setContentType("text/plain");
+        expectLastCall().once();
+        expect(response.getWriter())
+                .andReturn(new PrintWriter(new StringWriter())).once();
+
+        response.setStatus(503);
+        expectLastCall().once();
+        replay(response);
+
+        UsersActionImpl action = new UsersActionImpl();
+        action.setException(new TransientException("foo"));
+        action.doAction();
+    }
+
+    private void testDoAction(String message, int responseCode, Exception e)
+            throws Exception
+    {
+        HttpServletResponse response =
+                createMock(HttpServletResponse.class);
+        expect(response.isCommitted()).andReturn(Boolean.FALSE);
+        response.setContentType("text/plain");
+        expectLastCall().once();
+        expect(response.getWriter())
+                .andReturn(new PrintWriter(new StringWriter())).once();
+
+        response.setStatus(responseCode);
+        expectLastCall().once();
+        replay(response);
+
+        UsersActionImpl action = new UsersActionImpl();
+        action.setException(e);
+        action.doAction();
+    }
+
+    public class UsersActionImpl extends AbstractUserAction
+    {
+        Exception exception;
+
+        public UsersActionImpl()
+        {
+            super();
+        }
+
+        public void doAction() throws Exception
+        {
+            throw exception;
+        }
+
+        public void setException(Exception e)
+        {
+            this.exception = e;
+        }
+    }
+
+}
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserActionTest.java
index e309b17c0c05036d8704c71d6e412940d9736c7f..565a09d34be7e3e7b09ca1dda9f8ec683c0312ee 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserActionTest.java
@@ -68,8 +68,9 @@
 package ca.nrc.cadc.ac.server.web.users;
 
 import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.json.JsonUserWriter;
 import ca.nrc.cadc.ac.server.UserPersistence;
-import ca.nrc.cadc.ac.server.web.groups.GroupLogInfo;
+import ca.nrc.cadc.ac.xml.UserWriter;
 import ca.nrc.cadc.auth.HttpPrincipal;
 import org.junit.Test;
 
@@ -79,7 +80,6 @@ import java.io.StringWriter;
 import java.io.Writer;
 
 import static org.easymock.EasyMock.*;
-import static org.easymock.EasyMock.verify;
 import static org.junit.Assert.assertEquals;
 
 public class GetUserActionTest
@@ -116,13 +116,11 @@ public class GetUserActionTest
         testSubject.setResponse(mockResponse);
         testSubject.doAction();
 
-        assertEquals("Wrong XML output.",
-                     "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
-                     "<user>\r\n" +
-                     "  <userID>\r\n" +
-                     "    <identity type=\"HTTP\">CADCtest</identity>\r\n" +
-                     "  </userID>\r\n" +
-                     "</user>\r\n", writer.toString());
+        StringBuilder sb = new StringBuilder();
+        UserWriter userWriter = new UserWriter();
+        userWriter.write(user, sb);
+        assertEquals(sb.toString(), writer.toString());
+
         verify(mockResponse, mockUserPersistence);
     }
 
@@ -144,7 +142,7 @@ public class GetUserActionTest
             }
         };
 
-        testSubject.setAcceptedContentType(UsersAction.JSON_CONTENT_TYPE);
+        testSubject.setAcceptedContentType(AbstractUserAction.JSON_CONTENT_TYPE);
 
         final User<HttpPrincipal> user = new User<HttpPrincipal>(userID);
         final Writer writer = new StringWriter();
@@ -161,9 +159,11 @@ public class GetUserActionTest
         testSubject.setLogInfo(logInfo);
         testSubject.doAction();
 
-        assertEquals("Wrong JSON output.",
-                     "{\"user\":{\"userID\":{\"identity\":{\"name\":\"CADCtest\",\"type\":\"HTTP\"}}}}",
-                     writer.toString());
+        StringBuilder sb = new StringBuilder();
+        JsonUserWriter userWriter = new JsonUserWriter();
+        userWriter.write(user, sb);
+        assertEquals(sb.toString(), writer.toString());
+
         verify(mockResponse, mockUserPersistence);
     }
 }
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUsersActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserListActionTest.java
similarity index 73%
rename from projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUsersActionTest.java
rename to projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserListActionTest.java
index 74ea262be6f776c1d10a8ee6f1a0db66de2bf636..19e838932bce04647f2d24a6af98c01f4697adf7 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUsersActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/GetUserListActionTest.java
@@ -70,7 +70,9 @@ package ca.nrc.cadc.ac.server.web.users;
 
 
 import ca.nrc.cadc.ac.PersonalDetails;
+import ca.nrc.cadc.ac.json.JsonUserListWriter;
 import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.ac.xml.UserListWriter;
 import ca.nrc.cadc.auth.HttpPrincipal;
 import org.apache.log4j.Level;
 
@@ -82,8 +84,6 @@ import javax.servlet.http.HttpServletResponse;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.io.Writer;
-import java.util.ArrayList;
-import java.util.Collection;
 import java.util.HashMap;
 import java.util.Map;
 
@@ -98,7 +98,7 @@ import org.skyscreamer.jsonassert.JSONAssert;
  *
  * @author adriand
  */
-public class GetUsersActionTest
+public class GetUserListActionTest
 {
     @BeforeClass
     public static void setUpClass()
@@ -123,7 +123,7 @@ public class GetUsersActionTest
                             new PersonalDetails("USER", Integer.toString(i)));
         }
 
-        final GetUsersAction testSubject = new GetUsersAction()
+        final GetUserListAction testSubject = new GetUserListAction()
         {
             @Override
             UserPersistence<HttpPrincipal> getUserPersistence()
@@ -132,14 +132,14 @@ public class GetUsersActionTest
             }
         };
 
-        testSubject.setAcceptedContentType(UsersAction.JSON_CONTENT_TYPE);
+        testSubject.setAcceptedContentType(AbstractUserAction.JSON_CONTENT_TYPE);
 
-        final Writer writer = new StringWriter();
-        final PrintWriter printWriter = new PrintWriter(writer);
+        final Writer actualWriter = new StringWriter();
+        final PrintWriter actualPrintWriter = new PrintWriter(actualWriter);
 
         expect(mockUserPersistence.getUsers()).andReturn(
                 userEntries).once();
-        expect(mockResponse.getWriter()).andReturn(printWriter).once();
+        expect(mockResponse.getWriter()).andReturn(actualPrintWriter).once();
         mockResponse.setContentType("application/json");
         expectLastCall().once();
 
@@ -149,11 +149,12 @@ public class GetUsersActionTest
         testSubject.setLogInfo(logInfo);
         testSubject.doAction();
 
-        final JSONArray expected =
-                new JSONArray("[{\"id\":\"USER_1\",\"firstName\":\"USER\",\"lastName\":\"1\"},{\"id\":\"USER_3\",\"firstName\":\"USER\",\"lastName\":\"3\"},{\"id\":\"USER_2\",\"firstName\":\"USER\",\"lastName\":\"2\"},{\"id\":\"USER_4\",\"firstName\":\"USER\",\"lastName\":\"4\"},{\"id\":\"USER_5\",\"firstName\":\"USER\",\"lastName\":\"5\"}]");
-        final JSONArray result = new JSONArray(writer.toString());
+        final Writer expectedWriter = new StringWriter();
+        final PrintWriter expectedPrintWriter = new PrintWriter(expectedWriter);
+        JsonUserListWriter userListWriter = new JsonUserListWriter();
+        userListWriter.write(userEntries, expectedPrintWriter);
+        JSONAssert.assertEquals(expectedWriter.toString(), actualWriter.toString(), false);
 
-        JSONAssert.assertEquals(expected, result, true);
         verify(mockResponse, mockUserPersistence);
     }
 
@@ -174,7 +175,7 @@ public class GetUsersActionTest
                             new PersonalDetails("USER", Integer.toString(i)));
         }
 
-        final GetUsersAction testSubject = new GetUsersAction()
+        final GetUserListAction testSubject = new GetUserListAction()
         {
             @Override
             UserPersistence<HttpPrincipal> getUserPersistence()
@@ -183,12 +184,12 @@ public class GetUsersActionTest
             }
         };
 
-        final Writer writer = new StringWriter();
-        final PrintWriter printWriter = new PrintWriter(writer);
+        final Writer actualWriter = new StringWriter();
+        final PrintWriter actualPrintWriter = new PrintWriter(actualWriter);
 
         expect(mockUserPersistence.getUsers()).andReturn(
                 userEntries).once();
-        expect(mockResponse.getWriter()).andReturn(printWriter).once();
+        expect(mockResponse.getWriter()).andReturn(actualPrintWriter).once();
         mockResponse.setContentType("text/xml");
         expectLastCall().once();
 
@@ -198,32 +199,12 @@ public class GetUsersActionTest
         testSubject.setLogInfo(logInfo);
         testSubject.doAction();
 
-        final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
-                                "<users>\r\n" +
-                                "  <user id=\"USER_1\">\r\n" +
-                                "    <firstName>USER</firstName>\r\n" +
-                                "    <lastName>1</lastName>\r\n" +
-                                "  </user>\r\n" +
-                                "  <user id=\"USER_3\">\r\n" +
-                                "    <firstName>USER</firstName>\r\n" +
-                                "    <lastName>3</lastName>\r\n" +
-                                "  </user>\r\n" +
-                                "  <user id=\"USER_2\">\r\n" +
-                                "    <firstName>USER</firstName>\r\n" +
-                                "    <lastName>2</lastName>\r\n" +
-                                "  </user>\r\n" +
-                                "  <user id=\"USER_4\">\r\n" +
-                                "    <firstName>USER</firstName>\r\n" +
-                                "    <lastName>4</lastName>\r\n" +
-                                "  </user>\r\n" +
-                                "  <user id=\"USER_5\">\r\n" +
-                                "    <firstName>USER</firstName>\r\n" +
-                                "    <lastName>5</lastName>\r\n" +
-                                "  </user>\r\n" +
-                                "</users>\r\n";
-        final String result = writer.toString();
-
-        assertEquals("Wrong XML", expected, result);
+        final Writer expectedWriter = new StringWriter();
+        final PrintWriter expectedPrintWriter = new PrintWriter(expectedWriter);
+        UserListWriter userListWriter = new UserListWriter();
+        userListWriter.write(userEntries, expectedPrintWriter);
+        assertEquals("Wrong XML", expectedWriter.toString(), actualWriter.toString());
+
         verify(mockResponse, mockUserPersistence);
     }
 }
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/ModifyUserActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/ModifyUserActionTest.java
index 5ad9f24dfc4e195b954ed54722eebe96964ac5b7..00bfed3ffd0cf8124940725cd2e98d575f845d2c 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/ModifyUserActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/ModifyUserActionTest.java
@@ -68,20 +68,25 @@
 
 package ca.nrc.cadc.ac.server.web.users;
 
-
-import java.io.*;
-import java.security.Principal;
-
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-
 import ca.nrc.cadc.ac.PersonalDetails;
 import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.json.JsonUserWriter;
 import ca.nrc.cadc.ac.server.UserPersistence;
 import ca.nrc.cadc.auth.HttpPrincipal;
 import org.junit.Test;
-import static org.junit.Assert.*;
-import static org.easymock.EasyMock.*;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.ByteArrayInputStream;
+import java.io.InputStream;
+import java.security.Principal;
+
+import static org.easymock.EasyMock.createMock;
+import static org.easymock.EasyMock.expect;
+import static org.easymock.EasyMock.expectLastCall;
+import static org.easymock.EasyMock.replay;
+import static org.easymock.EasyMock.verify;
+import static org.junit.Assert.assertEquals;
 
 
 public class ModifyUserActionTest
@@ -89,9 +94,18 @@ public class ModifyUserActionTest
     @Test
     public void run() throws Exception
     {
-        final byte[] input =
-                "{\"user\":{\"userID\":{\"identity\":{\"name\":\"CADCtest\",\"type\":\"HTTP\"}},\"details\":[{\"userDetails\":{\"lastName\":\"Test\",\"firstName\":\"CADC\",\"email\":\"CADC.Test@nrc-cnrc.gc.ca\",\"type\":\"personalDetails\"}}],\"identities\":[{\"identity\":{\"name\":\"CADCtest\",\"type\":\"HTTP\"}}]}}"
-                        .getBytes();
+        final HttpPrincipal httpPrincipal = new HttpPrincipal("CADCtest");
+        User<Principal> expected = new User<Principal>(httpPrincipal);
+        expected.getIdentities().add(httpPrincipal);
+        final PersonalDetails pd = new PersonalDetails("CADC", "Test");
+        pd.email = "CADC.Test@nrc-cnrc.gc.ca";
+        expected.details.add(pd);
+
+        final StringBuilder sb = new StringBuilder();
+        final JsonUserWriter userWriter = new JsonUserWriter();
+        userWriter.write(expected, sb);
+
+        final byte[] input = sb.toString().getBytes();
         final InputStream inputStream = new ByteArrayInputStream(input);
 
         // Should match the JSON above, without the e-mail modification.
@@ -123,6 +137,7 @@ public class ModifyUserActionTest
         expectLastCall().once();
 
         mockResponse.setStatus(200);
+
         expectLastCall().once();
 
         mockResponse.setContentType("application/json");
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserActionFactoryTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserActionFactoryTest.java
index b833d66005d722cf0bf4d72c05fdcd6a22b48cfd..131d167a64f5ef259a1fccc97d94ef3523a86d59 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserActionFactoryTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserActionFactoryTest.java
@@ -94,8 +94,7 @@ public class UserActionFactoryTest
             EasyMock.expect(request.getPathInfo()).andReturn("");
             EasyMock.expect(request.getInputStream()).andReturn(null);
             EasyMock.replay(request);
-
-            UsersAction action = UsersActionFactory.httpPutFactory().createAction(request);
+            AbstractUserAction action = UserActionFactory.httpPutFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof CreateUserAction);
         }
@@ -115,7 +114,7 @@ public class UserActionFactoryTest
             EasyMock.expect(request.getPathInfo()).andReturn("userName");
             EasyMock.expect(request.getParameter("idType")).andReturn("sessionID");
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.httpDeleteFactory().createAction(request);
+            AbstractUserAction action = UserActionFactory.httpDeleteFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof DeleteUserAction);
         }
@@ -135,7 +134,7 @@ public class UserActionFactoryTest
             EasyMock.expect(request.getPathInfo()).andReturn("userName");
             EasyMock.expect(request.getParameter("idType")).andReturn("sessionID");
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.httpGetFactory().createAction(request);
+            AbstractUserAction action = UserActionFactory.httpGetFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof GetUserAction);
         }
@@ -154,9 +153,9 @@ public class UserActionFactoryTest
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("");
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.httpGetFactory().createAction(request);
+            AbstractUserAction action = UserActionFactory.httpGetFactory().createAction(request);
             EasyMock.verify(request);
-            Assert.assertTrue("Wrong action", action instanceof GetUsersAction);
+            Assert.assertTrue("Wrong action", action instanceof GetUserListAction);
         }
         catch (Throwable t)
         {
@@ -181,7 +180,7 @@ public class UserActionFactoryTest
             EasyMock.expect(request.getInputStream()).andReturn(null);
             //EasyMock.expect(request.getParameter("idType")).andReturn("sessionID");
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.httpPostFactory().createAction(request);
+            AbstractUserAction action = UserActionFactory.httpPostFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof ModifyUserAction);
         }
@@ -202,7 +201,7 @@ public class UserActionFactoryTest
             EasyMock.replay(request);
             try
             {
-                UsersActionFactory.httpPostFactory().createAction(request);
+                UserActionFactory.httpPostFactory().createAction(request);
                 Assert.fail("Should have been a bad request");
             }
             catch (IllegalArgumentException e)
@@ -228,7 +227,7 @@ public class UserActionFactoryTest
             EasyMock.replay(request);
             try
             {
-                UsersActionFactory.httpDeleteFactory().createAction(request);
+                UserActionFactory.httpDeleteFactory().createAction(request);
                 Assert.fail("Should have been a bad request");
             }
             catch (IllegalArgumentException e)
@@ -253,7 +252,7 @@ public class UserActionFactoryTest
             EasyMock.replay(request);
             try
             {
-                UsersActionFactory.httpHeadFactory().createAction(request);
+                UserActionFactory.httpHeadFactory().createAction(request);
                 Assert.fail("Should have been a bad request");
             }
             catch (UnsupportedOperationException e)
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersServletTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserServletTest.java
similarity index 88%
rename from projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersServletTest.java
rename to projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserServletTest.java
index 8e0a27f5b988c9cecbdcbd567d418c8825e4671e..3ab00e88c44a487a6367aec89f39d44ecb9142e9 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersServletTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UserServletTest.java
@@ -8,14 +8,14 @@ import static org.easymock.EasyMock.*;
 import static org.junit.Assert.*;
 
 
-public class UsersServletTest
+public class UserServletTest
 {
     @Test
     public void getAcceptedContentTypeJSON() throws Exception
     {
         final HttpServletRequest mockRequest =
                 createMock(HttpServletRequest.class);
-        final UsersServlet testSubject = new UsersServlet();
+        final UserServlet testSubject = new UserServlet();
 
         expect(mockRequest.getHeader("Accept")).
                 andReturn("application/json").once();
@@ -33,7 +33,7 @@ public class UsersServletTest
     {
         final HttpServletRequest mockRequest =
                 createMock(HttpServletRequest.class);
-        final UsersServlet testSubject = new UsersServlet();
+        final UserServlet testSubject = new UserServlet();
 
         expect(mockRequest.getHeader("Accept")).andReturn(null).once();
 
diff --git a/projects/cadcAccessControl/build.xml b/projects/cadcAccessControl/build.xml
index 3296ce950c082b9ba95b90a8d517461a1d0ca14f..82f0e2c62be27a1548a145a699f0695183afbcd7 100644
--- a/projects/cadcAccessControl/build.xml
+++ b/projects/cadcAccessControl/build.xml
@@ -117,7 +117,7 @@
     
     <property name="testingJars" value="${build}/class:${jars}:${xerces}:${asm}:${cglib}:${easymock}:${junit}:${objenesis}" />
 
-    <target name="test" depends="compile,compile-test">
+    <target name="single-test" depends="compile,compile-test">
         <echo message="Running test suite..." />
         <junit printsummary="yes" haltonfailure="yes" fork="yes">
             <classpath>
@@ -125,10 +125,6 @@
                 <pathelement path="${build}/test/class"/>
                 <pathelement path="${testingJars}"/>
             </classpath>
-            <test name="ca.nrc.cadc.ac.json.GroupPropertyReaderWriterTest" />
-            <test name="ca.nrc.cadc.ac.json.UserDetailsReaderWriterTest" />
-            <test name="ca.nrc.cadc.ac.json.IdentityReaderWriterTest" />
-            <test name="ca.nrc.cadc.ac.json.UserReaderWriterTest" />
             <test name="ca.nrc.cadc.ac.json.GroupReaderWriterTest" />
             <formatter type="plain" usefile="false" />
         </junit>
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/UserAlreadyExistsException.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserAlreadyExistsException.java
similarity index 100%
rename from projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/UserAlreadyExistsException.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserAlreadyExistsException.java
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserRequest.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserRequest.java
index d1c28cfac115e291be92c9fc60618f32de33113f..664b18d2a99d3f39e2927e9e7e6e89986371d2b6 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserRequest.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/UserRequest.java
@@ -74,15 +74,15 @@ import java.security.Principal;
 public class UserRequest<T extends Principal>
 {
     private User<T> user;
-    private String password;
+    private char[] password;
 
-    public UserRequest(final User<T> user, final String password)
+    public UserRequest(final User<T> user, final char[] password)
     {
         if (user == null)
         {
             throw new IllegalArgumentException("null user");
         }
-        if (password == null || password.isEmpty())
+        if (password == null || password.length == 0)
         {
             throw new IllegalArgumentException("null or empty password");
         }
@@ -95,11 +95,19 @@ public class UserRequest<T extends Principal>
         return this.user;
     }
 
-    public String getPassword()
+    public char[] getPassword()
     {
         return this.password;
     }
 
+    public void clear()
+    {
+        for (int i = 0; i < password.length; i++)
+        {
+            password[i] = '0';
+        }
+    }
+
     @Override
     public boolean equals(Object o)
     {
@@ -115,7 +123,6 @@ public class UserRequest<T extends Principal>
         UserRequest<?> that = (UserRequest<?>) o;
 
         return user.equals(that.user);
-
     }
 
     @Override
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 8f4eab02036b1155294d80566c74a434875ad142..bad17d1f5f80cee819082265ca7f344940ac14b1 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/GMSClient.java
@@ -68,7 +68,34 @@
  */
 package ca.nrc.cadc.ac.client;
 
-import java.io.*;
+import ca.nrc.cadc.ac.Group;
+import ca.nrc.cadc.ac.GroupAlreadyExistsException;
+import ca.nrc.cadc.ac.GroupNotFoundException;
+import ca.nrc.cadc.ac.Role;
+import ca.nrc.cadc.ac.User;
+import ca.nrc.cadc.ac.UserNotFoundException;
+import ca.nrc.cadc.ac.xml.GroupListReader;
+import ca.nrc.cadc.ac.xml.GroupReader;
+import ca.nrc.cadc.ac.xml.GroupWriter;
+import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.auth.HttpPrincipal;
+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.InputStreamWrapper;
+import ca.nrc.cadc.net.NetUtil;
+import org.apache.log4j.Logger;
+
+import javax.net.ssl.HttpsURLConnection;
+import javax.net.ssl.SSLSocketFactory;
+import javax.security.auth.Subject;
+import java.io.BufferedReader;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
 import java.net.HttpURLConnection;
 import java.net.MalformedURLException;
 import java.net.URL;
@@ -84,27 +111,6 @@ import java.util.List;
 import java.util.Map;
 import java.util.Set;
 
-import javax.net.ssl.HttpsURLConnection;
-import javax.net.ssl.SSLSocketFactory;
-import javax.security.auth.Subject;
-
-import ca.nrc.cadc.ac.*;
-import ca.nrc.cadc.auth.HttpPrincipal;
-import ca.nrc.cadc.util.StringUtil;
-import org.apache.log4j.Logger;
-
-import ca.nrc.cadc.ac.xml.GroupReader;
-import ca.nrc.cadc.ac.xml.GroupWriter;
-import ca.nrc.cadc.ac.xml.GroupsReader;
-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.InputStreamWrapper;
-import ca.nrc.cadc.net.NetUtil;
-import org.json.JSONObject;
-
 
 /**
  * Client class for performing group searching and group actions
@@ -217,7 +223,7 @@ public class GMSClient
     {
         final URL usersListURL = new URL(this.baseURL + "/users");
         return new HttpDownload(usersListURL,
-                                new JSONUserListInputStreamWrapper(webUsers));
+                                new JsonUserListInputStreamWrapper(webUsers));
     }
 
     /**
@@ -242,7 +248,8 @@ public class GMSClient
         clearCache();
 
         StringBuilder groupXML = new StringBuilder();
-        GroupWriter.write(group, groupXML);
+        GroupWriter groupWriter = new GroupWriter();
+        groupWriter.write(group, groupXML);
         log.debug("createGroup: " + groupXML);
 
         byte[] bytes = groupXML.toString().getBytes("UTF-8");
@@ -283,7 +290,8 @@ public class GMSClient
         try
         {
             log.debug("createGroup returned: " + retXML);
-            return GroupReader.read(retXML);
+            GroupReader groupReader = new GroupReader();
+            return groupReader.read(retXML);
         }
         catch (Exception bug)
         {
@@ -339,7 +347,8 @@ public class GMSClient
         {
             String groupXML = new String(out.toByteArray(), "UTF-8");
             log.debug("getGroup returned: " + groupXML);
-            return GroupReader.read(groupXML);
+            GroupReader groupReader = new GroupReader();
+            return groupReader.read(groupXML);
         }
         catch (Exception bug)
         {
@@ -441,7 +450,8 @@ public class GMSClient
         clearCache();
 
         StringBuilder groupXML = new StringBuilder();
-        GroupWriter.write(group, groupXML);
+        GroupWriter groupWriter = new GroupWriter();
+        groupWriter.write(group, groupXML);
         log.debug("updateGroup: " + groupXML);
 
         HttpPost transfer = new HttpPost(updateGroupURL, groupXML.toString(),
@@ -482,7 +492,8 @@ public class GMSClient
         {
             String retXML = transfer.getResponseBody();
             log.debug("getGroup returned: " + retXML);
-            return GroupReader.read(retXML);
+            GroupReader groupReader = new GroupReader();
+            return groupReader.read(retXML);
         }
         catch (Exception bug)
         {
@@ -887,7 +898,8 @@ public class GMSClient
         {
             String groupsXML = new String(out.toByteArray(), "UTF-8");
             log.debug("getMemberships returned: " + groupsXML);
-            List<Group> groups = GroupsReader.read(groupsXML);
+            GroupListReader groupListReader = new GroupListReader();
+            List<Group> groups = groupListReader.read(groupsXML);
             setCachedGroups(userID, groups, role);
             return groups;
         }
@@ -1004,7 +1016,8 @@ public class GMSClient
         {
             String groupsXML = new String(out.toByteArray(), "UTF-8");
             log.debug("getMembership returned: " + groupsXML);
-            List<Group> groups = GroupsReader.read(groupsXML);
+            GroupListReader groupListReader = new GroupListReader();
+            List<Group> groups = groupListReader.read(groupsXML);
             if (groups.size() == 0)
             {
                 return null;
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapper.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapper.java
similarity index 96%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapper.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapper.java
index 8e3a47447965467f2f822dc5a532fff1ab0571a0..f00b0aeef2821f4740ae2eab7130b33d0001009f 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapper.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapper.java
@@ -82,15 +82,15 @@ import java.io.InputStream;
 import java.io.InputStreamReader;
 import java.util.List;
 
-public class JSONUserListInputStreamWrapper implements InputStreamWrapper
+public class JsonUserListInputStreamWrapper implements InputStreamWrapper
 {
     private static final Logger LOGGER = Logger
-            .getLogger(JSONUserListInputStreamWrapper.class);
+            .getLogger(JsonUserListInputStreamWrapper.class);
     private final List<User<HttpPrincipal>> output;
 
 
-    public JSONUserListInputStreamWrapper(
-            final List<User<HttpPrincipal>> output)
+    public JsonUserListInputStreamWrapper(
+        final List<User<HttpPrincipal>> output)
     {
         this.output = output;
     }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupReader.java
similarity index 96%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/GroupReader.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupReader.java
index f726b87a5ea426ffd94bbf914b1da5124bb533ee..9d8055828d911f509543906fd9106ba9f92d706e 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/GroupReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupReader.java
@@ -87,7 +87,7 @@ import java.text.DateFormat;
 import java.text.ParseException;
 import java.util.Scanner;
 
-public class GroupReader
+public class JsonGroupReader
 {
     /**
      * Construct a Group from a InputStream.
@@ -186,7 +186,7 @@ public class GroupReader
         {
             JSONObject ownerObject = groupObject.getJSONObject("owner");
             JSONObject userObject = ownerObject.getJSONObject("user");
-            user = UserReader.parseUser(userObject);
+            user = JsonUserReader.parseUser(userObject);
         }
 
         Group group = new Group(groupID, user);
@@ -221,7 +221,7 @@ public class GroupReader
             {
                 JSONObject propertiesObject = propertiesArray.getJSONObject(i);
                 JSONObject propertyObject = propertiesObject.getJSONObject("property");
-                group.getProperties().add(GroupPropertyReader.read(propertyObject));
+                group.getProperties().add(JsonGroupPropertyReader.read(propertyObject));
             }
         }
 
@@ -245,7 +245,7 @@ public class GroupReader
             {
                 JSONObject userMemberObject = userMembersArray.getJSONObject(i);
                 JSONObject userObject = userMemberObject.getJSONObject("user");
-                group.getUserMembers().add(UserReader.parseUser(userObject));
+                group.getUserMembers().add(JsonUserReader.parseUser(userObject));
             }
         }
 
@@ -269,7 +269,7 @@ public class GroupReader
             {
                 JSONObject userAdminObject = userAdminsArray.getJSONObject(i);
                 JSONObject userObject = userAdminObject.getJSONObject("user");
-                group.getUserAdmins().add(UserReader.parseUser(userObject));
+                group.getUserAdmins().add(JsonUserReader.parseUser(userObject));
             }
         }
 
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/GroupWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupWriter.java
similarity index 96%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/GroupWriter.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupWriter.java
index d5a1f1b684a75eb1f3fedd04286df23e95cac6e5..aa78fa68a379ac903cc63fe005bd66d3fecdf699 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/GroupWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonGroupWriter.java
@@ -88,7 +88,7 @@ import java.io.Writer;
 import java.security.Principal;
 import java.text.DateFormat;
 
-public class GroupWriter
+public class JsonGroupWriter
 {
     /**
      * Write a Group to a StringBuilder.
@@ -175,7 +175,7 @@ public class GroupWriter
         // Group owner
         if (group.getOwner() != null)
         {
-            groupObject.put("owner", UserWriter.getUserObject(group.getOwner()));
+            groupObject.put("owner", JsonUserWriter.getUserObject(group.getOwner()));
         }
 
         if (deepCopy)
@@ -200,7 +200,7 @@ public class GroupWriter
                 for (GroupProperty property : group.getProperties())
                 {
                     JSONObject propertyObject = new JSONObject();
-                    propertyObject.put("property", GroupPropertyWriter.write(property));
+                    propertyObject.put("property", JsonGroupPropertyWriter.write(property));
                     propertiesArray.put(propertyObject);
                 }
                 groupObject.put("properties", propertiesArray);
@@ -223,7 +223,7 @@ public class GroupWriter
                 JSONArray userMembersArray = new JSONArray();
                 for (User<? extends Principal> userMember : group.getUserMembers())
                 {
-                    userMembersArray.put(UserWriter.getUserObject(userMember));
+                    userMembersArray.put(JsonUserWriter.getUserObject(userMember));
                 }
                 groupObject.put("userMembers", userMembersArray);
             }
@@ -245,7 +245,7 @@ public class GroupWriter
                 JSONArray userAdminsArray = new JSONArray();
                 for (User<? extends Principal> userAdmin : group.getUserAdmins())
                 {
-                    userAdminsArray.put(UserWriter.getUserObject(userAdmin));
+                    userAdminsArray.put(JsonUserWriter.getUserObject(userAdmin));
                 }
                 groupObject.put("userAdmins", userAdminsArray);
             }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UsersWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListWriter.java
similarity index 99%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UsersWriter.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListWriter.java
index 308148bcdadde1530c994bc1bea70a7a61f9cd14..5bf9317a44b6c30c61b50f569471f652dc875daa 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UsersWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserListWriter.java
@@ -80,7 +80,7 @@ import java.util.Map;
 /**
  * Class to write out, as JSON, a list of user entries.
  */
-public class UsersWriter
+public class JsonUserListWriter
 {
     public static void write(final Map<String, PersonalDetails> users,
                              final Writer writer) throws IOException
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserReader.java
similarity index 96%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserReader.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserReader.java
index b6aedd7f92293b5f7c49580ff44fd5ef20078567..f2d9db38d3c5459c8d86b0cf2f7c9d9f870d5230 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserReader.java
@@ -81,7 +81,7 @@ import java.io.Reader;
 import java.security.Principal;
 import java.util.Scanner;
 
-public class UserReader
+public class JsonUserReader
 {
     /**
      * Construct a User from a InputStream.
@@ -162,7 +162,7 @@ public class UserReader
         JSONObject userIDObject = userObject.getJSONObject("userID");
         JSONObject userIDIdentityObject = userIDObject.getJSONObject("identity");
 
-        Principal userID = IdentityReader.read(userIDIdentityObject);
+        Principal userID = JsonIdentityReader.read(userIDIdentityObject);
         User<Principal> user = new User<Principal>(userID);
 
         // identities
@@ -173,7 +173,7 @@ public class UserReader
             {
                 JSONObject identitiesObject = identitiesArray.getJSONObject(i);
                 JSONObject identityObject = identitiesObject.getJSONObject(("identity"));
-                user.getIdentities().add(IdentityReader.read(identityObject));
+                user.getIdentities().add(JsonIdentityReader.read(identityObject));
             }
         }
 
@@ -185,7 +185,7 @@ public class UserReader
             {
                 JSONObject detailsObject = detailsArray.getJSONObject(i);
                 JSONObject userDetailsObject = detailsObject.getJSONObject(UserDetails.NAME);
-                user.details.add(UserDetailsReader.read(userDetailsObject));
+                user.details.add(JsonUserDetailsReader.read(userDetailsObject));
             }
         }
 
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserRequestReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestReader.java
similarity index 97%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserRequestReader.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestReader.java
index 4f246e9df6e9d07618244a51eb960b8192d8a3f2..2a5c8d394e39520237c503d6937a50445be0b460 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserRequestReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserRequestReader.java
@@ -78,7 +78,7 @@ import java.io.*;
 import java.security.Principal;
 import java.util.Scanner;
 
-public class UserRequestReader
+public class JsonUserRequestReader
 {
     /**
      * Construct a UserRequest from an JSON String source.
@@ -159,10 +159,10 @@ public class UserRequestReader
         throws ReaderException, JSONException
     {
         final User<Principal> user =
-                ca.nrc.cadc.ac.json.UserReader.parseUser(
-                        userRequestObject.getJSONObject("user"));
+                JsonUserReader.parseUser(
+                    userRequestObject.getJSONObject("user"));
 
         return new UserRequest<Principal>(user, userRequestObject.
-                getString("password"));
+                getString("password").toCharArray());
     }
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserWriter.java
similarity index 96%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserWriter.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserWriter.java
index 2865235807f885d24cbe3986b370c50e5bf8d753..41da645e0ad3cac0a85bcfcde103520644bc5fd4 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/UserWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/json/JsonUserWriter.java
@@ -85,7 +85,7 @@ import java.io.Writer;
 import java.security.Principal;
 import java.util.Set;
 
-public class UserWriter
+public class JsonUserWriter
 {
     /**
      * Write a User as a JSON string to a StringBuilder.
@@ -164,7 +164,7 @@ public class UserWriter
     {
         JSONObject userObject = new JSONObject();
         JSONObject userIDIdentityObject = new JSONObject();
-        userIDIdentityObject.put("identity", IdentityWriter.write(user.getUserID()));
+        userIDIdentityObject.put("identity", JsonIdentityWriter.write(user.getUserID()));
         userObject.put("userID", userIDIdentityObject);
 
         // identities
@@ -175,7 +175,7 @@ public class UserWriter
             for (Principal identity : identities)
             {
                 JSONObject identityObject = new JSONObject();
-                identityObject.put("identity" , IdentityWriter.write(identity));
+                identityObject.put("identity" , JsonIdentityWriter.write(identity));
                 identityArray.put(identityObject);
             }
             userObject.put("identities", identityArray);
@@ -189,7 +189,7 @@ public class UserWriter
             for (UserDetails userDetail : userDetails)
             {
                 JSONObject detailsObject = new JSONObject();
-                detailsObject.put(UserDetails.NAME , UserDetailsWriter.write(userDetail));
+                detailsObject.put(UserDetails.NAME , JsonUserDetailsWriter.write(userDetail));
                 detailsArray.put(detailsObject);
             }
             userObject.put("details", detailsArray);
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupsReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListReader.java
similarity index 95%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupsReader.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListReader.java
index c0c31fbe4a2ecfbbb7f44e0baf411cf15f08745c..cd962918e443f8d3db33cd7eead08d475285543d 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupsReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListReader.java
@@ -84,7 +84,11 @@ import org.jdom2.Document;
 import org.jdom2.Element;
 import org.jdom2.JDOMException;
 
-public class GroupsReader
+/**
+ * Class to read an XML representation of a list of Groups
+ * into a List of Group objects.
+ */
+public class GroupListReader
 {
     /**
      * Construct a list of Group's from an XML String source.
@@ -95,7 +99,7 @@ public class GroupsReader
      * @throws java.io.IOException
      * @throws java.net.URISyntaxException
      */
-    public static List<Group> read(String xml)
+    public List<Group> read(String xml)
         throws ReaderException, IOException, URISyntaxException
     {
         if (xml == null)
@@ -114,7 +118,7 @@ public class GroupsReader
      * @throws java.io.IOException
      * @throws java.net.URISyntaxException
      */
-    public static List<Group> read(InputStream in)
+    public List<Group> read(InputStream in)
         throws ReaderException, IOException, URISyntaxException
     {
         if (in == null)
@@ -142,7 +146,7 @@ public class GroupsReader
      * @throws java.io.IOException
      * @throws java.net.URISyntaxException
      */
-    public static List<Group> read(Reader reader)
+    public List<Group> read(Reader reader)
         throws ReaderException, IOException, URISyntaxException
     {
         if (reader == null)
@@ -182,7 +186,7 @@ public class GroupsReader
         List<Element> groupElements = groupsElement.getChildren("group");
         for (Element groupElement : groupElements)
         {
-            groups.add(ca.nrc.cadc.ac.xml.GroupReader.parseGroup(groupElement));
+            groups.add(GroupReader.parseGroup(groupElement));
         }
 
         return groups;
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupsWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListWriter.java
similarity index 90%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupsWriter.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListWriter.java
index fb92d2cd268c54985b379954a90493888043de8e..b3707fb3d7ce6bb3e2f3c16a6e9f59c01606b786 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupsWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupListWriter.java
@@ -15,7 +15,10 @@ import org.jdom2.Element;
 import org.jdom2.output.Format;
 import org.jdom2.output.XMLOutputter;
 
-public class GroupsWriter
+/**
+ * Class to write a XML representation from a Collection of Groups objects.
+ */
+public class GroupListWriter
 {
     /**
      * Write a List of Group's to a StringBuilder.
@@ -24,7 +27,7 @@ public class GroupsWriter
      * @throws java.io.IOException
      * @throws WriterException
      */
-    public static void write(Collection<Group> groups, StringBuilder builder)
+    public void write(Collection<Group> groups, StringBuilder builder)
         throws IOException, WriterException
     {
         write(groups, new StringBuilderWriter(builder));
@@ -38,7 +41,7 @@ public class GroupsWriter
      * @throws IOException if the writer fails to write.
      * @throws WriterException
      */
-    public static void write(Collection<Group> groups, OutputStream out)
+    public void write(Collection<Group> groups, OutputStream out)
         throws IOException, WriterException
     {
         OutputStreamWriter outWriter;
@@ -61,7 +64,7 @@ public class GroupsWriter
      * @throws IOException if the writer fails to write.
      * @throws WriterException
      */
-    public static void write(Collection<Group> groups, Writer writer)
+    public void write(Collection<Group> groups, Writer writer)
         throws IOException, WriterException
     {
         if (groups == null)
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupReader.java
index 8cfc37eec446e677bb9f648cfe4a52d2dc82f2b9..8a66c7e95aef3e98abffdb2906bb0bf369a9f4a6 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupReader.java
@@ -90,6 +90,9 @@ import java.text.DateFormat;
 import java.text.ParseException;
 import java.util.List;
 
+/**
+ * Class to read a XML representation of a Group to a Group object.
+ */
 public class GroupReader
 {
 
@@ -102,7 +105,7 @@ public class GroupReader
      * @throws java.io.IOException
      * @throws java.net.URISyntaxException
      */
-    public static Group read(String xml)
+    public Group read(String xml)
         throws ReaderException, IOException, URISyntaxException
     {
         if (xml == null)
@@ -121,7 +124,7 @@ public class GroupReader
      * @throws java.io.IOException
      * @throws java.net.URISyntaxException
      */
-    public static Group read(InputStream in)
+    public Group read(InputStream in)
         throws ReaderException, IOException
     {
         if (in == null)
@@ -149,7 +152,7 @@ public class GroupReader
      * @throws java.io.IOException
      * @throws java.net.URISyntaxException
      */
-    public static Group read(Reader reader)
+    public Group read(Reader reader)
         throws ReaderException, IOException
     {
         if (reader == null)
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupWriter.java
index 18f307a80afea5fb5552e34f2b9c9b338e871c1b..380ba79a8d7ec2fa8e203f8e7f6d8bafbb51ff0e 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/GroupWriter.java
@@ -90,6 +90,9 @@ import java.io.Writer;
 import java.security.Principal;
 import java.text.DateFormat;
 
+/**
+ * Class to write a XML representation of a Group object.
+ */
 public class GroupWriter
 {
     /**
@@ -99,7 +102,7 @@ public class GroupWriter
      * @throws java.io.IOException
      * @throws WriterException
      */
-    public static void write(Group group, StringBuilder builder)
+    public void write(Group group, StringBuilder builder)
         throws IOException, WriterException
     {
         write(group, new StringBuilderWriter(builder));
@@ -113,7 +116,7 @@ public class GroupWriter
      * @throws IOException if the writer fails to write.
      * @throws WriterException
      */
-    public static void write(Group group, OutputStream out)
+    public void write(Group group, OutputStream out)
         throws IOException, WriterException
     {
         OutputStreamWriter outWriter;
@@ -136,7 +139,7 @@ public class GroupWriter
      * @throws IOException if the writer fails to write.
      * @throws WriterException
      */
-    public static void write(Group group, Writer writer)
+    public void write(Group group, Writer writer)
         throws IOException, WriterException
     {
         if (group == null)
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UsersWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListWriter.java
similarity index 96%
rename from projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UsersWriter.java
rename to projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListWriter.java
index 757af4901c0a2572ac563350ca76e142dd7d12ea..f08d4de4c4ba371da692b858c25e51198cb6abe0 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UsersWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserListWriter.java
@@ -78,7 +78,10 @@ import java.io.IOException;
 import java.io.Writer;
 import java.util.Map;
 
-public class UsersWriter
+/**
+ * Class to write a XML representation of a Collection of User's.
+ */
+public class UserListWriter
 {
     /**
      * Write the Map of User entries as XML.
@@ -87,8 +90,8 @@ public class UsersWriter
      * @param writer            The Writer to output to.
      * @throws IOException      Any writing errors.
      */
-    public static void write(final Map<String, PersonalDetails> users,
-                             final Writer writer) throws IOException
+    public void write(final Map<String, PersonalDetails> users,
+                      final Writer writer) throws IOException
     {
         // Create the root users Element.
         final Element usersElement = new Element("users");
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java
index e9b957c29ed85fcc8133c781a2b8618e15650116..7cceba392b1852cedacbe10a1f25bd04a06492bf 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserReader.java
@@ -85,6 +85,9 @@ import java.net.URISyntaxException;
 import java.security.Principal;
 import java.util.List;
 
+/**
+ * Class to read a XML representation of a User to a User object.
+ */
 public class UserReader
 {
     /**
@@ -96,7 +99,7 @@ public class UserReader
      * @throws java.io.IOException
      * @throws java.net.URISyntaxException
      */
-    public static User<Principal> read(String xml)
+    public User<Principal> read(String xml)
         throws IOException, URISyntaxException
     {
         if (xml == null)
@@ -113,7 +116,7 @@ public class UserReader
      * @return User User.
      * @throws java.io.IOException
      */
-    public static User<Principal> read(InputStream in)
+    public User<Principal> read(InputStream in)
         throws IOException
     {
         if (in == null)
@@ -140,7 +143,7 @@ public class UserReader
      * @throws ReaderException
      * @throws java.io.IOException
      */
-    public static User<Principal> read(Reader reader)
+    public User<Principal> read(Reader reader)
         throws IOException
     {
         if (reader == null)
@@ -185,7 +188,8 @@ public class UserReader
             throw new ReaderException(error);
         }
 
-        Principal userID = IdentityReader.read(userIDIdentityElement);
+        IdentityReader identityReader = new IdentityReader();
+        Principal userID = identityReader.read(userIDIdentityElement);
 
         User<Principal> user = new User<Principal>(userID);
 
@@ -196,7 +200,7 @@ public class UserReader
             List<Element> identityElements = identitiesElement.getChildren("identity");
             for (Element identityElement : identityElements)
             {
-                user.getIdentities().add(IdentityReader.read(identityElement));
+                user.getIdentities().add(identityReader.read(identityElement));
             }
 
         }
@@ -205,10 +209,11 @@ public class UserReader
         Element detailsElement = userElement.getChild("details");
         if (detailsElement != null)
         {
+            UserDetailsReader userDetailsReader = new UserDetailsReader();
             List<Element> userDetailsElements = detailsElement.getChildren("userDetails");
             for (Element userDetailsElement : userDetailsElements)
             {
-                user.details.add(UserDetailsReader.read(userDetailsElement));
+                user.details.add(userDetailsReader.read(userDetailsElement));
             }
         }
 
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java
index da30cafda3aa6e952fa5eec3682318f9543b5555..e44eba1e4dc37fe8f9fa81b0f7b9629bf271e65e 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestReader.java
@@ -84,6 +84,9 @@ import org.jdom2.Document;
 import org.jdom2.Element;
 import org.jdom2.JDOMException;
 
+/**
+ * Class to read a XML representation of a UserRequest to a UserRequest object.
+ */
 public class UserRequestReader
 {
     /**
@@ -93,7 +96,7 @@ public class UserRequestReader
      * @return UserRequest UserRequest.
      * @throws java.io.IOException
      */
-    public static UserRequest<Principal> read(String xml)
+    public UserRequest<Principal> read(String xml)
         throws IOException
     {
         if (xml == null)
@@ -111,7 +114,7 @@ public class UserRequestReader
      * @throws ReaderException
      * @throws java.io.IOException
      */
-    public static UserRequest<Principal> read(InputStream in)
+    public UserRequest<Principal> read(InputStream in)
         throws IOException
     {
         if (in == null)
@@ -138,7 +141,7 @@ public class UserRequestReader
      * @throws ReaderException
      * @throws java.io.IOException
      */
-    public static UserRequest<Principal> read(Reader reader)
+    public UserRequest<Principal> read(Reader reader)
         throws IOException
     {
         if (reader == null)
@@ -186,6 +189,6 @@ public class UserRequestReader
         }
         String password = passwordElement.getText();
 
-        return new UserRequest<Principal>(user, password);
+        return new UserRequest<Principal>(user, password.toCharArray());
     }
 }
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestWriter.java
index 10aaf852a0fd05e5add47518984e50ce132856cc..fe0ffee96a6fbbd1a9e9a70f77d575dfbdf0a629 100644
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserRequestWriter.java
@@ -81,6 +81,9 @@ import java.io.IOException;
 import java.io.Writer;
 import java.security.Principal;
 
+/**
+ * Class to write a XML representation of a UserRequest object.
+ */
 public class UserRequestWriter
 {
     /**
@@ -91,7 +94,7 @@ public class UserRequestWriter
      * @throws java.io.IOException if the writer fails to write.
      * @throws WriterException
      */
-    public static void write(UserRequest<? extends Principal> userRequest, StringBuilder builder)
+    public void write(UserRequest<? extends Principal> userRequest, StringBuilder builder)
         throws IOException, WriterException
     {
         write(userRequest, new StringBuilderWriter(builder));
@@ -135,7 +138,7 @@ public class UserRequestWriter
 
         // password element
         Element passwordElement = new Element("password");
-        passwordElement.setText(userRequest.getPassword());
+        passwordElement.setText(String.valueOf(userRequest.getPassword()));
         userRequestElement.addContent(passwordElement);
 
         return userRequestElement;
diff --git a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserWriter.java b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserWriter.java
index b1d6642de691ebc47e3516054b08bdb0a640976a..86327ad8f27e7e77bfaa4b17291fe4d8830e2fbf 100755
--- a/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserWriter.java
+++ b/projects/cadcAccessControl/src/ca/nrc/cadc/ac/xml/UserWriter.java
@@ -86,6 +86,9 @@ import java.io.Writer;
 import java.security.Principal;
 import java.util.Set;
 
+/**
+ * Class to write a XML representation of a User object.
+ */
 public class UserWriter
 {
     /**
@@ -96,7 +99,7 @@ public class UserWriter
      * @throws java.io.IOException if the writer fails to write.
      * @throws WriterException
      */
-    public static void write(User<? extends Principal> user, StringBuilder builder)
+    public void write(User<? extends Principal> user, StringBuilder builder)
         throws IOException, WriterException
     {
         write(user, new StringBuilderWriter(builder));
@@ -110,7 +113,7 @@ public class UserWriter
      * @throws IOException if the writer fails to write.
      * @throws WriterException
      */
-    public static void write(User<? extends Principal> user, OutputStream out)
+    public void write(User<? extends Principal> user, OutputStream out)
         throws IOException, WriterException
     {                
         OutputStreamWriter outWriter;
@@ -133,7 +136,7 @@ public class UserWriter
      * @throws IOException if the writer fails to write.
      * @throws WriterException
      */
-    public static void write(User<? extends Principal> user, Writer writer)
+    public void write(User<? extends Principal> user, Writer writer)
         throws IOException, WriterException
     {
         if (user == null)
@@ -158,8 +161,9 @@ public class UserWriter
         Element userElement = new Element("user");
 
         // userID element
+        IdentityWriter identityWriter = new IdentityWriter();
         Element userIDElement = new Element("userID");
-        userIDElement.addContent(IdentityWriter.write(user.getUserID()));
+        userIDElement.addContent(identityWriter.write(user.getUserID()));
         userElement.addContent(userIDElement);
 
         // identities
@@ -169,7 +173,7 @@ public class UserWriter
             Element identitiesElement = new Element("identities");
             for (Principal identity : identities)
             {
-                identitiesElement.addContent(IdentityWriter.write(identity));
+                identitiesElement.addContent(identityWriter.write(identity));
             }
             userElement.addContent(identitiesElement);
         }
@@ -177,11 +181,12 @@ public class UserWriter
         // details
         if (!user.details.isEmpty())
         {
+            UserDetailsWriter userDetailsWriter = new UserDetailsWriter();
             Element detailsElement = new Element("details");
             Set<UserDetails> userDetails = user.details;
             for (UserDetails userDetail : userDetails)
             {
-                detailsElement.addContent(UserDetailsWriter.write(userDetail));
+                detailsElement.addContent(userDetailsWriter.write(userDetail));
             }
             userElement.addContent(detailsElement);
         }
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserRequestTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserRequestTest.java
index 9507d25e5b83156d2627652c90560565c4d30ee3..7bc85ed92c3c65e1ddd07c0b4c543ea4e6790a02 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserRequestTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/UserRequestTest.java
@@ -83,7 +83,9 @@ public class UserRequestTest
     @Test
     public void simpleEqualityTests() throws Exception
     {
-        UserRequest<HttpPrincipal> ur1 = new UserRequest<HttpPrincipal>(new User(new HttpPrincipal(("foo"))), "password");
+        UserRequest<HttpPrincipal> ur1 =
+            new UserRequest<HttpPrincipal>(
+                new User<HttpPrincipal>(new HttpPrincipal(("foo"))), "password".toCharArray());
         UserRequest<HttpPrincipal> ur2 = ur1;
         assertEquals(ur1, ur2);
         assertEquals(ur1.getUser(), ur2.getUser());
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapperTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapperTest.java
similarity index 96%
rename from projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapperTest.java
rename to projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapperTest.java
index fe977e4e3e5120c17b8e407df7bc210767384099..d8bd34ea6a1aae47bc8b0bd4b7182f3838932329 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JSONUserListInputStreamWrapperTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/client/JsonUserListInputStreamWrapperTest.java
@@ -80,15 +80,15 @@ import org.junit.Test;
 import static org.junit.Assert.*;
 
 
-public class JSONUserListInputStreamWrapperTest
+public class JsonUserListInputStreamWrapperTest
 {
     @Test
     public void readInputStream() throws Exception
     {
         final List<User<HttpPrincipal>> output =
                 new ArrayList<User<HttpPrincipal>>();
-        final JSONUserListInputStreamWrapper testSubject =
-                new JSONUserListInputStreamWrapper(output);
+        final JsonUserListInputStreamWrapper testSubject =
+                new JsonUserListInputStreamWrapper(output);
         final InputStream inputStream =
                 new ByteArrayInputStream("[{\"id\":\"CADCTest\",\"firstName\":\"CADCtest\",\"lastName\":\"USER\"}\n,{\"id\":\"User_2\",\"firstName\":\"User\",\"lastName\":\"2\"}]".getBytes());
 
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/GroupReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonGroupReaderWriterTest.java
similarity index 93%
rename from projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/GroupReaderWriterTest.java
rename to projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonGroupReaderWriterTest.java
index b60eee86191be9b62deda3174b51b9a1fe55a5e3..970c112d64728cf4055c17255cd072aaccd79794 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/GroupReaderWriterTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonGroupReaderWriterTest.java
@@ -93,9 +93,9 @@ import static org.junit.Assert.fail;
  *
  * @author jburke
  */
-public class GroupReaderWriterTest
+public class JsonGroupReaderWriterTest
 {
-    private static Logger log = Logger.getLogger(GroupReaderWriterTest.class);
+    private static Logger log = Logger.getLogger(JsonGroupReaderWriterTest.class);
 
     @Test
     public void testReaderExceptions()
@@ -104,7 +104,7 @@ public class GroupReaderWriterTest
         try
         {
             String s = null;
-            Group g = GroupReader.read(s);
+            Group g = JsonGroupReader.read(s);
             fail("null String should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -112,7 +112,7 @@ public class GroupReaderWriterTest
         try
         {
             InputStream in = null;
-            Group g = GroupReader.read(in);
+            Group g = JsonGroupReader.read(in);
             fail("null InputStream should throw IOException");
         }
         catch (IOException e) {}
@@ -120,7 +120,7 @@ public class GroupReaderWriterTest
         try
         {
             Reader r = null;
-            Group g = GroupReader.read(r);
+            Group g = JsonGroupReader.read(r);
             fail("null element should throw ReaderException");
         }
         catch (IllegalArgumentException e) {}
@@ -132,7 +132,7 @@ public class GroupReaderWriterTest
     {
         try
         {
-            GroupWriter.write(null, new StringBuilder());
+            JsonGroupWriter.write(null, new StringBuilder());
             fail("null Group should throw WriterException");
         }
         catch (WriterException e) {}
@@ -145,10 +145,10 @@ public class GroupReaderWriterTest
         Group expected = new Group("groupID", null);
                 
         StringBuilder json = new StringBuilder();
-        GroupWriter.write(expected, json);
+        JsonGroupWriter.write(expected, json);
         assertFalse(json.toString().isEmpty());
         
-        Group actual = GroupReader.read(json.toString());
+        Group actual = JsonGroupReader.read(json.toString());
         assertNotNull(actual);
         assertEquals(expected, actual);
     }
@@ -173,10 +173,10 @@ public class GroupReaderWriterTest
         expected.getUserAdmins().add(userAdmin);
         
         StringBuilder json = new StringBuilder();
-        GroupWriter.write(expected, json);
+        JsonGroupWriter.write(expected, json);
         assertFalse(json.toString().isEmpty());
 
-        Group actual = GroupReader.read(json.toString());
+        Group actual = JsonGroupReader.read(json.toString());
         assertNotNull(actual);
         assertEquals(expected, actual);
         assertEquals(expected.description, actual.description);
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/UserReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserReaderWriterTest.java
similarity index 92%
rename from projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/UserReaderWriterTest.java
rename to projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserReaderWriterTest.java
index 6e468da861196bcd48c20c419b02ffa21bb0c684..bc3a7c0fb491f9edf84341a921800ec19cd2106d 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/UserReaderWriterTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/json/JsonUserReaderWriterTest.java
@@ -91,9 +91,9 @@ import static org.junit.Assert.fail;
  *
  * @author jburke
  */
-public class UserReaderWriterTest
+public class JsonUserReaderWriterTest
 {
-    private static Logger log = Logger.getLogger(UserReaderWriterTest.class);
+    private static Logger log = Logger.getLogger(JsonUserReaderWriterTest.class);
 
     @Test
     public void testReaderExceptions()
@@ -102,7 +102,7 @@ public class UserReaderWriterTest
         try
         {
             String s = null;
-            User<? extends Principal> u = UserReader.read(s);
+            User<? extends Principal> u = JsonUserReader.read(s);
             fail("null String should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -110,7 +110,7 @@ public class UserReaderWriterTest
         try
         {
             InputStream in = null;
-            User<? extends Principal> u = UserReader.read(in);
+            User<? extends Principal> u = JsonUserReader.read(in);
             fail("null InputStream should throw IOException");
         }
         catch (IOException e) {}
@@ -118,7 +118,7 @@ public class UserReaderWriterTest
         try
         {
             Reader r = null;
-            User<? extends Principal> u = UserReader.read(r);
+            User<? extends Principal> u = JsonUserReader.read(r);
             fail("null Reader should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -130,7 +130,7 @@ public class UserReaderWriterTest
     {
         try
         {
-            UserWriter.write(null, new StringBuilder());
+            JsonUserWriter.write(null, new StringBuilder());
             fail("null User should throw WriterException");
         }
         catch (WriterException e) {}
@@ -146,10 +146,10 @@ public class UserReaderWriterTest
         expected.details.add(new PosixDetails(123l, 456l, "foo"));
 
         StringBuilder json = new StringBuilder();
-        UserWriter.write(expected, json);
+        JsonUserWriter.write(expected, json);
         assertFalse(json.toString().isEmpty());
         
-        User<? extends Principal> actual = UserReader.read(json.toString());
+        User<? extends Principal> actual = JsonUserReader.read(json.toString());
         assertNotNull(actual);
         assertEquals(expected, actual);
     }
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupReaderWriterTest.java
index 12f3f5093d5f35c4b98aa0d6bcdaea8b31125477..3c3dbe07116b1f1e3f2e41bfc49b39a12d2edb94 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupReaderWriterTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupReaderWriterTest.java
@@ -104,7 +104,8 @@ public class GroupReaderWriterTest
         try
         {
             String s = null;
-            Group g = GroupReader.read(s);
+            GroupReader groupReader = new GroupReader();
+            Group g = groupReader.read(s);
             fail("null String should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -112,7 +113,8 @@ public class GroupReaderWriterTest
         try
         {
             InputStream in = null;
-            Group g = GroupReader.read(in);
+            GroupReader groupReader = new GroupReader();
+            Group g = groupReader.read(in);
             fail("null InputStream should throw IOException");
         }
         catch (IOException e) {}
@@ -120,7 +122,8 @@ public class GroupReaderWriterTest
         try
         {
             Reader r = null;
-            Group g = GroupReader.read(r);
+            GroupReader groupReader = new GroupReader();
+            Group g = groupReader.read(r);
             fail("null element should throw ReaderException");
         }
         catch (IllegalArgumentException e) {}
@@ -132,7 +135,8 @@ public class GroupReaderWriterTest
     {
         try
         {
-            GroupWriter.write(null, new StringBuilder());
+            GroupWriter groupWriter = new GroupWriter();
+            groupWriter.write(null, new StringBuilder());
             fail("null Group should throw WriterException");
         }
         catch (WriterException e) {}
@@ -145,10 +149,12 @@ public class GroupReaderWriterTest
         Group expected = new Group("groupID", null);
                 
         StringBuilder xml = new StringBuilder();
-        GroupWriter.write(expected, xml);
+        GroupWriter groupWriter = new GroupWriter();
+        groupWriter.write(expected, xml);
         assertFalse(xml.toString().isEmpty());
-        
-        Group actual = GroupReader.read(xml.toString());
+
+        GroupReader groupReader = new GroupReader();
+        Group actual = groupReader.read(xml.toString());
         assertNotNull(actual);
         assertEquals(expected, actual);
     }
@@ -173,10 +179,12 @@ public class GroupReaderWriterTest
         expected.getUserAdmins().add(userAdmin);
         
         StringBuilder xml = new StringBuilder();
-        GroupWriter.write(expected, xml);
+        GroupWriter groupWriter = new GroupWriter();
+        groupWriter.write(expected, xml);
         assertFalse(xml.toString().isEmpty());
-        
-        Group actual = GroupReader.read(xml.toString());
+
+        GroupReader groupReader = new GroupReader();
+        Group actual = groupReader.read(xml.toString());
         assertNotNull(actual);
         assertEquals(expected, actual);
         assertEquals(expected.description, actual.description);
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupsReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupsReaderWriterTest.java
index 1d4d9f3052d68324ad7db6e332215c985108c9aa..0281eaf87cf1456c0d5781fc187012d3d5c7caa2 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupsReaderWriterTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/GroupsReaderWriterTest.java
@@ -99,7 +99,8 @@ public class GroupsReaderWriterTest
         try
         {
             String s = null;
-            List<Group> g = GroupsReader.read(s);
+            GroupListReader groupListReader = new GroupListReader();
+            List<Group> g = groupListReader.read(s);
             fail("null String should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -107,7 +108,8 @@ public class GroupsReaderWriterTest
         try
         {
             InputStream in = null;
-            List<Group> g = GroupsReader.read(in);
+            GroupListReader groupListReader = new GroupListReader();
+            List<Group> g = groupListReader.read(in);
             fail("null InputStream should throw IOException");
         }
         catch (IOException e) {}
@@ -115,7 +117,8 @@ public class GroupsReaderWriterTest
         try
         {
             Reader r = null;
-            List<Group> g = GroupsReader.read(r);
+            GroupListReader groupListReader = new GroupListReader();
+            List<Group> g = groupListReader.read(r);
             fail("null element should throw ReaderException");
         }
         catch (IllegalArgumentException e) {}
@@ -127,7 +130,8 @@ public class GroupsReaderWriterTest
     {
         try
         {
-            GroupsWriter.write(null, new StringBuilder());
+            GroupListWriter groupListWriter = new GroupListWriter();
+            groupListWriter.write(null, new StringBuilder());
             fail("null Group should throw WriterException");
         }
         catch (WriterException e) {}
@@ -142,10 +146,12 @@ public class GroupsReaderWriterTest
         expected.add(new Group("group2", null));
         
         StringBuilder xml = new StringBuilder();
-        GroupsWriter.write(expected, xml);
+        GroupListWriter groupListWriter = new GroupListWriter();
+        groupListWriter.write(expected, xml);
         assertFalse(xml.toString().isEmpty());
-        
-        List<Group> actual = GroupsReader.read(xml.toString());
+
+        GroupListReader groupListReader = new GroupListReader();
+        List<Group> actual = groupListReader.read(xml.toString());
         assertNotNull(actual);
         assertEquals(expected.size(), actual.size());
         assertEquals(expected.get(0), actual.get(0));
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserReaderWriterTest.java
index a0e3d54881d83f310dbc616884dcf37277066cd6..4e8d9f2b066ef053fb815854ee2a3ce03c66e30c 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserReaderWriterTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserReaderWriterTest.java
@@ -101,7 +101,8 @@ public class UserReaderWriterTest
         try
         {
             String s = null;
-            User<? extends Principal> u = UserReader.read(s);
+            UserReader userReader = new UserReader();
+            User<? extends Principal> u = userReader.read(s);
             fail("null String should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -109,7 +110,8 @@ public class UserReaderWriterTest
         try
         {
             InputStream in = null;
-            User<? extends Principal> u = UserReader.read(in);
+            UserReader userReader = new UserReader();
+            User<? extends Principal> u = userReader.read(in);
             fail("null InputStream should throw IOException");
         }
         catch (IOException e) {}
@@ -117,7 +119,8 @@ public class UserReaderWriterTest
         try
         {
             Reader r = null;
-            User<? extends Principal> u = UserReader.read(r);
+            UserReader userReader = new UserReader();
+            User<? extends Principal> u = userReader.read(r);
             fail("null Reader should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -129,7 +132,8 @@ public class UserReaderWriterTest
     {
         try
         {
-            ca.nrc.cadc.ac.xml.UserWriter.write(null, new StringBuilder());
+            UserWriter userWriter = new UserWriter();
+            userWriter.write(null, new StringBuilder());
             fail("null User should throw WriterException");
         }
         catch (WriterException e) {}
@@ -144,10 +148,12 @@ public class UserReaderWriterTest
         expected.details.add(new PersonalDetails("firstname", "lastname"));
         
         StringBuilder xml = new StringBuilder();
-        UserWriter.write(expected, xml);
+        UserWriter userWriter = new UserWriter();
+        userWriter.write(expected, xml);
         assertFalse(xml.toString().isEmpty());
-        
-        User<? extends Principal> actual = UserReader.read(xml.toString());
+
+        UserReader userReader = new UserReader();
+        User<? extends Principal> actual = userReader.read(xml.toString());
         assertNotNull(actual);
         assertEquals(expected, actual);
     }
diff --git a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserRequestReaderWriterTest.java b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserRequestReaderWriterTest.java
index 191fb9b603131b691659c843aa11b25303309d14..30dec9315f46556d62e8193380f68452e29b4a88 100644
--- a/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserRequestReaderWriterTest.java
+++ b/projects/cadcAccessControl/test/src/ca/nrc/cadc/ac/xml/UserRequestReaderWriterTest.java
@@ -99,7 +99,8 @@ public class UserRequestReaderWriterTest
         try
         {
             String s = null;
-            UserRequest u = UserRequestReader.read(s);
+            UserRequestReader userRequestReader = new UserRequestReader();
+            UserRequest u = userRequestReader.read(s);
             fail("null String should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -107,7 +108,8 @@ public class UserRequestReaderWriterTest
         try
         {
             InputStream in = null;
-            UserRequest u = UserRequestReader.read(in);
+            UserRequestReader userRequestReader = new UserRequestReader();
+            UserRequest u = userRequestReader.read(in);
             fail("null InputStream should throw IOException");
         }
         catch (IOException e) {}
@@ -115,7 +117,8 @@ public class UserRequestReaderWriterTest
         try
         {
             Reader r = null;
-            UserRequest u = UserRequestReader.read(r);
+            UserRequestReader userRequestReader = new UserRequestReader();
+            UserRequest u = userRequestReader.read(r);
             fail("null Reader should throw IllegalArgumentException");
         }
         catch (IllegalArgumentException e) {}
@@ -127,7 +130,8 @@ public class UserRequestReaderWriterTest
     {
         try
         {
-            UserRequestWriter.write(null, new StringBuilder());
+            UserRequestWriter userRequestWriter = new UserRequestWriter();
+            userRequestWriter.write(null, new StringBuilder());
             fail("null UserRequest should throw WriterException");
         }
         catch (WriterException e) {}
@@ -137,22 +141,26 @@ public class UserRequestReaderWriterTest
     public void testReadWrite()
         throws Exception
     {
-        User<? extends Principal> expectedUser = new User<Principal>(new HttpPrincipal("foo"));
+        User<HttpPrincipal> expectedUser = new User<HttpPrincipal>(new HttpPrincipal("foo"));
         expectedUser.getIdentities().add(new NumericPrincipal(123l));
         expectedUser.details.add(new PersonalDetails("firstname", "lastname"));
 
-        String expectedPassword = "123456";
+        char[] expectedPassword = "123456".toCharArray();
 
-        UserRequest expected = new UserRequest(expectedUser, expectedPassword);
+        UserRequest<HttpPrincipal> expected =
+            new UserRequest<HttpPrincipal>(expectedUser, expectedPassword);
 
         StringBuilder xml = new StringBuilder();
-        UserRequestWriter.write(expected, xml);
+        UserRequestWriter userRequestWriter = new UserRequestWriter();
+        userRequestWriter.write(expected, xml);
         assertFalse(xml.toString().isEmpty());
-        
-        UserRequest actual = UserRequestReader.read(xml.toString());
+
+        UserRequestReader userRequestReader = new UserRequestReader();
+        UserRequest actual = userRequestReader.read(xml.toString());
         assertNotNull(actual);
         assertEquals(expected.getUser(), actual.getUser());
-        assertEquals(expected.getPassword(), actual.getPassword());
+        assertEquals(String.valueOf(expected.getPassword()),
+                     String.valueOf(actual.getPassword()));
     }
     
 }