diff --git a/projects/cadcAccessControl-Server/build.xml b/projects/cadcAccessControl-Server/build.xml
index 12e688469aee1444d7027bef89f048af9de33f88..b3856f6dbb9aa7a1a3238e04662ba734567a1a96 100644
--- a/projects/cadcAccessControl-Server/build.xml
+++ b/projects/cadcAccessControl-Server/build.xml
@@ -139,7 +139,7 @@
   <property name="testingJars"
             value="${lib.commons-logging}:${dev.junit}:${dev.jsonassert}:${dev.httpunit}:${dev.easyMock}:${dev.selenium.server}:${dev.objenesis}:${lib.js}:${lib.nekoHTML}:${lib.xerces}"/>
 
-  <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>
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 61669fa27d74b9f372b10fe30c4225c1e63d6a6b..12dff65bc553e6ac6f711e77ddc73aaa65b70f40 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
@@ -79,16 +79,15 @@ public class AddGroupMemberAction extends GroupsAction
     private final String groupName;
     private final String groupMemberName;
 
-    AddGroupMemberAction(GroupLogInfo logInfo, String groupName,
+    AddGroupMemberAction(String groupName,
                          String groupMemberName)
     {
-        super(logInfo);
+        super();
         this.groupName = groupName;
         this.groupMemberName = groupMemberName;
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = groupPersistence.getGroup(this.groupName);
@@ -102,7 +101,6 @@ public class AddGroupMemberAction extends GroupsAction
         List<String> addedMembers = new ArrayList<String>();
         addedMembers.add(toAdd.getID());
         logGroupInfo(group.getID(), null, addedMembers);
-        return null;
     }
 
 }
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 cdf6093abea691a7412e1fd0a4a6bacd36c12cd4..bf39ee26d1225841677806914312106d38525442 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
@@ -83,18 +83,17 @@ public class AddUserMemberAction extends GroupsAction
     private final String userID;
     private final String userIDType;
 
-    AddUserMemberAction(GroupLogInfo logInfo, String groupName, String userID,
+    AddUserMemberAction(String groupName, String userID,
                         String userIDType)
     {
-        super(logInfo);
+        super();
         this.groupName = groupName;
         this.userID = userID;
         this.userIDType = userIDType;
     }
 
     @SuppressWarnings("unchecked")
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = groupPersistence.getGroup(this.groupName);
@@ -104,13 +103,12 @@ public class AddUserMemberAction extends GroupsAction
         {
             throw new MemberAlreadyExistsException();
         }
-        
+
         groupPersistence.modifyGroup(group);
 
         List<String> addedMembers = new ArrayList<String>();
         addedMembers.add(toAdd.getUserID().getName());
         logGroupInfo(group.getID(), null, addedMembers);
-        return null;
     }
 
 }
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 f282d722627e25796756e8921ae77ca2fb4f3940..91a058d3bed9f6fbe377631e85c118aeb193c0b8 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
@@ -82,14 +82,13 @@ public class CreateGroupAction extends GroupsAction
 {
     private final InputStream inputStream;
 
-    CreateGroupAction(GroupLogInfo logInfo, InputStream inputStream)
+    CreateGroupAction(InputStream inputStream)
     {
-        super(logInfo);
+        super();
         this.inputStream = inputStream;
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = GroupReader.read(this.inputStream);
@@ -111,7 +110,6 @@ public class CreateGroupAction extends GroupsAction
             }
         }
         logGroupInfo(newGroup.getID(), null, addedMembers);
-        return null;
     }
 
 }
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 2c96f279f30a5108de5be505761094a2c4e5bf9a..579f4b7e99600fb958e91de27d4b8183a1de0ca0 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
@@ -78,14 +78,13 @@ public class DeleteGroupAction extends GroupsAction
 {
     private final String groupName;
 
-    DeleteGroupAction(GroupLogInfo logInfo, String groupName)
+    DeleteGroupAction(String groupName)
     {
-        super(logInfo);
+        super();
         this.groupName = groupName;
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group deletedGroup = groupPersistence.getGroup(this.groupName);
@@ -102,7 +101,6 @@ public class DeleteGroupAction extends GroupsAction
                 this.logInfo.deletedMembers.add(usr.getUserID().getName());
             }
         }
-        return null;
     }
 
 }
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 3d7d9646f6692d46f3c76e2943702a7d55707bb2..adaf8d029a5e6fa0201c8985fa55d6de1584ba9c 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
@@ -75,20 +75,18 @@ public class GetGroupAction extends GroupsAction
 {
     private final String groupName;
 
-    GetGroupAction(GroupLogInfo logInfo, String groupName)
+    GetGroupAction( String groupName)
     {
-        super(logInfo);
+        super();
         this.groupName = groupName;
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = groupPersistence.getGroup(this.groupName);
         this.response.setContentType("application/xml");
         GroupWriter.write(group, this.response.getOutputStream());
-        return null;
     }
 
 }
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 a62ef9526d1ff961f6f9b0a88ea82283762f061c..fcf2cc4b8e863de961a9c648511b591e9e112eba 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
@@ -78,16 +78,15 @@ import ca.nrc.cadc.ac.server.GroupPersistence;
 
 public class GetGroupNamesAction extends GroupsAction
 {
-    
+
     private static final Logger log = Logger.getLogger(GetGroupNamesAction.class);
 
-    GetGroupNamesAction(GroupLogInfo logInfo)
+    GetGroupNamesAction()
     {
-        super(logInfo);
+        super();
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Collection<String> groups = groupPersistence.getGroupNames();
@@ -105,7 +104,5 @@ public class GetGroupNamesAction extends GroupsAction
             writer.write(group);
             start = false;
         }
-        
-        return null;
     }
 }
diff --git a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupLogInfo.java b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupLogInfo.java
index 38739a0849e2755fdcd6c42503a9e402a3cae8dd..7840104fdeb406edfeac5be1999a37916f8361bd 100755
--- a/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupLogInfo.java
+++ b/projects/cadcAccessControl-Server/src/ca/nrc/cadc/ac/server/web/groups/GroupLogInfo.java
@@ -72,6 +72,10 @@ import ca.nrc.cadc.log.ServletLogInfo;
 import java.util.List;
 import javax.servlet.http.HttpServletRequest;
 
+/**
+ * Extension of regular servlet log info that tracks
+ * group membership changes.
+ */
 public class GroupLogInfo extends ServletLogInfo
 {
     public String groupID;
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/GroupsAction.java
index 4ef1811573d47b135823e0e584ae9b2ace14b720..78cda24d63fceed6ea5a68d7f1fcc8145fa85bf3 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/GroupsAction.java
@@ -76,6 +76,7 @@ import java.security.PrivilegedExceptionAction;
 import java.util.List;
 
 import javax.security.auth.Subject;
+import javax.servlet.http.HttpServletRequest;
 import javax.servlet.http.HttpServletResponse;
 
 import org.apache.log4j.Logger;
@@ -95,40 +96,35 @@ public abstract class GroupsAction
 {
     private static final Logger log = Logger.getLogger(GroupsAction.class);
     protected GroupLogInfo logInfo;
+    protected HttpServletRequest request;
     protected HttpServletResponse response;
 
-    GroupsAction(GroupLogInfo logInfo)
+    GroupsAction()
+    {
+    }
+
+    abstract void doAction() throws Exception;
+
+    void setLogInfo(GroupLogInfo logInfo)
     {
         this.logInfo = logInfo;
     }
 
-    public void doAction(Subject subject, HttpServletResponse response)
-        throws IOException
+    void setHttpServletRequest(HttpServletRequest request)
+    {
+        this.request = request;
+    }
+
+    void setHttpServletResponse(HttpServletResponse response)
+    {
+        this.response = response;
+    }
+
+    public Object run() throws PrivilegedActionException
     {
         try
         {
-            try
-            {
-                this.response = response;
-
-                if (subject == null)
-                {
-                    run();
-                }
-                else
-                {
-                    Subject.doAs(subject, this);
-                }
-            }
-            catch (PrivilegedActionException e)
-            {
-                Throwable cause = e.getCause();
-                if (cause != null)
-                {
-                    throw cause;
-                }
-                throw e;
-            }
+            doAction();
         }
         catch (AccessControlException e)
         {
@@ -201,23 +197,29 @@ public abstract class GroupsAction
             log.error(message, t);
             sendError(500, message);
         }
+        return null;
     }
 
     private void sendError(int responseCode)
-        throws IOException
     {
         sendError(responseCode, null);
     }
 
     private void sendError(int responseCode, String message)
-        throws IOException
     {
         if (!this.response.isCommitted())
         {
             this.response.setContentType("text/plain");
             if (message != null)
             {
-                this.response.getWriter().write(message);
+                try
+                {
+                    this.response.getWriter().write(message);
+                }
+                catch (IOException e)
+                {
+                    log.warn("Could not write error message to output stream");
+                }
             }
             this.response.setStatus(responseCode);
         }
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 c2aaf942be82e190f33b7f9e1f7cb40aa6fafe97..87da08e15210467f9dc84030ec954c8dd5c31cc1 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
@@ -76,126 +76,206 @@ import javax.servlet.http.HttpServletRequest;
 
 import org.apache.log4j.Logger;
 
+import ca.nrc.cadc.ac.server.web.WebUtil;
 import ca.nrc.cadc.util.StringUtil;
 
-public class GroupsActionFactory
+/**
+ * This class provides static methods for each of the http methods for
+ * creating a factory object that will in turn create the correct group
+ * action.
+ *
+ * @author majorb
+ */
+public abstract class GroupsActionFactory
 {
     private static final Logger log = Logger.getLogger(GroupsActionFactory.class);
 
-    static GroupsAction getGroupsAction(HttpServletRequest request, GroupLogInfo logInfo)
-        throws IOException
-    {
-        GroupsAction action = null;
-        String method = request.getMethod();
-        String path = request.getPathInfo();
-        log.debug("method: " + method);
-        log.debug("path: " + path);
-
-        if (path == null)
-        {
-            path = "";
-        }
+    public abstract GroupsAction createAction(HttpServletRequest request)
+        throws IllegalArgumentException, IOException;
 
-        if (path.startsWith("/"))
+    public static GroupsActionFactory httpGetFactory()
+    {
+        return new GroupsActionFactory()
         {
-            path = path.substring(1);
-        }
+            public GroupsAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
+            {
+                GroupsAction action = null;
+                String path = request.getPathInfo();
+                log.debug("path: " + path);
 
-        if (path.endsWith("/"))
-        {
-            path = path.substring(0, path.length() - 1);
-        }
+                String[] segments = WebUtil.getPathSegments(path);
 
-        String[] segments = new String[0];
-        if (StringUtil.hasText(path))
-        {
-            segments = path.split("/");
-        }
+                if (segments.length == 0)
+                {
+                    action = new GetGroupNamesAction();
+                }
+                else if (segments.length == 1)
+                {
+                    String groupName = segments[0];
+                    action = new GetGroupAction(groupName);
+                }
 
-        if (segments.length == 0)
-        {
-            if (method.equals("GET"))
-            {
-                action = new GetGroupNamesAction(logInfo);
-            }
-            else if (method.equals("PUT"))
-            {
-                action = new CreateGroupAction(logInfo, request.getInputStream());
+                if (action != null)
+                {
+                    log.debug("Returning action: " + action.getClass());
+                    return action;
+                }
+                throw new IllegalArgumentException("Bad GET request to " + path);
             }
+        };
+    }
 
-        }
-        else if (segments.length == 1)
+    public static GroupsActionFactory httpPutFactory()
+    {
+        return new GroupsActionFactory()
         {
-            String groupName = segments[0];
-            if (method.equals("GET"))
-            {
-                action = new GetGroupAction(logInfo, groupName);
-            }
-            else if (method.equals("DELETE"))
+            public GroupsAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
             {
-                action = new DeleteGroupAction(logInfo, groupName);
-            }
-            else if (method.equals("POST"))
-            {
-                final URL requestURL = new URL(request.getRequestURL().toString());
-                final StringBuilder sb = new StringBuilder();
-                sb.append(requestURL.getProtocol());
-                sb.append("://");
-                sb.append(requestURL.getHost());
-                if (requestURL.getPort() > 0)
+                GroupsAction action = null;
+                String path = request.getPathInfo();
+                log.debug("path: " + path);
+
+                String[] segments = WebUtil.getPathSegments(path);
+
+                if (segments.length == 0)
                 {
-                    sb.append(":");
-                    sb.append(requestURL.getPort());
+                    action = new CreateGroupAction(request.getInputStream());
+                }
+                else if (segments.length == 3)
+                {
+                    String groupName = segments[0];
+                    String memberCategory = segments[1];
+                    if (memberCategory.equals("groupMembers"))
+                    {
+                        String groupMemberName = segments[2];
+                        action = new AddGroupMemberAction(groupName, groupMemberName);
+                    }
+                    else if (memberCategory.equals("userMembers"))
+                    {
+                        String userMemberID = segments[2];
+                        String userMemberIDType = request.getParameter("idType");
+                        action = new AddUserMemberAction(groupName, userMemberID, userMemberIDType);
+                    }
                 }
-                sb.append(request.getContextPath());
-                sb.append(request.getServletPath());
-                sb.append("/");
-                sb.append(path);
 
-                action = new ModifyGroupAction(logInfo, groupName, sb.toString(),
-                                               request.getInputStream());
+                if (action != null)
+                {
+                    log.debug("Returning action: " + action.getClass());
+                    return action;
+                }
+                throw new IllegalArgumentException("Bad PUT request to " + path);
             }
-        }
-        else if (segments.length == 3)
+        };
+    }
+
+    public static GroupsActionFactory httpPostFactory()
+    {
+        return new GroupsActionFactory()
         {
-            String groupName = segments[0];
-            String memberCategory = segments[1];
-            if (method.equals("PUT"))
+            public GroupsAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
             {
-                if (memberCategory.equals("groupMembers"))
+                GroupsAction action = null;
+                String path = request.getPathInfo();
+                log.debug("path: " + path);
+
+                String[] segments = WebUtil.getPathSegments(path);
+
+                if (segments.length == 1)
                 {
-                    String groupMemberName = segments[2];
-                    action = new AddGroupMemberAction(logInfo, groupName, groupMemberName);
+
+
+                    String groupName = segments[0];
+
+                    final URL requestURL = new URL(request.getRequestURL().toString());
+                    final StringBuilder sb = new StringBuilder();
+                    sb.append(requestURL.getProtocol());
+                    sb.append("://");
+                    sb.append(requestURL.getHost());
+                    if (requestURL.getPort() > 0)
+                    {
+                        sb.append(":");
+                        sb.append(requestURL.getPort());
+                    }
+                    sb.append(request.getContextPath());
+                    sb.append(request.getServletPath());
+                    sb.append("/");
+                    sb.append(path);
+
+                    action = new ModifyGroupAction(groupName, sb.toString(), request.getInputStream());
                 }
-                else if (memberCategory.equals("userMembers"))
+
+                if (action != null)
                 {
-                    String userMemberID = URLDecoder.decode(segments[2], "UTF-8");
-                    String userMemberIDType = request.getParameter("idType");
-                    action = new AddUserMemberAction(logInfo, groupName, userMemberID, userMemberIDType);
+                    log.debug("Returning action: " + action.getClass());
+                    return action;
                 }
+                throw new IllegalArgumentException("Bad POST request to " + path);
             }
-            else if (method.equals("DELETE"))
+        };
+
+    }
+
+    public static GroupsActionFactory httpDeleteFactory()
+    {
+        return new GroupsActionFactory()
+        {
+            public GroupsAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
             {
-                if (memberCategory.equals("groupMembers"))
+                GroupsAction action = null;
+                String path = request.getPathInfo();
+                log.debug("path: " + path);
+
+                String[] segments = WebUtil.getPathSegments(path);
+
+                if (segments.length == 1)
                 {
-                    String groupMemberName = segments[2];
-                    action = new RemoveGroupMemberAction(logInfo, groupName, groupMemberName);
+                    String groupName = segments[0];
+                    action = new DeleteGroupAction(groupName);
                 }
-                else if (memberCategory.equals("userMembers"))
+                else if (segments.length == 3)
+                {
+                    String groupName = segments[0];
+                    String memberCategory = segments[1];
+
+                    if (memberCategory.equals("groupMembers"))
+                    {
+                        String groupMemberName = segments[2];
+                        action = new RemoveGroupMemberAction(groupName, groupMemberName);
+                    }
+                    else if (memberCategory.equals("userMembers"))
+                    {
+                        String memberUserID = segments[2];
+                        String memberUserIDType = request.getParameter("idType");
+                        action = new RemoveUserMemberAction(groupName, memberUserID, memberUserIDType);
+                    }
+                }
+
+                if (action != null)
                 {
-                    String memberUserID = URLDecoder.decode(segments[2], "UTF-8");
-                    String memberUserIDType = request.getParameter("idType");
-                    action = new RemoveUserMemberAction(logInfo, groupName, memberUserID, memberUserIDType);
+                    log.debug("Returning action: " + action.getClass());
+                    return action;
                 }
+                throw new IllegalArgumentException("Bad DELETE request to " + path);
             }
-        }
+        };
 
-        if (action != null)
-        {
-            log.debug("Returning action: " + action.getClass());
-            return action;
-        }
-        throw new IllegalArgumentException("Bad groups request: " + method + " on " + path);
     }
 
+    public static GroupsActionFactory httpHeadFactory()
+    {
+        return new GroupsActionFactory()
+        {
+            public GroupsAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
+            {
+                // http head not supported
+                throw new UnsupportedOperationException();
+            }
+        };
+
+    }
 }
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/GroupsServlet.java
index cd4682325e14892bdb73c8cd96c7ed1782368730..fb5413bbfb2c8b4eb3b8920cb7c1b8a1a8054ebc 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/GroupsServlet.java
@@ -69,6 +69,7 @@
 package ca.nrc.cadc.ac.server.web.groups;
 
 import java.io.IOException;
+import java.security.PrivilegedActionException;
 
 import javax.security.auth.Subject;
 import javax.servlet.http.HttpServlet;
@@ -78,15 +79,23 @@ import javax.servlet.http.HttpServletResponse;
 import org.apache.log4j.Logger;
 
 import ca.nrc.cadc.auth.AuthenticationUtil;
+import ca.nrc.cadc.util.StringUtil;
 
+/**
+ * Servlet for handling all requests to /groups
+ *
+ * @author majorb
+ */
 public class GroupsServlet extends HttpServlet
 {
+
+    private static final long serialVersionUID = 7854660717655869213L;
     private static final Logger log = Logger.getLogger(GroupsServlet.class);
 
     /**
      * Create a GroupAction and run the action safely.
      */
-    private void doAction(HttpServletRequest request, HttpServletResponse response)
+    private void doAction(GroupsActionFactory factory, HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
         long start = System.currentTimeMillis();
@@ -96,14 +105,43 @@ public class GroupsServlet extends HttpServlet
             log.info(logInfo.start());
             Subject subject = AuthenticationUtil.getSubject(request);
             logInfo.setSubject(subject);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, logInfo);
-            action.doAction(subject, response);
+
+            GroupsAction action = factory.createAction(request);
+
+            action.setLogInfo(logInfo);
+            action.setHttpServletRequest(request);
+            action.setHttpServletResponse(response);
+
+            try
+            {
+                if (subject == null)
+                {
+                    action.run();
+                }
+                else
+                {
+                    Subject.doAs(subject, action);
+                }
+            }
+            catch (PrivilegedActionException e)
+            {
+                Throwable cause = e.getCause();
+                if (cause != null)
+                {
+                    throw cause;
+                }
+                Exception exception = e.getException();
+                if (exception != null)
+                {
+                    throw exception;
+                }
+                throw e;
+            }
         }
         catch (IllegalArgumentException e)
         {
             log.debug(e.getMessage(), e);
             logInfo.setMessage(e.getMessage());
-            logInfo.setSuccess(false);
             response.getWriter().write(e.getMessage());
             response.setStatus(400);
         }
@@ -124,38 +162,38 @@ public class GroupsServlet extends HttpServlet
     }
 
     @Override
-    public void doGet(HttpServletRequest request, HttpServletResponse response)
+    public void doGet(final HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(GroupsActionFactory.httpGetFactory(), request, response);
     }
 
     @Override
     public void doPost(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(GroupsActionFactory.httpPostFactory(), request, response);
     }
 
     @Override
     public void doDelete(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(GroupsActionFactory.httpDeleteFactory(), request, response);
     }
 
     @Override
     public void doPut(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(GroupsActionFactory.httpPutFactory(), request, response);
     }
 
     @Override
     public void doHead(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(GroupsActionFactory.httpHeadFactory(), request, response);
     }
 
 }
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 444e335dc3594ec7543872a5f77db25ef0cc4c32..82f262e535b07b27469dd68c1b047293d87b7c75 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
@@ -83,17 +83,15 @@ public class ModifyGroupAction extends GroupsAction
     private final String request;
     private final InputStream inputStream;
 
-    ModifyGroupAction(GroupLogInfo logInfo, String groupName,
-                      final String request, InputStream inputStream)
+    ModifyGroupAction(String groupName, final String request, InputStream inputStream)
     {
-        super(logInfo);
+        super();
         this.groupName = groupName;
         this.request = request;
         this.inputStream = inputStream;
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = GroupReader.read(this.inputStream);
@@ -135,8 +133,6 @@ public class ModifyGroupAction extends GroupsAction
         logGroupInfo(group.getID(), deletedMembers, addedMembers);
 
         this.response.sendRedirect(request);
-
-        return null;
     }
 
 }
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 a05d17f93d73a061e96bb44975b3f68548b3b7c5..26352aa7610d252343dd530f04b7a158fbc7a07c 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
@@ -79,15 +79,14 @@ public class RemoveGroupMemberAction extends GroupsAction
     private final String groupName;
     private final String groupMemberName;
 
-    RemoveGroupMemberAction(GroupLogInfo logInfo, String groupName, String groupMemberName)
+    RemoveGroupMemberAction(String groupName, String groupMemberName)
     {
-        super(logInfo);
+        super();
         this.groupName = groupName;
         this.groupMemberName = groupMemberName;
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = groupPersistence.getGroup(this.groupName);
@@ -101,7 +100,6 @@ public class RemoveGroupMemberAction extends GroupsAction
         List<String> deletedMembers = new ArrayList<String>();
         deletedMembers.add(toRemove.getID());
         logGroupInfo(group.getID(), deletedMembers, null);
-        return null;
     }
 
 }
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 20dcf8b4a32af6119d4033ea5b72db0389218ba1..f7bcc91f6ae471b78a75b638d91630a8ec467acb 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
@@ -83,17 +83,16 @@ public class RemoveUserMemberAction extends GroupsAction
     private final String userID;
     private final String userIDType;
 
-    RemoveUserMemberAction(GroupLogInfo logInfo, String groupName, String userID, String userIDType)
+    RemoveUserMemberAction(String groupName, String userID, String userIDType)
     {
-        super(logInfo);
+        super();
         this.groupName = groupName;
         this.userID = userID;
         this.userIDType = userIDType;
     }
 
     @SuppressWarnings("unchecked")
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         GroupPersistence groupPersistence = getGroupPersistence();
         Group group = groupPersistence.getGroup(this.groupName);
@@ -108,7 +107,6 @@ public class RemoveUserMemberAction extends GroupsAction
         List<String> deletedMembers = new ArrayList<String>();
         deletedMembers.add(toRemove.getUserID().getName());
         logGroupInfo(group.getID(), deletedMembers, null);
-        return null;
     }
 
 }
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 96ec4f6268f2ceb96ffe6e0e97ff743a29f867e0..4fe9bd125b8799624ac0d47f7f7c7709b388364d 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
@@ -68,71 +68,39 @@
  */
 package ca.nrc.cadc.ac.server.web.users;
 
-import java.io.IOException;
 import java.io.InputStream;
 
-import ca.nrc.cadc.ac.ReaderException;
 import ca.nrc.cadc.ac.User;
-import ca.nrc.cadc.ac.UserAlreadyExistsException;
 import ca.nrc.cadc.ac.UserRequest;
 import ca.nrc.cadc.ac.server.UserPersistence;
-import ca.nrc.cadc.auth.HttpPrincipal;
 
 import javax.servlet.http.HttpServletResponse;
 import java.security.Principal;
-import java.util.Set;
 
 
 public class CreateUserAction extends UsersAction
 {
     private final InputStream inputStream;
 
-
-    CreateUserAction(final UserLogInfo logInfo,
-                     final InputStream inputStream)
+    CreateUserAction(final InputStream inputStream)
     {
-        super(logInfo);
+        super();
         this.inputStream = inputStream;
     }
 
 
-    public Object run() throws Exception
+    public void doAction() throws Exception
     {
-        try
-        {
-            final UserPersistence<Principal> userPersistence =
-                    getUserPersistence();
-            final UserRequest<Principal> userRequest =
-                    readUserRequest(this.inputStream);
-            final User<Principal> newUser =
-                    userPersistence.addUser(userRequest);
-            final Set<HttpPrincipal> httpPrincipals =
-                    newUser.getIdentities(HttpPrincipal.class);
-
-            if (httpPrincipals.isEmpty())
-            {
-                throw new IOException("No Web Identity found (HttpPrincipal)");
-            }
-            else
-            {
-                response.setStatus(HttpServletResponse.SC_CREATED);
-                redirectGet(httpPrincipals.toArray(
-                        new HttpPrincipal[1])[0].getName());
-            }
+        final UserPersistence<Principal> userPersistence =
+                getUserPersistence();
+        final UserRequest<Principal> userRequest =
+                readUserRequest(this.inputStream);
+        final User<Principal> newUser =
+                userPersistence.addUser(userRequest);
 
-            logUserInfo(newUser.getUserID().getName());
-        }
-        catch (UserAlreadyExistsException e)
-        {
-            response.setStatus(HttpServletResponse.SC_CONFLICT);
-            response.getWriter().write("User already exists");
-        }
-        catch (ReaderException e)
-        {
-            throw new IllegalArgumentException("Invalid input", e);
-        }
+        response.setStatus(HttpServletResponse.SC_CREATED);
 
-        return null;
+        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 806e8f9812bec8751246ef1f7f8a84b8081a2d29..f24cb87195687d8002e7b86bf4dac19778333a24 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
@@ -76,19 +76,17 @@ public class DeleteUserAction extends UsersAction
 {
     private final Principal userID;
 
-    DeleteUserAction(UserLogInfo logInfo, Principal userID)
+    DeleteUserAction(Principal userID)
     {
-        super(logInfo);
+        super();
         this.userID = userID;
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         UserPersistence userPersistence = getUserPersistence();
         User<? extends Principal> deletedUser = userPersistence.getUser(userID);
         userPersistence.deleteUser(deletedUser.getUserID());
-        return null;
     }
 
 }
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 0d87074e50c61558ce4b2f18ea43e7c273e518f1..b060426997c6bd6e363194db6ab400551782de69 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
@@ -78,13 +78,13 @@ public class GetUserAction extends UsersAction
 {
     private final Principal userID;
 
-    GetUserAction(UserLogInfo logInfo, Principal userID)
+    GetUserAction(Principal userID)
     {
-        super(logInfo);
+        super();
         this.userID = userID;
     }
 
-    public Object run() throws Exception
+    public void doAction() throws Exception
     {
         final UserPersistence<Principal> userPersistence = getUserPersistence();
 
@@ -100,7 +100,6 @@ public class GetUserAction extends UsersAction
         }
 
         writeUser(user);
-        return null;
     }
 
 }
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/GetUsersAction.java
index 008a8572ec167b146db84497e82df94e4d95aee0..3f366b34ae89038fc7ab3ac7d4e47e4e655eda79 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/GetUsersAction.java
@@ -77,20 +77,18 @@ import ca.nrc.cadc.ac.server.UserPersistence;
 
 public class GetUsersAction extends UsersAction
 {
-    
+
     private static final Logger log = Logger.getLogger(GetUsersAction.class);
 
-    GetUsersAction(UserLogInfo logInfo)
+    GetUsersAction()
     {
-        super(logInfo);
+        super();
     }
 
-    public Object run()
-        throws Exception
+    public void doAction() throws Exception
     {
         final UserPersistence userPersistence = getUserPersistence();
 
         writeUsers(userPersistence.getUsers());
-        return null;
     }
 }
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 1349d5744b6f72457fce6a0609307d0874c31b47..055aa1ee24f1bb95ab15a6921407ad2dce4f6df3 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
@@ -70,12 +70,14 @@ package ca.nrc.cadc.ac.server.web.users;
 
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.auth.AuthenticationUtil;
 import ca.nrc.cadc.auth.HttpPrincipal;
 
 import javax.servlet.http.HttpServletResponse;
 import java.io.IOException;
 import java.io.InputStream;
 import java.security.Principal;
+import java.util.Iterator;
 import java.util.Set;
 
 
@@ -84,36 +86,21 @@ public class ModifyUserAction extends UsersAction
     private final InputStream inputStream;
 
 
-    ModifyUserAction(final UserLogInfo logInfo,
-                     final InputStream inputStream)
+    ModifyUserAction(final InputStream inputStream)
     {
-        super(logInfo);
+        super();
 
         this.inputStream = inputStream;
     }
 
 
-    public Object run() throws Exception
+    public void doAction() throws Exception
     {
         final User<Principal> user = readUser(this.inputStream);
         final User<Principal> modifiedUser = modifyUser(user);
         logUserInfo(modifiedUser.getUserID().getName());
 
-        final Set<HttpPrincipal> httpPrincipals =
-                modifiedUser.getIdentities(HttpPrincipal.class);
-
-        if (httpPrincipals.isEmpty())
-        {
-            throw new IOException("No Web Identity found (HttpPrincipal)");
-        }
-        else
-        {
-            response.setStatus(HttpServletResponse.SC_OK);
-            redirectGet(httpPrincipals.toArray(
-                    new HttpPrincipal[1])[0].getName());
-        }
-
-        return null;
+        redirectGet(modifiedUser);
     }
 
     /**
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/UsersAction.java
index 39ca1313e2f1b4b283628ec53a640f32a82e4911..e8bd10aa22f33658b707101c4b0cdbe5f3499e6f 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/UsersAction.java
@@ -74,6 +74,7 @@ import ca.nrc.cadc.ac.UserNotFoundException;
 import ca.nrc.cadc.ac.UserRequest;
 import ca.nrc.cadc.ac.server.PluginFactory;
 import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.auth.AuthenticationUtil;
 import ca.nrc.cadc.net.TransientException;
 import org.apache.log4j.Logger;
 
@@ -86,10 +87,11 @@ 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 UsersAction implements PrivilegedExceptionAction<Object>
 {
     private static final Logger log = Logger.getLogger(UsersAction.class);
     static final String DEFAULT_CONTENT_TYPE = "text/xml";
@@ -99,41 +101,27 @@ public abstract class UsersAction
     protected HttpServletResponse response;
     protected String acceptedContentType = DEFAULT_CONTENT_TYPE;
 
-    private String redirectURLPrefix;
+    UsersAction()
+    {
+    }
 
+    public abstract void doAction() throws Exception;
 
-    UsersAction(UserLogInfo logInfo)
+    public void setLogInfo(UserLogInfo logInfo)
     {
         this.logInfo = logInfo;
     }
 
-    public void doAction(Subject subject, HttpServletResponse response)
-        throws IOException
+    public void setResponse(HttpServletResponse response)
+    {
+        this.response = response;
+    }
+
+    public Object run() throws IOException
     {
         try
         {
-            try
-            {
-                this.response = response;
-
-                if (subject == null)
-                {
-                    run();
-                }
-                else
-                {
-                    Subject.doAs(subject, this);
-                }
-            }
-            catch (PrivilegedActionException e)
-            {
-                Throwable cause = e.getCause();
-                if (cause != null)
-                {
-                    throw cause;
-                }
-                throw e;
-            }
+            doAction();
         }
         catch (AccessControlException e)
         {
@@ -178,6 +166,7 @@ public abstract class UsersAction
             log.error(message, t);
             sendError(500, message);
         }
+        return null;
     }
 
     private void sendError(int responseCode)
@@ -328,15 +317,28 @@ public abstract class UsersAction
         }
     }
 
-    protected void setRedirectURLPrefix(final String redirectURLPrefix)
+    void redirectGet(User<?> user) throws Exception
     {
-        this.redirectURLPrefix = redirectURLPrefix;
-    }
+        final Set<Principal> httpPrincipals =  user.getIdentities();
 
-    void redirectGet(final String userID) throws Exception
-    {
-        final String redirectURL = this.redirectURLPrefix + "/" + userID
-                                   + "?idType=HTTP";
+        String id = null;
+        String idType = null;
+        Iterator<Principal> i = httpPrincipals.iterator();
+        Principal next = null;
+        while (idType == null && i.hasNext())
+        {
+            next = i.next();
+            idType = AuthenticationUtil.getPrincipalType(next);
+            id = next.getName();
+        }
+
+        if (idType == null)
+        {
+            throw new IllegalStateException("No identities found.");
+        }
+
+        response.setStatus(HttpServletResponse.SC_OK);
+        final String redirectURL = "/" + id + "?idType=" + idType;
         response.setHeader("Location", redirectURL);
     }
 }
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/UsersActionFactory.java
index 9333357cdd6723110d0750d682b4b8071f5713da..49d53403005a5433e7600281635b3d936cfcc310 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/UsersActionFactory.java
@@ -70,6 +70,9 @@ 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;
@@ -85,86 +88,151 @@ import javax.servlet.http.HttpServletRequest;
 import org.apache.log4j.Logger;
 
 
-public class UsersActionFactory
+public abstract class UsersActionFactory
 {
     private static final Logger log = Logger
             .getLogger(UsersActionFactory.class);
 
-    static UsersAction getUsersAction(HttpServletRequest request, UserLogInfo logInfo)
-            throws IOException
-    {
-        UsersAction action = null;
-        String method = request.getMethod();
-        String path = request.getPathInfo();
-        log.debug("method: " + method);
-        log.debug("path: " + path);
+    public abstract UsersAction createAction(HttpServletRequest request)
+            throws IllegalArgumentException, IOException;
 
-        if (path == null)
+    public static UsersActionFactory httpGetFactory()
+    {
+        return new UsersActionFactory()
         {
-            path = "";
-        }
+            public UsersAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
+            {
+                UsersAction action = null;
+                String path = request.getPathInfo();
+                log.debug("path: " + path);
 
-        if (path.startsWith("/"))
-        {
-            path = path.substring(1);
-        }
+                String[] segments = WebUtil.getPathSegments(path);
 
-        if (path.endsWith("/"))
-        {
-            path = path.substring(0, path.length() - 1);
-        }
+                if (segments.length == 0)
+                {
+                    action = new GetUsersAction();
+                }
+                else if (segments.length == 1)
+                {
+                    User user = getUser(segments[0], request.getParameter("idType"), path);
+                        action = new GetUserAction(user.getUserID());
+                }
 
-        String[] segments = new String[0];
-        if (StringUtil.hasText(path))
-        {
-            segments = path.split("/");
-        }
+                if (action != null)
+                {
+                    log.debug("Returning action: " + action.getClass());
+                    return action;
+                }
 
-        if (segments.length == 0)
-        {
-            if (method.equals("GET"))
-            {
-                action = new GetUsersAction(logInfo);
+                 throw new IllegalArgumentException("Bad GET request to " + path);
             }
-            else if (method.equals("PUT"))
-            {
-                action = new CreateUserAction(logInfo,
-                                              request.getInputStream());
-                action.setRedirectURLPrefix(request.getRequestURL().toString());
-            }
-        }
-        else
+        };
+    }
+
+    public static UsersActionFactory httpPutFactory()
+    {
+        return new UsersActionFactory()
         {
-            User user = getUser(segments[0], request.getParameter("idType"),
-                                method, path);
-            if (method.equals("GET"))
+            public UsersAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
             {
-                action = new GetUserAction(logInfo, user.getUserID());
+                UsersAction action = null;
+                String path = request.getPathInfo();
+                log.debug("path: " + path);
+
+                String[] segments = WebUtil.getPathSegments(path);
+
+                if (segments.length == 0)
+                {
+                    action = new CreateUserAction(request.getInputStream());
+                }
+
+                if (action != null)
+                {
+                    log.debug("Returning action: " + action.getClass());
+                    return action;
+                }
+
+                 throw new IllegalArgumentException("Bad PUT request to " + path);
             }
-            else if (method.equals("DELETE"))
+        };
+    }
+
+    public static UsersActionFactory httpPostFactory()
+    {
+        return new UsersActionFactory()
+        {
+            public UsersAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
             {
-                action = new DeleteUserAction(logInfo, user.getUserID());
+                UsersAction action = null;
+                String path = request.getPathInfo();
+                log.debug("path: " + path);
+
+                String[] segments = WebUtil.getPathSegments(path);
+
+                if (segments.length == 1)
+                {
+                    action = new ModifyUserAction(request.getInputStream());
+                }
+
+                if (action != null)
+                {
+                    log.debug("Returning action: " + action.getClass());
+                    return action;
+                }
+
+                 throw new IllegalArgumentException("Bad POST request to " + path);
             }
-            else if (method.equals("POST"))
+        };
+    }
+
+    public static UsersActionFactory httpDeleteFactory()
+    {
+        return new UsersActionFactory()
+        {
+            public UsersAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
             {
-                action = new ModifyUserAction(logInfo,
-                                              request.getInputStream());
-                action.setRedirectURLPrefix(request.getRequestURL().toString());
+                UsersAction action = null;
+                String path = request.getPathInfo();
+                log.debug("path: " + path);
+
+                String[] segments = WebUtil.getPathSegments(path);
+
+                if (segments.length == 1)
+                {
+                    User user = getUser(segments[0], request.getParameter("idType"), path);
+                    action = new DeleteUserAction(user.getUserID());
+                }
+
+                if (action != null)
+                {
+                    log.debug("Returning action: " + action.getClass());
+                    return action;
+                }
+
+                 throw new IllegalArgumentException("Bad DELETE request to " + path);
             }
-        }
+        };
+    }
 
-        if (action != null)
+    public static UsersActionFactory httpHeadFactory()
+    {
+        return new UsersActionFactory()
         {
-            log.debug("Returning action: " + action.getClass());
-            return action;
-        }
-        final String error = "Bad users request: " + method + " on " + path;
-        throw new IllegalArgumentException(error);
+            public UsersAction createAction(HttpServletRequest request)
+                throws IllegalArgumentException, IOException
+            {
+                // http head not supported
+                throw new UnsupportedOperationException();
+            }
+        };
     }
 
     private static User<? extends Principal> getUser(final String userName,
                                                      final String idType,
-                                                     final String method,
                                                      final String path)
     {
         if (idType == null || idType.isEmpty())
@@ -194,9 +262,7 @@ public class UsersActionFactory
         }
         else
         {
-            final String error = "Bad users request: " + method + " on " + path +
-                                 " because of unknown principal type " + idType;
-            throw new IllegalArgumentException(error);
+            throw new IllegalArgumentException("Unregonized userid");
         }
     }
 
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/UsersServlet.java
index c3f2e53c75821f6de8d170d5d1a02393196ca316..eae260e1945108f0c84a31e300d01aaabc783dcf 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/UsersServlet.java
@@ -69,6 +69,7 @@
 package ca.nrc.cadc.ac.server.web.users;
 
 import java.io.IOException;
+import java.security.PrivilegedActionException;
 
 import javax.security.auth.Subject;
 import javax.servlet.http.HttpServlet;
@@ -89,7 +90,7 @@ public class UsersServlet extends HttpServlet
     /**
      * Create a UserAction and run the action safely.
      */
-    private void doAction(HttpServletRequest request, HttpServletResponse response)
+    private void doAction(UsersActionFactory factory, HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
         long start = System.currentTimeMillis();
@@ -100,9 +101,38 @@ public class UsersServlet extends HttpServlet
             log.info(logInfo.start());
             Subject subject = AuthenticationUtil.getSubject(request);
             logInfo.setSubject(subject);
-            UsersAction action = UsersActionFactory.getUsersAction(request, logInfo);
+
+            UsersAction action = factory.createAction(request);
+
+            action.setLogInfo(logInfo);
+            action.setResponse(response);
             action.setAcceptedContentType(getAcceptedContentType(request));
-            action.doAction(subject, response);
+
+            try
+            {
+                if (subject == null)
+                {
+                    action.run();
+                }
+                else
+                {
+                    Subject.doAs(subject, action);
+                }
+            }
+            catch (PrivilegedActionException e)
+            {
+                Throwable cause = e.getCause();
+                if (cause != null)
+                {
+                    throw cause;
+                }
+                Exception exception = e.getException();
+                if (exception != null)
+                {
+                    throw exception;
+                }
+                throw e;
+            }
         }
         catch (IllegalArgumentException e)
         {
@@ -132,35 +162,35 @@ public class UsersServlet extends HttpServlet
     public void doGet(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(UsersActionFactory.httpGetFactory(), request, response);
     }
 
     @Override
     public void doPost(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(UsersActionFactory.httpGetFactory(), request, response);
     }
 
     @Override
     public void doDelete(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(UsersActionFactory.httpDeleteFactory(), request, response);
     }
 
     @Override
     public void doPut(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(UsersActionFactory.httpPutFactory(), request, response);
     }
 
     @Override
     public void doHead(HttpServletRequest request, HttpServletResponse response)
         throws IOException
     {
-        doAction(request, response);
+        doAction(UsersActionFactory.httpHeadFactory(), request, response);
     }
 
     /**
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AddGroupMemberActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AddGroupMemberActionTest.java
index 8550e9ba2dfa13797a5da1a34c5a93fcd8261c10..f8075c5abd57eceab1fe478be97bc2060086e3eb 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AddGroupMemberActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AddGroupMemberActionTest.java
@@ -88,7 +88,7 @@ import static org.junit.Assert.*;
 public class AddGroupMemberActionTest
 {
     private final static Logger log = Logger.getLogger(AddGroupMemberActionTest.class);
-    
+
     @BeforeClass
     public static void setUpClass()
     {
@@ -103,15 +103,13 @@ public class AddGroupMemberActionTest
             Group group = new Group("group", null);
             Group member = new Group("member", null);
             group.getGroupMembers().add(member);
-            
+
             final GroupPersistence groupPersistence = createMock(GroupPersistence.class);
             expect(groupPersistence.getGroup("group")).andReturn(group);
             expect(groupPersistence.getGroup("member")).andReturn(member);
             replay(groupPersistence);
-            
-            GroupLogInfo logInfo = createMock(GroupLogInfo.class);
-            
-            AddGroupMemberAction action = new AddGroupMemberAction(logInfo, "group", "member")
+
+            AddGroupMemberAction action = new AddGroupMemberAction("group", "member")
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
@@ -119,10 +117,10 @@ public class AddGroupMemberActionTest
                     return groupPersistence;
                 };
             };
-            
+
             try
             {
-                action.run();
+                action.doAction();
                 fail("duplicate group member should throw GroupAlreadyExistsException");
             }
             catch (GroupAlreadyExistsException ignore) {}
@@ -133,7 +131,7 @@ public class AddGroupMemberActionTest
             fail("unexpected error: " + t.getMessage());
         }
     }
-    
+
     @Test
     public void testRun() throws Exception
     {
@@ -152,10 +150,8 @@ public class AddGroupMemberActionTest
             expect(groupPersistence.modifyGroup(group)).andReturn(modified);
 
             replay(groupPersistence);
-            
-            GroupLogInfo logInfo = createMock(GroupLogInfo.class);
-            
-            AddGroupMemberAction action = new AddGroupMemberAction(logInfo, "group", "member")
+
+            AddGroupMemberAction action = new AddGroupMemberAction("group", "member")
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
@@ -164,7 +160,11 @@ public class AddGroupMemberActionTest
                 };
             };
 
-            action.run();
+            GroupLogInfo logInfo = createMock(GroupLogInfo.class);
+            action.setLogInfo(logInfo);
+
+            action.doAction();
+
         }
         catch (Throwable t)
         {
@@ -172,5 +172,5 @@ public class AddGroupMemberActionTest
             fail("unexpected error: " + t.getMessage());
         }
     }
-    
+
 }
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AddUserMemberActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AddUserMemberActionTest.java
index 1cdb6f4fc1cf397c8388bbb7322fd39cbb845e61..8eae8ace9c01d0fce6d452861e0f61030008e954 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AddUserMemberActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/AddUserMemberActionTest.java
@@ -82,6 +82,8 @@ import org.apache.log4j.Logger;
 import org.easymock.EasyMock;
 import org.junit.BeforeClass;
 import org.junit.Test;
+
+import static org.easymock.EasyMock.createMock;
 import static org.junit.Assert.*;
 
 /**
@@ -91,7 +93,7 @@ import static org.junit.Assert.*;
 public class AddUserMemberActionTest
 {
     private final static Logger log = Logger.getLogger(AddUserMemberActionTest.class);
-    
+
     @BeforeClass
     public static void setUpClass()
     {
@@ -103,43 +105,42 @@ public class AddUserMemberActionTest
     public void testExceptions()
     {
         try
-        {   
+        {
             String userID = "foo";
             String userIDType = AuthenticationUtil.AUTH_TYPE_HTTP;
             Principal userPrincipal = AuthenticationUtil.createPrincipal(userID, userIDType);
             User<Principal> user = new User<Principal>(userPrincipal);
-            
+
             Group group = new Group("group", null);
             group.getUserMembers().add(user);
-            
+
             final GroupPersistence groupPersistence = EasyMock.createMock(GroupPersistence.class);
             EasyMock.expect(groupPersistence.getGroup("group")).andReturn(group);
             EasyMock.replay(groupPersistence);
-            
+
             final UserPersistence userPersistence = EasyMock.createMock(UserPersistence.class);
             EasyMock.expect(userPersistence.getUser(userPrincipal)).andReturn(user);
             EasyMock.replay(userPersistence);
-            
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            
-            AddUserMemberAction action = new AddUserMemberAction(logInfo, "group", userID, userIDType)
+
+
+            AddUserMemberAction action = new AddUserMemberAction("group", userID, userIDType)
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
                 {
                     return groupPersistence;
                 };
-                
+
                 @Override
                 <T extends Principal> UserPersistence<T> getUserPersistence()
                 {
                     return userPersistence;
                 };
             };
-            
+
             try
             {
-                action.run();
+                action.doAction();
                 fail("duplicate group member should throw MemberAlreadyExistsException");
             }
             catch (MemberAlreadyExistsException ignore) {}
@@ -150,7 +151,7 @@ public class AddUserMemberActionTest
             fail("unexpected error: " + t.getMessage());
         }
     }
-    
+
     @Test
     @SuppressWarnings("unchecked")
     public void testRun() throws Exception
@@ -161,38 +162,38 @@ public class AddUserMemberActionTest
             String userIDType = AuthenticationUtil.AUTH_TYPE_HTTP;
             Principal userPrincipal = AuthenticationUtil.createPrincipal(userID, userIDType);
             User<Principal> user = new User<Principal>(userPrincipal);
-            
+
             Group group = new Group("group", null);
             Group modified = new Group("group", null);
             modified.getUserMembers().add(user);
-            
+
             final GroupPersistence groupPersistence = EasyMock.createMock(GroupPersistence.class);
             EasyMock.expect(groupPersistence.getGroup("group")).andReturn(group);
             EasyMock.expect(groupPersistence.modifyGroup(group)).andReturn(modified);
             EasyMock.replay(groupPersistence);
-            
+
             final UserPersistence userPersistence = EasyMock.createMock(UserPersistence.class);
             EasyMock.expect(userPersistence.getUser(userPrincipal)).andReturn(user);
             EasyMock.replay(userPersistence);
-            
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            
-            AddUserMemberAction action = new AddUserMemberAction(logInfo, "group", userID, userIDType)
+
+            AddUserMemberAction action = new AddUserMemberAction("group", userID, userIDType)
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
                 {
                     return groupPersistence;
                 };
-                
+
                 @Override
                 <T extends Principal> UserPersistence<T> getUserPersistence()
                 {
                     return userPersistence;
                 };
             };
-            
-            action.run();
+
+            GroupLogInfo logInfo = createMock(GroupLogInfo.class);
+            action.setLogInfo(logInfo);
+            action.doAction();
         }
         catch (Throwable t)
         {
@@ -200,5 +201,5 @@ public class AddUserMemberActionTest
             fail("unexpected error: " + t.getMessage());
         }
     }
-    
+
 }
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/DeleteGroupActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/DeleteGroupActionTest.java
index 0994610208aab34a25f8b521508c6146283df352..ca58a75c3d9760419cb50be1f7c6e5feea82f4fc 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/DeleteGroupActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/DeleteGroupActionTest.java
@@ -77,6 +77,8 @@ import org.apache.log4j.Logger;
 import org.easymock.EasyMock;
 import org.junit.BeforeClass;
 import org.junit.Test;
+
+import static org.easymock.EasyMock.createMock;
 import static org.junit.Assert.*;
 
 /**
@@ -86,7 +88,7 @@ import static org.junit.Assert.*;
 public class DeleteGroupActionTest
 {
    private final static Logger log = Logger.getLogger(DeleteGroupActionTest.class);
-    
+
     @BeforeClass
     public static void setUpClass()
     {
@@ -97,18 +99,16 @@ public class DeleteGroupActionTest
     public void testRun()
     {
         try
-        {   
+        {
             Group group = new Group("group", null);
-            
+
             final GroupPersistence groupPersistence = EasyMock.createMock(GroupPersistence.class);
             EasyMock.expect(groupPersistence.getGroup("group")).andReturn(group);
             groupPersistence.deleteGroup("group");
             EasyMock.expectLastCall().once();
             EasyMock.replay(groupPersistence);
-            
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            
-            DeleteGroupAction action = new DeleteGroupAction(logInfo, "group")
+
+            DeleteGroupAction action = new DeleteGroupAction("group")
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
@@ -117,7 +117,9 @@ public class DeleteGroupActionTest
                 };
             };
 
-            action.run();
+            GroupLogInfo logInfo = createMock(GroupLogInfo.class);
+            action.setLogInfo(logInfo);
+            action.doAction();
         }
         catch (Throwable t)
         {
@@ -125,5 +127,5 @@ public class DeleteGroupActionTest
             fail("unexpected error: " + t.getMessage());
         }
     }
-    
+
 }
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GetGroupNamesActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GetGroupNamesActionTest.java
index 1f2d4d67b0f71e4a2734230b8308f04cd27d5353..0ade66ad54e553d496d9df4366ec2698a7130bee 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GetGroupNamesActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GetGroupNamesActionTest.java
@@ -85,6 +85,7 @@ import java.security.Principal;
 import java.util.ArrayList;
 import java.util.Collection;
 
+import static org.easymock.EasyMock.createMock;
 import static org.junit.Assert.fail;
 
 /**
@@ -134,11 +135,9 @@ public class GetGroupNamesActionTest
             EasyMock.expectLastCall();
             EasyMock.expect(mockResponse.getWriter()).andReturn(mockWriter).once();
 
-            GroupLogInfo mockLog = EasyMock.createMock(GroupLogInfo.class);
+            EasyMock.replay(mockPersistence, mockWriter, mockResponse);
 
-            EasyMock.replay(mockPersistence, mockWriter, mockResponse, mockLog);
-
-            GetGroupNamesAction action = new GetGroupNamesAction(mockLog)
+            GetGroupNamesAction action = new GetGroupNamesAction()
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
@@ -147,7 +146,9 @@ public class GetGroupNamesActionTest
                 };
             };
 
-            action.run();
+            GroupLogInfo logInfo = createMock(GroupLogInfo.class);
+            action.setLogInfo(logInfo);
+            action.doAction();
         }
         catch (Throwable t)
         {
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 3a9b19ccb053c960256ea8c67cd32fe39dac6544..ffa77fa364cb09ddf24256f90afcf2adc4131878 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
@@ -93,9 +93,8 @@ public class GroupActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName/groupMembers/groupToAdd");
-            EasyMock.expect(request.getMethod()).andReturn("PUT");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpPutFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof AddGroupMemberAction);
         }
@@ -114,9 +113,8 @@ public class GroupActionFactoryTest
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName/userMembers/userToAdd");
             EasyMock.expect(request.getParameter("idType")).andReturn("x509");
-            EasyMock.expect(request.getMethod()).andReturn("PUT");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpPutFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof AddUserMemberAction);
         }
@@ -134,10 +132,9 @@ public class GroupActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("");
-            EasyMock.expect(request.getMethod()).andReturn("PUT");
             EasyMock.expect(request.getInputStream()).andReturn(null);
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpPutFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof CreateGroupAction);
         }
@@ -155,9 +152,8 @@ public class GroupActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName");
-            EasyMock.expect(request.getMethod()).andReturn("DELETE");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof DeleteGroupAction);
         }
@@ -175,9 +171,8 @@ public class GroupActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName");
-            EasyMock.expect(request.getMethod()).andReturn("GET");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpGetFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof GetGroupAction);
         }
@@ -195,9 +190,8 @@ public class GroupActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("");
-            EasyMock.expect(request.getMethod()).andReturn("GET");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpGetFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof GetGroupNamesAction);
         }
@@ -218,13 +212,12 @@ public class GroupActionFactoryTest
 
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName");
-            EasyMock.expect(request.getMethod()).andReturn("POST");
             EasyMock.expect(request.getRequestURL()).andReturn(sb);
             EasyMock.expect(request.getContextPath()).andReturn("");
             EasyMock.expect(request.getServletPath()).andReturn("");
             EasyMock.expect(request.getInputStream()).andReturn(null);
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpPostFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof ModifyGroupAction);
         }
@@ -242,9 +235,8 @@ public class GroupActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName/groupMembers/groupToRenove");
-            EasyMock.expect(request.getMethod()).andReturn("DELETE");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof RemoveGroupMemberAction);
         }
@@ -262,10 +254,9 @@ public class GroupActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("groupName/userMembers/userToRemove");
-            EasyMock.expect(request.getMethod()).andReturn("DELETE");
             EasyMock.expect(request.getParameter("idType")).andReturn("x509");
             EasyMock.replay(request);
-            GroupsAction action = GroupsActionFactory.getGroupsAction(request, null);
+            GroupsAction action = GroupsActionFactory.httpDeleteFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof RemoveUserMemberAction);
         }
@@ -276,10 +267,7 @@ public class GroupActionFactoryTest
         }
     }
 
-    @Test
-    public void testBadRequests()
-    {
-        try
+    /*
         {
             TestRequest[] testRequests =
             {
@@ -304,6 +292,151 @@ public class GroupActionFactoryTest
                 new TestRequest("groupName/groupMembers/groupMemberName/tooManySegments", "HEAD"),
                 new TestRequest("groupName/groupMembers/groupMemberName/tooManySegments", "DELETE"),
             };
+            */
+
+    @Test
+    public void testBadGetRequests()
+    {
+        try
+        {
+            TestRequest[] testRequests =
+            {
+                new TestRequest("groupName/groupMembers"),
+                new TestRequest("groupName/misspelled/id"),
+                new TestRequest("groupName/groupMembers/groupMemberName"),
+                new TestRequest("groupName/userMembers/userMemberName"),
+                new TestRequest("groupName/groupMembers/groupMemberName/tooManySegments")
+            };
+
+            for (TestRequest testRequest : testRequests)
+            {
+
+                log.debug("Testing: " + testRequest);
+
+                HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
+                EasyMock.expect(request.getPathInfo()).andReturn(testRequest.path);
+                if (testRequest.paramName != null)
+                {
+                    EasyMock.expect(request.getParameter(testRequest.paramName)).andReturn(testRequest.paramValue);
+                }
+                EasyMock.replay(request);
+                try
+                {
+                    GroupsActionFactory.httpGetFactory().createAction(request);
+                    Assert.fail("Should have been a bad request: on " + testRequest.path);
+                }
+                catch (IllegalArgumentException e)
+                {
+                    // expected
+                }
+                EasyMock.verify(request);
+            }
+        }
+        catch (Throwable t)
+        {
+            log.error(t.getMessage(), t);
+            Assert.fail("unexpected error: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testBadPutRequests()
+    {
+        try
+        {
+            TestRequest[] testRequests =
+            {
+                    new TestRequest("groupName/groupMembers"),
+                    new TestRequest("groupName/groupMembers/groupMemberName/tooManySegments")
+            };
+
+            for (TestRequest testRequest : testRequests)
+            {
+
+                log.debug("Testing: " + testRequest);
+
+                HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
+                EasyMock.expect(request.getPathInfo()).andReturn(testRequest.path);
+                if (testRequest.paramName != null)
+                {
+                    EasyMock.expect(request.getParameter(testRequest.paramName)).andReturn(testRequest.paramValue);
+                }
+                EasyMock.replay(request);
+                try
+                {
+                    GroupsActionFactory.httpPutFactory().createAction(request);
+                    Assert.fail("Should have been a bad request: on " + testRequest.path);
+                }
+                catch (IllegalArgumentException e)
+                {
+                    // expected
+                }
+                EasyMock.verify(request);
+            }
+        }
+        catch (Throwable t)
+        {
+            log.error(t.getMessage(), t);
+            Assert.fail("unexpected error: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testBadPostRequests()
+    {
+        try
+        {
+            TestRequest[] testRequests =
+            {
+                    new TestRequest(""),
+                    new TestRequest("groupName/groupMembers"),
+                    new TestRequest("groupName/groupMembers/groupMemberName"),
+                    new TestRequest("groupName/userMembers/userMemberName"),
+                    new TestRequest("groupName/groupMembers/groupMemberName/tooManySegments")
+            };
+
+            for (TestRequest testRequest : testRequests)
+            {
+
+                log.debug("Testing: " + testRequest);
+
+                HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
+                EasyMock.expect(request.getPathInfo()).andReturn(testRequest.path);
+                if (testRequest.paramName != null)
+                {
+                    EasyMock.expect(request.getParameter(testRequest.paramName)).andReturn(testRequest.paramValue);
+                }
+                EasyMock.replay(request);
+                try
+                {
+                    GroupsActionFactory.httpPostFactory().createAction(request);
+                    Assert.fail("Should have been a bad request: on " + testRequest.path);
+                }
+                catch (IllegalArgumentException e)
+                {
+                    // expected
+                }
+                EasyMock.verify(request);
+            }
+        }
+        catch (Throwable t)
+        {
+            log.error(t.getMessage(), t);
+            Assert.fail("unexpected error: " + t.getMessage());
+        }
+    }
+
+    @Test
+    public void testBadDeleteRequests()
+    {
+        try
+        {
+            TestRequest[] testRequests =
+            {
+                    new TestRequest(""),
+                    new TestRequest("groupName/groupMembers"),
+                    new TestRequest("groupName/groupMembers/groupMemberName/tooManySegments"),
+            };
 
             for (TestRequest testRequest : testRequests)
             {
@@ -312,7 +445,6 @@ public class GroupActionFactoryTest
 
                 HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
                 EasyMock.expect(request.getPathInfo()).andReturn(testRequest.path);
-                EasyMock.expect(request.getMethod()).andReturn(testRequest.method);
                 if (testRequest.paramName != null)
                 {
                     EasyMock.expect(request.getParameter(testRequest.paramName)).andReturn(testRequest.paramValue);
@@ -320,8 +452,8 @@ public class GroupActionFactoryTest
                 EasyMock.replay(request);
                 try
                 {
-                    GroupsActionFactory.getGroupsAction(request, null);
-                    Assert.fail("Should have been a bad request: " + testRequest.method + " on " + testRequest.path);
+                    GroupsActionFactory.httpDeleteFactory().createAction(request);
+                    Assert.fail("Should have been a bad request: on " + testRequest.path);
                 }
                 catch (IllegalArgumentException e)
                 {
@@ -340,25 +472,23 @@ public class GroupActionFactoryTest
     private class TestRequest
     {
         public String path;
-        public String method;
         public String paramName;
         public String paramValue;
 
-        public TestRequest(String path, String method)
+        public TestRequest(String path)
         {
-            this(path, method, null, null);
+            this(path, null, null);
         }
-        public TestRequest(String path, String method, String paramName, String paramValue)
+        public TestRequest(String path, String paramName, String paramValue)
         {
             this.path = path;
-            this.method = method;
             this.paramName = paramName;
             this.paramValue = paramValue;
         }
         @Override
         public String toString()
         {
-            return method + " on path " + path +
+            return "paht " + path +
                 ((paramName == null) ? "" : "?" + paramName + "=" + paramValue);
         }
 
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionTest.java
deleted file mode 100644
index def8852585eaf9d5102a31ede3712bb7214504f0..0000000000000000000000000000000000000000
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/GroupsActionTest.java
+++ /dev/null
@@ -1,260 +0,0 @@
-/*
- ************************************************************************
- *******************  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 GroupsActionTest
-{
-    private final static Logger log = Logger.getLogger(GroupsActionTest.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);
-
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            logInfo.setSuccess(false);
-            EasyMock.expectLastCall().once();
-            logInfo.setMessage("Internal Transient Error: foo");
-            EasyMock.expectLastCall().once();
-            EasyMock.replay(logInfo);
-
-            GroupsActionImpl action = new GroupsActionImpl(logInfo);
-            action.setException(new TransientException("foo"));
-            action.doAction(null, response);
-        }
-        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);
-
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            logInfo.setMessage(message);
-            EasyMock.expectLastCall().once();
-            EasyMock.replay(logInfo);
-
-            GroupsActionImpl action = new GroupsActionImpl(logInfo);
-            action.setException(e);
-            action.doAction(null, response);
-        }
-        catch (Throwable t)
-        {
-            log.error(t.getMessage(), t);
-            fail("unexpected error: " + t.getMessage());
-        }
-    }
-
-    public class GroupsActionImpl extends GroupsAction
-    {
-        Exception exception;
-        
-        public GroupsActionImpl(GroupLogInfo logInfo)
-        {
-            super(logInfo);
-        }
-
-        public Object run() 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/RemoveGroupMemberActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/RemoveGroupMemberActionTest.java
index 749d0076eed5527337e293ac48f4b11951d54a55..acb8224e91380a5c03e9d024eae0cd3dfa682298 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/RemoveGroupMemberActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/RemoveGroupMemberActionTest.java
@@ -78,6 +78,8 @@ import org.apache.log4j.Logger;
 import org.easymock.EasyMock;
 import org.junit.BeforeClass;
 import org.junit.Test;
+
+import static org.easymock.EasyMock.createMock;
 import static org.junit.Assert.*;
 
 /**
@@ -87,7 +89,7 @@ import static org.junit.Assert.*;
 public class RemoveGroupMemberActionTest
 {
     private final static Logger log = Logger.getLogger(RemoveGroupMemberActionTest.class);
-    
+
     @BeforeClass
     public static void setUpClass()
     {
@@ -101,15 +103,13 @@ public class RemoveGroupMemberActionTest
         {
             Group group = new Group("group", null);
             Group member = new Group("member", null);
-            
+
             final GroupPersistence groupPersistence = EasyMock.createMock(GroupPersistence.class);
             EasyMock.expect(groupPersistence.getGroup("group")).andReturn(group);
             EasyMock.expect(groupPersistence.getGroup("member")).andReturn(member);
             EasyMock.replay(groupPersistence);
-            
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            
-            RemoveGroupMemberAction action = new RemoveGroupMemberAction(logInfo, "group", "member")
+
+            RemoveGroupMemberAction action = new RemoveGroupMemberAction( "group", "member")
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
@@ -117,10 +117,10 @@ public class RemoveGroupMemberActionTest
                     return groupPersistence;
                 };
             };
-            
+
             try
             {
-                action.run();
+                action.doAction();
                 fail("unknown group member should throw GroupNotFoundException");
             }
             catch (GroupNotFoundException ignore) {}
@@ -131,7 +131,7 @@ public class RemoveGroupMemberActionTest
             fail("unexpected error: " + t.getMessage());
         }
     }
-    
+
     @Test
     public void testRun() throws Exception
     {
@@ -140,19 +140,17 @@ public class RemoveGroupMemberActionTest
             Group member = new Group("member", null);
             Group group = new Group("group", null);
             group.getGroupMembers().add(member);
-            
+
             Group modified = new Group("group", null);
             modified.getGroupMembers().add(member);
-            
+
             final GroupPersistence groupPersistence = EasyMock.createMock(GroupPersistence.class);
             EasyMock.expect(groupPersistence.getGroup("group")).andReturn(group);
             EasyMock.expect(groupPersistence.getGroup("member")).andReturn(member);
             EasyMock.expect(groupPersistence.modifyGroup(group)).andReturn(modified);
             EasyMock.replay(groupPersistence);
-            
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            
-            RemoveGroupMemberAction action = new RemoveGroupMemberAction(logInfo, "group", "member")
+
+            RemoveGroupMemberAction action = new RemoveGroupMemberAction("group", "member")
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
@@ -161,7 +159,9 @@ public class RemoveGroupMemberActionTest
                 };
             };
 
-            action.run();
+            GroupLogInfo logInfo = createMock(GroupLogInfo.class);
+            action.setLogInfo(logInfo);
+            action.doAction();
         }
         catch (Throwable t)
         {
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/RemoveUserMemberActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/RemoveUserMemberActionTest.java
index 05609091e46dba3e1b60d8c307622578d75b62b3..92f5263dabbed86331eb015a9e0098bc02a146b0 100644
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/RemoveUserMemberActionTest.java
+++ b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/groups/RemoveUserMemberActionTest.java
@@ -81,6 +81,8 @@ import org.apache.log4j.Logger;
 import org.easymock.EasyMock;
 import org.junit.BeforeClass;
 import org.junit.Test;
+
+import static org.easymock.EasyMock.createMock;
 import static org.junit.Assert.*;
 
 /**
@@ -90,7 +92,7 @@ import static org.junit.Assert.*;
 public class RemoveUserMemberActionTest
 {
    private final static Logger log = Logger.getLogger(RemoveUserMemberActionTest.class);
-    
+
     @BeforeClass
     public static void setUpClass()
     {
@@ -102,42 +104,40 @@ public class RemoveUserMemberActionTest
     public void testExceptions()
     {
         try
-        {   
+        {
             String userID = "foo";
             String userIDType = AuthenticationUtil.AUTH_TYPE_HTTP;
             Principal userPrincipal = AuthenticationUtil.createPrincipal(userID, userIDType);
             User<Principal> user = new User<Principal>(userPrincipal);
-            
+
             Group group = new Group("group", null);
-            
+
             final GroupPersistence groupPersistence = EasyMock.createMock(GroupPersistence.class);
             EasyMock.expect(groupPersistence.getGroup("group")).andReturn(group);
             EasyMock.replay(groupPersistence);
-            
+
             final UserPersistence userPersistence = EasyMock.createMock(UserPersistence.class);
             EasyMock.expect(userPersistence.getUser(userPrincipal)).andReturn(user);
             EasyMock.replay(userPersistence);
-            
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            
-            RemoveUserMemberAction action = new RemoveUserMemberAction(logInfo, "group", userID, userIDType)
+
+            RemoveUserMemberAction action = new RemoveUserMemberAction("group", userID, userIDType)
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
                 {
                     return groupPersistence;
                 };
-                
+
                 @Override
                 <T extends Principal> UserPersistence<T> getUserPersistence()
                 {
                     return userPersistence;
                 };
             };
-            
+
             try
             {
-                action.run();
+                action.doAction();
                 fail("unknown group member should throw MemberNotFoundException");
             }
             catch (MemberNotFoundException ignore) {}
@@ -148,7 +148,7 @@ public class RemoveUserMemberActionTest
             fail("unexpected error: " + t.getMessage());
         }
     }
-    
+
     @Test
     @SuppressWarnings("unchecked")
     public void testRun() throws Exception
@@ -159,38 +159,38 @@ public class RemoveUserMemberActionTest
             String userIDType = AuthenticationUtil.AUTH_TYPE_HTTP;
             Principal userPrincipal = AuthenticationUtil.createPrincipal(userID, userIDType);
             User<Principal> user = new User<Principal>(userPrincipal);
-            
+
             Group group = new Group("group", null);
             group.getUserMembers().add(user);
             Group modified = new Group("group", null);
-            
+
             final GroupPersistence groupPersistence = EasyMock.createMock(GroupPersistence.class);
             EasyMock.expect(groupPersistence.getGroup("group")).andReturn(group);
             EasyMock.expect(groupPersistence.modifyGroup(group)).andReturn(modified);
             EasyMock.replay(groupPersistence);
-            
+
             final UserPersistence userPersistence = EasyMock.createMock(UserPersistence.class);
             EasyMock.expect(userPersistence.getUser(userPrincipal)).andReturn(user);
             EasyMock.replay(userPersistence);
-            
-            GroupLogInfo logInfo = EasyMock.createMock(GroupLogInfo.class);
-            
-            RemoveUserMemberAction action = new RemoveUserMemberAction(logInfo, "group", userID, userIDType)
+
+            RemoveUserMemberAction action = new RemoveUserMemberAction("group", userID, userIDType)
             {
                 @Override
                 <T extends Principal> GroupPersistence<T> getGroupPersistence()
                 {
                     return groupPersistence;
                 };
-                
+
                 @Override
                 <T extends Principal> UserPersistence<T> getUserPersistence()
                 {
                     return userPersistence;
                 };
             };
-            
-            action.run();
+
+            GroupLogInfo logInfo = createMock(GroupLogInfo.class);
+            action.setLogInfo(logInfo);
+            action.doAction();
         }
         catch (Throwable t)
         {
@@ -198,5 +198,5 @@ public class RemoveUserMemberActionTest
             fail("unexpected error: " + t.getMessage());
         }
     }
-    
+
 }
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 e116d69fc410b6e5c740bec4259f440753f03bd7..e309b17c0c05036d8704c71d6e412940d9736c7f 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
@@ -69,6 +69,7 @@ package ca.nrc.cadc.ac.server.web.users;
 
 import ca.nrc.cadc.ac.User;
 import ca.nrc.cadc.ac.server.UserPersistence;
+import ca.nrc.cadc.ac.server.web.groups.GroupLogInfo;
 import ca.nrc.cadc.auth.HttpPrincipal;
 import org.junit.Test;
 
@@ -92,7 +93,7 @@ public class GetUserActionTest
                 createMock(UserPersistence.class);
         final HttpPrincipal userID = new HttpPrincipal("CADCtest");
 
-        final GetUserAction testSubject = new GetUserAction(null, userID)
+        final GetUserAction testSubject = new GetUserAction(userID)
         {
             @Override
             UserPersistence<HttpPrincipal> getUserPersistence()
@@ -112,7 +113,8 @@ public class GetUserActionTest
 
         replay(mockResponse, mockUserPersistence);
 
-        testSubject.doAction(null, mockResponse);
+        testSubject.setResponse(mockResponse);
+        testSubject.doAction();
 
         assertEquals("Wrong XML output.",
                      "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
@@ -133,7 +135,7 @@ public class GetUserActionTest
                 createMock(UserPersistence.class);
         final HttpPrincipal userID = new HttpPrincipal("CADCtest");
 
-        final GetUserAction testSubject = new GetUserAction(null, userID)
+        final GetUserAction testSubject = new GetUserAction(userID)
         {
             @Override
             UserPersistence<HttpPrincipal> getUserPersistence()
@@ -154,7 +156,10 @@ public class GetUserActionTest
         expectLastCall().once();
 
         replay(mockResponse, mockUserPersistence);
-        testSubject.doAction(null, mockResponse);
+        testSubject.setResponse(mockResponse);
+        UserLogInfo logInfo = createMock(UserLogInfo.class);
+        testSubject.setLogInfo(logInfo);
+        testSubject.doAction();
 
         assertEquals("Wrong JSON output.",
                      "{\"user\":{\"userID\":{\"identity\":{\"name\":\"CADCtest\",\"type\":\"HTTP\"}}}}",
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/GetUsersActionTest.java
index f1dc7a29aeb521d739a16325c62b97af3a83510a..74ea262be6f776c1d10a8ee6f1a0db66de2bf636 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/GetUsersActionTest.java
@@ -88,6 +88,7 @@ import java.util.HashMap;
 import java.util.Map;
 
 import static org.easymock.EasyMock.*;
+
 import org.junit.BeforeClass;
 import org.junit.Test;
 import static org.junit.Assert.assertEquals;
@@ -122,7 +123,7 @@ public class GetUsersActionTest
                             new PersonalDetails("USER", Integer.toString(i)));
         }
 
-        final GetUsersAction testSubject = new GetUsersAction(null)
+        final GetUsersAction testSubject = new GetUsersAction()
         {
             @Override
             UserPersistence<HttpPrincipal> getUserPersistence()
@@ -143,7 +144,10 @@ public class GetUsersActionTest
         expectLastCall().once();
 
         replay(mockResponse, mockUserPersistence);
-        testSubject.doAction(null, mockResponse);
+        testSubject.setResponse(mockResponse);
+        UserLogInfo logInfo = createMock(UserLogInfo.class);
+        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\"}]");
@@ -170,7 +174,7 @@ public class GetUsersActionTest
                             new PersonalDetails("USER", Integer.toString(i)));
         }
 
-        final GetUsersAction testSubject = new GetUsersAction(null)
+        final GetUsersAction testSubject = new GetUsersAction()
         {
             @Override
             UserPersistence<HttpPrincipal> getUserPersistence()
@@ -189,7 +193,10 @@ public class GetUsersActionTest
         expectLastCall().once();
 
         replay(mockResponse, mockUserPersistence);
-        testSubject.doAction(null, mockResponse);
+        testSubject.setResponse(mockResponse);
+        UserLogInfo logInfo = createMock(UserLogInfo.class);
+        testSubject.setLogInfo(logInfo);
+        testSubject.doAction();
 
         final String expected = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n" +
                                 "<users>\r\n" +
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 dde4aaa3110039f1d70db689e6309ffbbaaecff7..5ad9f24dfc4e195b954ed54722eebe96964ac5b7 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
@@ -95,8 +95,10 @@ public class ModifyUserActionTest
         final InputStream inputStream = new ByteArrayInputStream(input);
 
         // Should match the JSON above, without the e-mail modification.
+        Principal principal = new HttpPrincipal("CADCtest");
         final User<Principal> userObject =
-                new User<Principal>(new HttpPrincipal("CADCtest"));
+                new User<Principal>(principal);
+        userObject.getIdentities().add(principal);
         final PersonalDetails personalDetail =
                 new PersonalDetails("CADC", "Test");
         personalDetail.email = "CADC.Test@nrc-cnrc.gc.ca";
@@ -107,20 +109,20 @@ public class ModifyUserActionTest
         final HttpServletResponse mockResponse =
                 createMock(HttpServletResponse.class);
 
-        final String requestURL = "http://mysite.com:8080/noentry/authorize";
-
         @SuppressWarnings("unchecked")
         final UserPersistence<Principal> mockUserPersistence =
                 createMock(UserPersistence.class);
 
         expect(mockUserPersistence.modifyUser(userObject)).andReturn(
                 userObject).once();
+//
+//        expect(mockRequest.getRemoteAddr()).andReturn(requestURL).
+//                once();
 
-        expect(mockRequest.getMethod()).andReturn("POST").once();
-        expect(mockRequest.getRemoteAddr()).andReturn(requestURL).
-                once();
+        mockResponse.setHeader("Location", "/CADCtest?idType=http");
+        expectLastCall().once();
 
-        mockResponse.sendRedirect(requestURL);
+        mockResponse.setStatus(200);
         expectLastCall().once();
 
         mockResponse.setContentType("application/json");
@@ -128,9 +130,7 @@ public class ModifyUserActionTest
 
         replay(mockRequest, mockResponse, mockUserPersistence);
 
-        final UserLogInfo userLogInfo = new UserLogInfo(mockRequest);
-        final ModifyUserAction testSubject = new ModifyUserAction(userLogInfo,
-                                                                  inputStream)
+        final ModifyUserAction testSubject = new ModifyUserAction(inputStream)
         {
             @Override
             @SuppressWarnings("unchecked")
@@ -142,11 +142,10 @@ public class ModifyUserActionTest
 
         testSubject.setAcceptedContentType("application/json");
         testSubject.response = mockResponse;
+        UserLogInfo logInfo = createMock(UserLogInfo.class);
+        testSubject.setLogInfo(logInfo);
+        testSubject.doAction();
 
-        testSubject.run();
-
-        assertEquals("Wrong username logged.", "CADCtest",
-                     userLogInfo.userName);
         verify(mockRequest, mockResponse, mockUserPersistence);
     }
 }
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 4241c015a6a3f7a2d21b0a0e41f0fac1ff37d45b..b833d66005d722cf0bf4d72c05fdcd6a22b48cfd 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
@@ -92,10 +92,10 @@ public class UserActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("");
-            EasyMock.expect(request.getMethod()).andReturn("PUT");
             EasyMock.expect(request.getInputStream()).andReturn(null);
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.getUsersAction(request, null);
+
+            UsersAction action = UsersActionFactory.httpPutFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof CreateUserAction);
         }
@@ -113,10 +113,9 @@ public class UserActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("userName");
-            EasyMock.expect(request.getMethod()).andReturn("DELETE");
             EasyMock.expect(request.getParameter("idType")).andReturn("sessionID");
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.getUsersAction(request, null);
+            UsersAction action = UsersActionFactory.httpDeleteFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof DeleteUserAction);
         }
@@ -134,10 +133,9 @@ public class UserActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("userName");
-            EasyMock.expect(request.getMethod()).andReturn("GET");
             EasyMock.expect(request.getParameter("idType")).andReturn("sessionID");
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.getUsersAction(request, null);
+            UsersAction action = UsersActionFactory.httpGetFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof GetUserAction);
         }
@@ -155,9 +153,8 @@ public class UserActionFactoryTest
         {
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("");
-            EasyMock.expect(request.getMethod()).andReturn("GET");
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.getUsersAction(request, null);
+            UsersAction action = UsersActionFactory.httpGetFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof GetUsersAction);
         }
@@ -178,14 +175,13 @@ public class UserActionFactoryTest
 
             HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
             EasyMock.expect(request.getPathInfo()).andReturn("userName");
-            EasyMock.expect(request.getMethod()).andReturn("POST");
-            EasyMock.expect(request.getRequestURL()).andReturn(sb);
-            EasyMock.expect(request.getContextPath()).andReturn("");
-            EasyMock.expect(request.getServletPath()).andReturn("");
+            //EasyMock.expect(request.getRequestURL()).andReturn(sb);
+            //EasyMock.expect(request.getContextPath()).andReturn("");
+            //EasyMock.expect(request.getServletPath()).andReturn("");
             EasyMock.expect(request.getInputStream()).andReturn(null);
-            EasyMock.expect(request.getParameter("idType")).andReturn("sessionID");
+            //EasyMock.expect(request.getParameter("idType")).andReturn("sessionID");
             EasyMock.replay(request);
-            UsersAction action = UsersActionFactory.getUsersAction(request, null);
+            UsersAction action = UsersActionFactory.httpPostFactory().createAction(request);
             EasyMock.verify(request);
             Assert.assertTrue("Wrong action", action instanceof ModifyUserAction);
         }
@@ -197,41 +193,23 @@ public class UserActionFactoryTest
     }
 
     @Test
-    public void testBadRequests()
+    public void testBadPostRequest()
     {
         try
         {
-            TestRequest[] testRequests =
+            HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
+            EasyMock.expect(request.getPathInfo()).andReturn("");
+            EasyMock.replay(request);
+            try
             {
-                new TestRequest("", "POST"),
-                new TestRequest("", "DELETE"),
-                new TestRequest("", "HEAD"),
-            };
-
-            for (TestRequest testRequest : testRequests)
+                UsersActionFactory.httpPostFactory().createAction(request);
+                Assert.fail("Should have been a bad request");
+            }
+            catch (IllegalArgumentException e)
             {
-
-                log.debug("Testing: " + testRequest);
-
-                HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
-                EasyMock.expect(request.getPathInfo()).andReturn(testRequest.path);
-                EasyMock.expect(request.getMethod()).andReturn(testRequest.method);
-                if (testRequest.paramName != null)
-                {
-                    EasyMock.expect(request.getParameter(testRequest.paramName)).andReturn(testRequest.paramValue);
-                }
-                EasyMock.replay(request);
-                try
-                {
-                    UsersActionFactory.getUsersAction(request, null);
-                    Assert.fail("Should have been a bad request: " + testRequest.method + " on " + testRequest.path);
-                }
-                catch (IllegalArgumentException e)
-                {
-                    // expected
-                }
-                EasyMock.verify(request);
+                // expected
             }
+            EasyMock.verify(request);
         }
         catch (Throwable t)
         {
@@ -240,31 +218,56 @@ public class UserActionFactoryTest
         }
     }
 
-    private class TestRequest
+    @Test
+    public void testBadDeleteRequest()
     {
-        public String path;
-        public String method;
-        public String paramName;
-        public String paramValue;
-
-        public TestRequest(String path, String method)
+        try
         {
-            this(path, method, null, null);
+            HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
+            EasyMock.expect(request.getPathInfo()).andReturn("");
+            EasyMock.replay(request);
+            try
+            {
+                UsersActionFactory.httpDeleteFactory().createAction(request);
+                Assert.fail("Should have been a bad request");
+            }
+            catch (IllegalArgumentException e)
+            {
+                // expected
+            }
+            EasyMock.verify(request);
         }
-        public TestRequest(String path, String method, String paramName, String paramValue)
+        catch (Throwable t)
         {
-            this.path = path;
-            this.method = method;
-            this.paramName = paramName;
-            this.paramValue = paramValue;
+            log.error(t.getMessage(), t);
+            Assert.fail("unexpected error: " + t.getMessage());
         }
-        @Override
-        public String toString()
+    }
+
+    @Test
+    public void testBadHeadRequest()
+    {
+        try
         {
-            return method + " on path " + path +
-                ((paramName == null) ? "" : "?" + paramName + "=" + paramValue);
+            HttpServletRequest request = EasyMock.createMock(HttpServletRequest.class);
+            EasyMock.replay(request);
+            try
+            {
+                UsersActionFactory.httpHeadFactory().createAction(request);
+                Assert.fail("Should have been a bad request");
+            }
+            catch (UnsupportedOperationException e)
+            {
+                // expected
+            }
+            EasyMock.verify(request);
+        }
+        catch (Throwable t)
+        {
+            log.error(t.getMessage(), t);
+            Assert.fail("unexpected error: " + t.getMessage());
         }
-
     }
 
+
 }
diff --git a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersActionTest.java b/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersActionTest.java
deleted file mode 100644
index 9f68864b52147a94be6ea974d9ae7fd5cdfe178c..0000000000000000000000000000000000000000
--- a/projects/cadcAccessControl-Server/test/src/ca/nrc/cadc/ac/server/web/users/UsersActionTest.java
+++ /dev/null
@@ -1,213 +0,0 @@
-/*
- ************************************************************************
- *******************  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.User;
-import ca.nrc.cadc.ac.UserNotFoundException;
-import ca.nrc.cadc.ac.server.UserPersistence;
-import ca.nrc.cadc.auth.HttpPrincipal;
-import ca.nrc.cadc.net.TransientException;
-import ca.nrc.cadc.util.Log4jInit;
-
-import java.io.*;
-import java.security.AccessControlException;
-import java.security.Principal;
-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.Ignore;
-import org.junit.Test;
-
-import static org.junit.Assert.*;
-
-/**
- * @author jburke
- */
-public class UsersActionTest
-{
-    private final static Logger log = Logger.getLogger(UsersActionTest.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);
-
-        UserLogInfo logInfo = createMock(UserLogInfo.class);
-        logInfo.setSuccess(false);
-        expectLastCall().once();
-        logInfo.setMessage("Internal Transient Error: foo");
-        expectLastCall().once();
-        replay(logInfo);
-
-        UsersActionImpl action = new UsersActionImpl(logInfo);
-        action.setException(new TransientException("foo"));
-        action.doAction(null, response);
-    }
-
-    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);
-
-        UserLogInfo logInfo = createMock(UserLogInfo.class);
-        logInfo.setMessage(message);
-        expectLastCall().once();
-        replay(logInfo);
-
-        UsersActionImpl action = new UsersActionImpl(logInfo);
-        action.setException(e);
-        action.doAction(null, response);
-    }
-
-    public class UsersActionImpl extends UsersAction
-    {
-        Exception exception;
-
-        public UsersActionImpl(UserLogInfo logInfo)
-        {
-            super(logInfo);
-        }
-
-        public Object run() throws Exception
-        {
-            throw exception;
-        }
-
-        public void setException(Exception e)
-        {
-            this.exception = e;
-        }
-    }
-
-}