Retry transactions in the face of optimistic locking exceptions for all "write" trans...
[pithos] / src / gr / ebs / gss / server / rest / GroupsHandler.java
1 /*
2  * Copyright 2008, 2009 Electronic Business Systems Ltd.
3  *
4  * This file is part of GSS.
5  *
6  * GSS is free software: you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation, either version 3 of the License, or
9  * (at your option) any later version.
10  *
11  * GSS is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with GSS.  If not, see <http://www.gnu.org/licenses/>.
18  */
19 package gr.ebs.gss.server.rest;
20
21 import gr.ebs.gss.client.exceptions.DuplicateNameException;
22 import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
23 import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
24 import gr.ebs.gss.client.exceptions.RpcException;
25 import gr.ebs.gss.server.domain.User;
26 import gr.ebs.gss.server.domain.dto.GroupDTO;
27 import gr.ebs.gss.server.domain.dto.UserDTO;
28
29 import java.io.IOException;
30 import java.net.URLDecoder;
31 import java.net.URLEncoder;
32 import java.util.List;
33 import java.util.concurrent.Callable;
34
35 import javax.servlet.http.HttpServletRequest;
36 import javax.servlet.http.HttpServletResponse;
37
38 import org.apache.commons.logging.Log;
39 import org.apache.commons.logging.LogFactory;
40 import org.json.JSONArray;
41 import org.json.JSONException;
42 import org.json.JSONObject;
43
44
45 /**
46  * A class that handles operations on the 'groups' namespace.
47  *
48  * @author past
49  */
50 public class GroupsHandler extends RequestHandler {
51         /**
52          * The logger.
53          */
54         private static Log logger = LogFactory.getLog(GroupsHandler.class);
55
56     /**
57      * Serve the groups namespace for the user.
58      *
59      * @param req The servlet request we are processing
60      * @param resp The servlet response we are processing
61      * @throws IOException if an input/output error occurs
62          */
63         void serveGroups(HttpServletRequest req, HttpServletResponse resp) throws IOException {
64         String parentUrl = getContextPath(req, true);
65         String path = getInnerPath(req, PATH_GROUPS);
66                 if (path.equals(""))
67                         path = "/";
68
69         User user = getUser(req);
70         User owner = getOwner(req);
71         if (!owner.equals(user)) {
72                 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED);
73                 return;
74         }
75         if (path.equals("/"))
76                         // Request to serve all groups
77                 try {
78                 List<GroupDTO> groups = getService().getGroups(owner.getId());
79                 JSONArray json = new JSONArray();
80                 for (GroupDTO group: groups) {
81                         JSONObject j = new JSONObject();
82                         j.put("name", group.getName()).
83                                 put("uri", parentUrl + URLEncoder.encode(group.getName(),"UTF-8"));
84                                 json.put(j);
85                 }
86
87                 sendJson(req, resp, json.toString());
88                 } catch (ObjectNotFoundException e) {
89                         logger.error("User not found", e);
90                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
91                         return;
92                 } catch (RpcException e) {
93                         logger.error("", e);
94                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
95                         return;
96                 } catch (JSONException e) {
97                         logger.error("", e);
98                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
99                         return;
100                         }
101                 else {
102                 // Chop any trailing slash
103                 path = path.endsWith("/")? path.substring(0, path.length()-1): path;
104                 // Chop any leading slash
105                 path = path.startsWith("/")? path.substring(1): path;
106                 int slash = path.indexOf('/');
107                         try {
108                         if (slash != -1) {
109                                 // Request to serve group member
110                                 if (logger.isDebugEnabled())
111                                         logger.debug("Serving member " + path.substring(slash + 1) +
112                                                                 " from group " + path.substring(0, slash));
113                                 GroupDTO group = getService().getGroup(owner.getId(), URLDecoder.decode(path.substring(0, slash),"UTF-8"));
114                                 for (UserDTO u: group.getMembers())
115                                         if (u.getUsername().equals(path.substring(slash + 1))) {
116                                                 // Build the proper parent URL
117                                                 String pathInfo = req.getPathInfo();
118                                                 parentUrl = parentUrl.replaceFirst(pathInfo, "");
119                                         JSONObject json = new JSONObject();
120                                         json.put("username", u.getUsername()).put("name", u.getName()).
121                                                         put("home", parentUrl + u.getUsername());
122
123                                         sendJson(req, resp, json.toString());
124                                         }
125                         } else {
126                                 // Request to serve group
127                                 if (logger.isDebugEnabled())
128                                         logger.debug("Serving group " + path);
129                                 GroupDTO group = getService().getGroup(owner.getId(), URLDecoder.decode(path,"UTF-8"));
130                                 JSONArray json = new JSONArray();
131                                 for (UserDTO u: group.getMembers())
132                                         json.put(parentUrl + u.getUsername());
133
134                                 sendJson(req, resp, json.toString());
135                         }
136                         } catch (ObjectNotFoundException e) {
137                         logger.error("", e);
138                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
139                         return;
140                         } catch (RpcException e) {
141                         logger.error("", e);
142                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
143                         return;
144                         } catch (JSONException e) {
145                                 logger.error("", e);
146                                 resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
147                                 return;
148                         }
149         }
150         // Workaround for IE's broken caching behavior.
151                 resp.setHeader("Expires", "-1");
152         }
153
154         /**
155          * Handle POST requests in the groups namespace.
156          *
157      * @param req The servlet request we are processing
158      * @param resp The servlet response we are processing
159      * @throws IOException if an input/output error occurs
160          */
161         void postGroup(HttpServletRequest req, HttpServletResponse resp) throws IOException {
162         // Identify the requested resource path
163         String path = getInnerPath(req, PATH_GROUPS);
164                 if (path.equals(""))
165                         path = "/";
166
167                 try {
168                 User user = getUser(req);
169                 final User owner = getOwner(req);
170                 if (!owner.equals(user))
171                         throw new InsufficientPermissionsException("User " + user.getUsername()
172                                                 + " does not have permission to modify the groups owned by "
173                                                 + owner.getUsername());
174                 if (path.equals("/")) {
175                         // Request to add group
176                         final String group = req.getParameter(GROUP_PARAMETER);
177                         if (logger.isDebugEnabled())
178                                 logger.debug("Adding group " + group);
179                                 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
180                                                 @Override
181                                                 public Void call() throws Exception {
182                                                         getService().createGroup(owner.getId(), group);
183                                                         return null;
184                                                 }
185                                         });
186                                 resp.setStatus(HttpServletResponse.SC_CREATED);
187                 } else {
188                         // Request to add group member
189                         String username = req.getParameter(USERNAME_PARAMETER);
190                         // Chop any trailing slash
191                         path = path.endsWith("/")? path.substring(0, path.length()-1): path;
192                         // Chop any leading slash
193                         path = path.startsWith("/")? path.substring(1): path;
194                         if (logger.isDebugEnabled())
195                                 logger.debug("Adding member " + username +
196                                                         " to group " + path);
197                         final GroupDTO group = getService().getGroup(owner.getId(), URLDecoder.decode(path,"UTF-8"));
198                         final User member = getService().findUser(username);
199                         if (member == null) {
200                                 resp.sendError(HttpServletResponse.SC_NOT_FOUND, "User " + username + " not found");
201                                 return;
202                         }
203                         new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
204                                         @Override
205                                         public Void call() throws Exception {
206                                                 getService().addUserToGroup(owner.getId(), group.getId(), member.getId());
207                                                 return null;
208                                         }
209                                 });
210                         resp.setStatus(HttpServletResponse.SC_CREATED);
211                 }
212                 // Workaround for IE's broken caching behavior.
213                         resp.setHeader("Expires", "-1");
214                 } catch (ObjectNotFoundException e) {
215                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
216                 } catch (IllegalArgumentException e) {
217                         resp.sendError(HttpServletResponse.SC_BAD_REQUEST, e.getMessage());
218                 } catch (DuplicateNameException e) {
219                         resp.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
220                 } catch (RpcException e) {
221                         logger.error("", e);
222                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
223                 } catch (InsufficientPermissionsException e) {
224                         resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, e.getMessage());
225                 } catch (Exception e) {
226                         logger.error("", e);
227                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
228                 }
229
230         }
231
232         /**
233          * Handle DELETE requests in the groups namespace.
234          *
235      * @param req The servlet request we are processing
236      * @param resp The servlet response we are processing
237      * @throws IOException if an input/output error occurs
238          */
239         void deleteGroup(HttpServletRequest req, HttpServletResponse resp) throws IOException {
240         String path = getInnerPath(req, PATH_GROUPS);
241                 if (path.equals(""))
242                         path = "/";
243
244         if (path.equals("/"))
245                         resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, METHOD_GET + ", " + METHOD_POST);
246                 else {
247                 // Chop any trailing slash
248                 path = path.endsWith("/")? path.substring(0, path.length()-1): path;
249                 // Chop any leading slash
250                 path = path.startsWith("/")? path.substring(1): path;
251                 int slash = path.indexOf('/');
252                 try {
253                 User user = getUser(req);
254                 final User owner = getOwner(req);
255                 if (!owner.equals(user))
256                         throw new InsufficientPermissionsException("User " + user.getUsername()
257                                                 + " does not have permission to modify the groups owned by "
258                                                 + owner.getUsername());
259                 if (slash != -1) {
260                         // Request to delete group member
261                         if (logger.isDebugEnabled())
262                                 logger.debug("Removing member " + path.substring(slash + 1) +
263                                                         " from group " + path.substring(0, slash));
264                         final GroupDTO group = getService().getGroup(owner.getId(), URLDecoder.decode(path.substring(0, slash),"UTF-8"));
265                         for (final UserDTO u: group.getMembers())
266                                 if (u.getUsername().equals(path.substring(slash + 1)))
267                                         new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
268                                                 @Override
269                                                 public Void call() throws Exception {
270                                                         getService().removeMemberFromGroup(owner.getId(), group.getId(), u.getId());
271                                                         return null;
272                                                 }
273                                         });
274                 } else {
275                         if (logger.isDebugEnabled())
276                                 logger.debug("Removing group " + path);
277                                 final GroupDTO group = getService().getGroup(owner.getId(), URLDecoder.decode(path,"UTF-8"));
278                                 new TransactionHelper<Void>().tryExecute(new Callable<Void>() {
279                                         @Override
280                                         public Void call() throws Exception {
281                                                 getService().deleteGroup(owner.getId(), group.getId());
282                                                 return null;
283                                         }
284                                 });
285                 }
286                         resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
287                         // Workaround for IE's broken caching behavior.
288                         resp.setHeader("Expires", "-1");
289                 } catch (RpcException e) {
290                         logger.error("", e);
291                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
292                 } catch (ObjectNotFoundException e) {
293                         resp.sendError(HttpServletResponse.SC_NOT_FOUND, e.getMessage());
294                         } catch (InsufficientPermissionsException e) {
295                                 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED, e.getMessage());
296                         } catch (Exception e) {
297                         logger.error("", e);
298                         resp.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
299                         }
300         }
301         }
302
303 }