Refactored webapplication specific configurations into a new project. cdmlib-remote...
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / BaseController.java
1 // $Id$
2 /**
3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
6 *
7 * The contents of this file are subject to the Mozilla Public License Version 1.1
8 * See LICENSE.TXT at the top of this package for the full license terms.
9 */
10
11 package eu.etaxonomy.cdm.remote.controller;
12
13 import java.beans.PropertyDescriptor;
14 import java.io.IOException;
15 import java.lang.reflect.InvocationTargetException;
16 import java.lang.reflect.Method;
17 import java.util.ArrayList;
18 import java.util.Arrays;
19 import java.util.Collection;
20 import java.util.List;
21 import java.util.UUID;
22
23 import javax.servlet.http.HttpServletRequest;
24 import javax.servlet.http.HttpServletResponse;
25
26 import org.apache.commons.beanutils.PropertyUtils;
27 import org.apache.commons.io.FilenameUtils;
28 import org.apache.commons.lang.StringUtils;
29 import org.hibernate.mapping.Map;
30 import org.springframework.web.bind.WebDataBinder;
31 import org.springframework.web.bind.annotation.InitBinder;
32 import org.springframework.web.bind.annotation.PathVariable;
33 import org.springframework.web.bind.annotation.RequestMapping;
34 import org.springframework.web.bind.annotation.RequestMethod;
35 import org.springframework.web.bind.annotation.RequestParam;
36 import org.springframework.web.servlet.ModelAndView;
37
38 import eu.etaxonomy.cdm.api.service.IService;
39 import eu.etaxonomy.cdm.api.service.pager.Pager;
40 import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
41 import eu.etaxonomy.cdm.model.common.CdmBase;
42 import eu.etaxonomy.cdm.remote.editor.UUIDPropertyEditor;
43
44 /**
45 * based on org.cateproject.controller.common
46 * @author b.clark
47 * @author a.kohlbecker
48 *
49 * @param <T>
50 * @param <SERVICE>
51 */
52
53 public abstract class BaseController<T extends CdmBase, SERVICE extends IService<T>> extends AbstractController {
54
55 protected SERVICE service;
56
57 protected Class<T> baseClass;
58
59 public abstract void setService(SERVICE service);
60
61 @InitBinder
62 public void initBinder(WebDataBinder binder) {
63 binder.registerCustomEditor(UUID.class, new UUIDPropertyEditor());
64 }
65
66 //TODO implement bulk version of this method
67 @RequestMapping(method = RequestMethod.GET)
68 public T doGet(@PathVariable("uuid") UUID uuid,
69 HttpServletRequest request,
70 HttpServletResponse response) throws IOException {
71 logger.info("doGet() " + request.getServletPath());
72 T obj = (T) getCdmBaseInstance(uuid, response, initializationStrategy);
73 return obj;
74 }
75
76 /**
77 * @param uuid
78 * @param request
79 * @param response
80 * @return
81 * @throws IOException
82 *
83 * TODO implement bulk version of this method
84 */
85 @RequestMapping(value = "*", method = RequestMethod.GET)
86 public ModelAndView doGetMethod(
87 @PathVariable("uuid") UUID uuid,
88 // doPage request parametes
89 @RequestParam(value = "pageNumber", required = false) Integer pageNumber,
90 @RequestParam(value = "pageSize", required = false) Integer pageSize,
91 // doList request parametes
92 @RequestParam(value = "start", required = false) Integer start,
93 @RequestParam(value = "limit", required = false) Integer limit,
94 HttpServletRequest request,
95 HttpServletResponse response) throws IOException {
96
97 ModelAndView modelAndView = new ModelAndView();
98
99 String servletPath = request.getServletPath();
100 String baseName = FilenameUtils.getBaseName(servletPath);
101
102 logger.info("doGetMethod()[doGet" + StringUtils.capitalize(baseName) + "] " + request.getServletPath());
103
104 // <CUT
105 // T instance = getCdmBaseInstance(uuid, response, (List<String>)null);
106
107 //Class<?> propertyClass = propertyClass(instance, baseName);
108
109 Object objectFromProperty = doGetProperty(uuid, baseName, response);// invokeProperty(instance, baseName, response);
110
111 // CUT>
112
113 if(objectFromProperty != null){
114
115 if( Collection.class.isAssignableFrom(objectFromProperty.getClass())){
116 // Map types cannot be returend as list or in a pager!
117
118 Collection c = (Collection)objectFromProperty;
119 if(start != null){
120 // return list
121 limit = (limit == null ? DEFAULT_PAGE_SIZE : limit);
122 Collection sub_c = subCollection(c, start, limit);
123 modelAndView.addObject(sub_c);
124
125 } else {
126 pageSize = (pageSize == null ? DEFAULT_PAGE_SIZE : pageSize);
127 pageNumber = (pageNumber == null ? 0 : pageNumber);
128 start = pageNumber * pageSize;
129 List sub_c = subCollection(c, start, pageSize);
130 Pager p = new DefaultPagerImpl(pageNumber, c.size(), pageSize, sub_c);
131 modelAndView.addObject(p);
132 }
133
134 } else {
135 modelAndView.addObject(objectFromProperty);
136 }
137
138 }
139
140 if(modelAndView.isEmpty()){
141 return null;
142 } else {
143
144 return modelAndView;
145 }
146 }
147
148 public Object doGetProperty(UUID uuid, String property, HttpServletResponse response) throws IOException{
149 T instance = getCdmBaseInstance(uuid, response, (List<String>) null);
150
151 Object objectFromProperty = invokeProperty(instance, property, response);
152
153 return objectFromProperty;
154 }
155
156 private Class<?> propertyClass(T instance, String baseName) {
157 PropertyDescriptor propertyDescriptor = null;
158 Class<?> c = null;
159 try {
160 propertyDescriptor = PropertyUtils.getPropertyDescriptor(instance, baseName);
161 if(propertyDescriptor != null){
162 c = propertyDescriptor.getClass();
163 }
164 } catch (IllegalAccessException e) {
165 // TODO Auto-generated catch block
166 e.printStackTrace();
167 } catch (InvocationTargetException e) {
168 // TODO Auto-generated catch block
169 e.printStackTrace();
170 } catch (NoSuchMethodException e) {
171 // TODO Auto-generated catch block
172 e.printStackTrace();
173 }
174 return c;
175 }
176
177 /**
178 * @param <SUB_T>
179 * @param clazz
180 * @param uuid
181 * @param response
182 * @param pathProperties
183 * @return
184 * @throws IOException
185 */
186 @SuppressWarnings("unchecked")
187 protected final <SUB_T extends T> SUB_T getCdmBaseInstance(Class<SUB_T> clazz, UUID uuid, HttpServletResponse response, List<String> pathProperties)
188 throws IOException {
189
190 CdmBase cdmBaseObject = getCdmBaseInstance(uuid, response, pathProperties);
191 if(!clazz.isAssignableFrom(cdmBaseObject.getClass())){
192 HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
193 }
194 return (SUB_T) cdmBaseObject;
195 }
196
197 /**
198 * @param <SUB_T>
199 * @param clazz
200 * @param uuid
201 * @param response
202 * @param pathProperty
203 * @return
204 * @throws IOException
205 */
206 @SuppressWarnings("unchecked")
207 protected final <SUB_T extends T> SUB_T getCdmBaseInstance(Class<SUB_T> clazz, UUID uuid, HttpServletResponse response, String pathProperty)
208 throws IOException {
209
210 CdmBase cdmBaseObject = getCdmBaseInstance(uuid, response, pathProperty);
211 if(!clazz.isAssignableFrom(cdmBaseObject.getClass())){
212 HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
213 }
214 return (SUB_T) cdmBaseObject;
215 }
216
217 /**
218 * @param uuid
219 * @param response
220 * @param pathProperty
221 * @return
222 * @throws IOException
223 */
224 protected final T getCdmBaseInstance(UUID uuid, HttpServletResponse response, String pathProperty)
225 throws IOException {
226 return getCdmBaseInstance(baseClass, uuid, response, Arrays
227 .asList(new String[] { pathProperty }));
228 }
229
230
231 /**
232 * @param uuid
233 * @param response
234 * @param pathProperties
235 * @return
236 * @throws IOException
237 */
238 protected final T getCdmBaseInstance(UUID uuid, HttpServletResponse response, List<String> pathProperties)
239 throws IOException {
240 return getCdmBaseInstance(baseClass, service, uuid, response, pathProperties);
241 }
242
243 /**
244 * @param <CDM_BASE>
245 * @param clazz
246 * @param service
247 * @param uuid
248 * @param response
249 * @param pathProperties
250 * @return
251 * @throws IOException
252 */
253 protected final <CDM_BASE extends CdmBase> CDM_BASE getCdmBaseInstance(Class<CDM_BASE> clazz, IService<CDM_BASE> service, UUID uuid, HttpServletResponse response, List<String> pathProperties)
254 throws IOException {
255
256 CDM_BASE cdmBaseObject = service.load(uuid, pathProperties);
257 if (cdmBaseObject == null) {
258 HttpStatusMessage.UUID_NOT_FOUND.send(response);
259 }
260 return cdmBaseObject;
261 }
262
263 /**
264 * @param instance
265 * @param baseName
266 * @param response
267 * @return
268 * @throws IOException
269 */
270 private final Object invokeProperty(T instance,
271 String baseName, HttpServletResponse response) throws IOException {
272
273 Object result = null;
274 try {
275 PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(instance, baseName);
276 Method method = propertyDescriptor.getReadMethod();
277
278 Class<?> returnType = method.getReturnType();
279
280 if(CdmBase.class.isAssignableFrom(returnType)
281 || Collection.class.isAssignableFrom(returnType)
282 || Map.class.isAssignableFrom(returnType)){
283 result = method.invoke(instance, (Object[])null);
284 }else{
285 HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
286 }
287 } catch (SecurityException e) {
288 logger.error("SecurityException: ", e);
289 HttpStatusMessage.INTERNAL_ERROR.send(response);
290 } catch (NoSuchMethodException e) {
291 HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
292 } catch (IllegalArgumentException e) {
293 HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
294 } catch (IllegalAccessException e) {
295 HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
296 } catch (InvocationTargetException e) {
297 HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
298 }
299 return result;
300 }
301
302 private <E> List<E> subCollection(Collection<? extends E> c, Integer start, Integer length){
303 List<E> sub_c = new ArrayList<E>(length);
304 if(c.size() > length){
305 E[] a = (E[]) c.toArray();
306 for(int i = start; i < start + length; i++){
307 sub_c.add(a[i]);
308 }
309 } else {
310 sub_c.addAll(c);
311 }
312 return sub_c;
313
314 }
315
316
317 /* TODO implement
318
319 private Validator validator;
320
321 private javax.validation.Validator javaxValidator;
322
323 @RequestMapping(method = RequestMethod.PUT, headers="content-type=multipart/form-data")
324 public T doPutForm(@PathVariable(value = "uuid") UUID uuid, @ModelAttribute("object") T object, BindingResult result) {
325 object.setUuid(uuid);
326 validator.validate(object, result);
327 if (result.hasErrors()) {
328 throw new Error();
329 // set http status code depending upon what happened, possibly return
330 // the put object and errors so that they can be rendered into a suitable error response
331 } else {
332 // requires merging detached object ?gilead?
333 service.save(object);
334 }
335
336 return object;
337 }
338
339 @RequestMapping(method = RequestMethod.PUT, headers="content-type=text/json")
340 public T doPutJSON(@PathVariable(value = "uuid") UUID uuid, @RequestBody String jsonMessage) {
341 JSONObject jsonObject = JSONObject.fromObject(jsonMessage);
342 T object = (T)JSONObject.toBean(jsonObject, this.getClass());
343
344
345 Set<ConstraintViolation<T>> constraintViolations = javaxValidator.validate(object);
346 if (!constraintViolations.isEmpty()) {
347 throw new Error();
348 // set http status code depending upon what happened, possibly return
349 // the put object and errors so that they can be rendered into a suitable error response
350 } else {
351 // requires merging detached object ?gilead?
352 service.save(object);
353 }
354
355 return object;
356 }
357
358 @RequestMapping(method = RequestMethod.PUT) // the cdm-server may not allow clients to specify the uuid for resources
359 public T doPut(@PathVariable(value = "uuid") UUID uuid, @ModelAttribute("object") T object, BindingResult result) {
360 validator.validate(object, result);
361 if (result.hasErrors()) {
362 // set http status code depending upon what happened, possibly return
363 // the put object and errors so that they can be rendered into a suitable error response
364 } else {
365 service.save(object);
366 }
367 }
368
369 @RequestMapping(method = RequestMethod.DELETE)
370 public void doDelete(@PathVariable(value = "uuid") UUID uuid) {
371 T object = service.find(uuid);
372 // provided the object exists
373 service.delete(uuid);
374 // might return 204 or 200
375 }
376 }
377 */
378
379
380 }