missing implementations for the generic controller architeture (works for data portal...
[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 T instance = getCdmBaseInstance(uuid, response, Arrays.asList(new String[]{baseName + ".titleCache"}));
105
106 //Class<?> propertyClass = propertyClass(instance, baseName);
107
108 Object objectFromProperty = invokeProperty(instance, baseName, response);
109 if(objectFromProperty != null){
110
111 if( Collection.class.isAssignableFrom(objectFromProperty.getClass())){
112 // Map types cannot be returend as list or in a pager!
113
114 Collection c = (Collection)objectFromProperty;
115 if(start != null){
116 // return list
117 limit = (limit == null ? DEFAULT_PAGE_SIZE : limit);
118 Collection sub_c = subCollection(c, start, limit);
119 modelAndView.addObject(sub_c);
120
121 } else {
122 pageSize = (pageSize == null ? DEFAULT_PAGE_SIZE : pageSize);
123 pageNumber = (pageNumber == null ? 0 : pageNumber);
124 start = pageNumber * pageSize;
125 List sub_c = subCollection(c, start, pageSize);
126 Pager p = new DefaultPagerImpl(pageNumber, c.size(), pageSize, sub_c);
127 modelAndView.addObject(p);
128 }
129
130 } else {
131 modelAndView.addObject(objectFromProperty);
132 }
133
134 }
135
136 if(modelAndView.isEmpty()){
137 return null;
138 } else {
139
140 return modelAndView;
141 }
142 }
143
144 private Class<?> propertyClass(T instance, String baseName) {
145 PropertyDescriptor propertyDescriptor = null;
146 Class<?> c = null;
147 try {
148 propertyDescriptor = PropertyUtils.getPropertyDescriptor(instance, baseName);
149 if(propertyDescriptor != null){
150 c = propertyDescriptor.getClass();
151 }
152 } catch (IllegalAccessException e) {
153 // TODO Auto-generated catch block
154 e.printStackTrace();
155 } catch (InvocationTargetException e) {
156 // TODO Auto-generated catch block
157 e.printStackTrace();
158 } catch (NoSuchMethodException e) {
159 // TODO Auto-generated catch block
160 e.printStackTrace();
161 }
162 return c;
163 }
164
165 /**
166 * @param <SUB_T>
167 * @param clazz
168 * @param uuid
169 * @param response
170 * @param pathProperties
171 * @return
172 * @throws IOException
173 */
174 @SuppressWarnings("unchecked")
175 protected final <SUB_T extends T> SUB_T getCdmBaseInstance(Class<SUB_T> clazz, UUID uuid, HttpServletResponse response, List<String> pathProperties)
176 throws IOException {
177
178 CdmBase cdmBaseObject = getCdmBaseInstance(uuid, response, pathProperties);
179 if(!clazz.isAssignableFrom(cdmBaseObject.getClass())){
180 HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
181 }
182 return (SUB_T) cdmBaseObject;
183 }
184
185 /**
186 * @param <SUB_T>
187 * @param clazz
188 * @param uuid
189 * @param response
190 * @param pathProperty
191 * @return
192 * @throws IOException
193 */
194 @SuppressWarnings("unchecked")
195 protected final <SUB_T extends T> SUB_T getCdmBaseInstance(Class<SUB_T> clazz, UUID uuid, HttpServletResponse response, String pathProperty)
196 throws IOException {
197
198 CdmBase cdmBaseObject = getCdmBaseInstance(uuid, response, pathProperty);
199 if(!clazz.isAssignableFrom(cdmBaseObject.getClass())){
200 HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
201 }
202 return (SUB_T) cdmBaseObject;
203 }
204
205 /**
206 * @param uuid
207 * @param response
208 * @param pathProperty
209 * @return
210 * @throws IOException
211 */
212 protected final T getCdmBaseInstance(UUID uuid, HttpServletResponse response, String pathProperty)
213 throws IOException {
214 return getCdmBaseInstance(baseClass, uuid, response, Arrays
215 .asList(new String[] { pathProperty }));
216 }
217
218
219 /**
220 * @param uuid
221 * @param response
222 * @param pathProperties
223 * @return
224 * @throws IOException
225 */
226 protected final T getCdmBaseInstance(UUID uuid, HttpServletResponse response, List<String> pathProperties)
227 throws IOException {
228 return getCdmBaseInstance(baseClass, service, uuid, response, pathProperties);
229 }
230
231 /**
232 * @param <CDM_BASE>
233 * @param clazz
234 * @param service
235 * @param uuid
236 * @param response
237 * @param pathProperties
238 * @return
239 * @throws IOException
240 */
241 protected final <CDM_BASE extends CdmBase> CDM_BASE getCdmBaseInstance(Class<CDM_BASE> clazz, IService<CDM_BASE> service, UUID uuid, HttpServletResponse response, List<String> pathProperties)
242 throws IOException {
243
244 CDM_BASE cdmBaseObject = service.load(uuid, pathProperties);
245 if (cdmBaseObject == null) {
246 HttpStatusMessage.UUID_NOT_FOUND.send(response);
247 }
248 return cdmBaseObject;
249 }
250
251 /**
252 * @param instance
253 * @param baseName
254 * @param response
255 * @return
256 * @throws IOException
257 */
258 private final Object invokeProperty(T instance,
259 String baseName, HttpServletResponse response) throws IOException {
260
261 Object result = null;
262 try {
263 PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(instance, baseName);
264 Method method = propertyDescriptor.getReadMethod();
265
266 Class<?> returnType = method.getReturnType();
267
268 if(CdmBase.class.isAssignableFrom(returnType)
269 || Collection.class.isAssignableFrom(returnType)
270 || Map.class.isAssignableFrom(returnType)){
271 result = method.invoke(instance, (Object[])null);
272 }else{
273 HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
274 }
275 } catch (SecurityException e) {
276 logger.error("SecurityException: ", e);
277 HttpStatusMessage.INTERNAL_ERROR.send(response);
278 } catch (NoSuchMethodException e) {
279 HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
280 } catch (IllegalArgumentException e) {
281 HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
282 } catch (IllegalAccessException e) {
283 HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
284 } catch (InvocationTargetException e) {
285 HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
286 }
287 return result;
288 }
289
290 private <E> List<E> subCollection(Collection<? extends E> c, Integer start, Integer length){
291 List<E> sub_c = new ArrayList<E>(length);
292 if(c.size() > length){
293 E[] a = (E[]) c.toArray();
294 for(int i = start; i < start + length; i++){
295 sub_c.add(a[i]);
296 }
297 } else {
298 sub_c.addAll(c);
299 }
300 return sub_c;
301
302 }
303
304
305 /* TODO implement
306
307 private Validator validator;
308
309 private javax.validation.Validator javaxValidator;
310
311 @RequestMapping(method = RequestMethod.PUT, headers="content-type=multipart/form-data")
312 public T doPutForm(@PathVariable(value = "uuid") UUID uuid, @ModelAttribute("object") T object, BindingResult result) {
313 object.setUuid(uuid);
314 validator.validate(object, result);
315 if (result.hasErrors()) {
316 throw new Error();
317 // set http status code depending upon what happened, possibly return
318 // the put object and errors so that they can be rendered into a suitable error response
319 } else {
320 // requires merging detached object ?gilead?
321 service.save(object);
322 }
323
324 return object;
325 }
326
327 @RequestMapping(method = RequestMethod.PUT, headers="content-type=text/json")
328 public T doPutJSON(@PathVariable(value = "uuid") UUID uuid, @RequestBody String jsonMessage) {
329 JSONObject jsonObject = JSONObject.fromObject(jsonMessage);
330 T object = (T)JSONObject.toBean(jsonObject, this.getClass());
331
332
333 Set<ConstraintViolation<T>> constraintViolations = javaxValidator.validate(object);
334 if (!constraintViolations.isEmpty()) {
335 throw new Error();
336 // set http status code depending upon what happened, possibly return
337 // the put object and errors so that they can be rendered into a suitable error response
338 } else {
339 // requires merging detached object ?gilead?
340 service.save(object);
341 }
342
343 return object;
344 }
345
346 @RequestMapping(method = RequestMethod.PUT) // the cdm-server may not allow clients to specify the uuid for resources
347 public T doPut(@PathVariable(value = "uuid") UUID uuid, @ModelAttribute("object") T object, BindingResult result) {
348 validator.validate(object, result);
349 if (result.hasErrors()) {
350 // set http status code depending upon what happened, possibly return
351 // the put object and errors so that they can be rendered into a suitable error response
352 } else {
353 service.save(object);
354 }
355 }
356
357 @RequestMapping(method = RequestMethod.DELETE)
358 public void doDelete(@PathVariable(value = "uuid") UUID uuid) {
359 T object = service.find(uuid);
360 // provided the object exists
361 service.delete(uuid);
362 // might return 204 or 200
363 }
364 }
365 */
366
367
368 }