3 * Copyright (C) 2007 EDIT
4 * European Distributed Institute of Taxonomy
5 * http://www.e-taxonomy.eu
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.
11 package eu
.etaxonomy
.cdm
.remote
.controller
;
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
;
23 import javax
.servlet
.http
.HttpServletRequest
;
24 import javax
.servlet
.http
.HttpServletResponse
;
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
;
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
;
45 * based on org.cateproject.controller.common
47 * @author a.kohlbecker
53 public abstract class BaseController
<T
extends CdmBase
, SERVICE
extends IService
<T
>> extends AbstractController
{
55 protected SERVICE service
;
57 protected Class
<T
> baseClass
;
59 public abstract void setService(SERVICE service
);
62 public void initBinder(WebDataBinder binder
) {
63 binder
.registerCustomEditor(UUID
.class, new UUIDPropertyEditor());
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
);
83 * TODO implement bulk version of this method
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
{
97 ModelAndView modelAndView
= new ModelAndView();
99 String servletPath
= request
.getServletPath();
100 String baseName
= FilenameUtils
.getBaseName(servletPath
);
102 logger
.info("doGetMethod()[doGet" + StringUtils
.capitalize(baseName
) + "] " + request
.getServletPath());
104 T instance
= getCdmBaseInstance(uuid
, response
, Arrays
.asList(new String
[]{baseName
+ ".titleCache"}));
106 //Class<?> propertyClass = propertyClass(instance, baseName);
108 Object objectFromProperty
= invokeProperty(instance
, baseName
, response
);
109 if(objectFromProperty
!= null){
111 if( Collection
.class.isAssignableFrom(objectFromProperty
.getClass())){
112 // Map types cannot be returend as list or in a pager!
114 Collection c
= (Collection
)objectFromProperty
;
117 limit
= (limit
== null ? DEFAULT_PAGE_SIZE
: limit
);
118 Collection sub_c
= subCollection(c
, start
, limit
);
119 modelAndView
.addObject(sub_c
);
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
);
131 modelAndView
.addObject(objectFromProperty
);
136 if(modelAndView
.isEmpty()){
144 private Class
<?
> propertyClass(T instance
, String baseName
) {
145 PropertyDescriptor propertyDescriptor
= null;
148 propertyDescriptor
= PropertyUtils
.getPropertyDescriptor(instance
, baseName
);
149 if(propertyDescriptor
!= null){
150 c
= propertyDescriptor
.getClass();
152 } catch (IllegalAccessException e
) {
153 // TODO Auto-generated catch block
155 } catch (InvocationTargetException e
) {
156 // TODO Auto-generated catch block
158 } catch (NoSuchMethodException e
) {
159 // TODO Auto-generated catch block
170 * @param pathProperties
172 * @throws IOException
174 @SuppressWarnings("unchecked")
175 protected final <SUB_T
extends T
> SUB_T
getCdmBaseInstance(Class
<SUB_T
> clazz
, UUID uuid
, HttpServletResponse response
, List
<String
> pathProperties
)
178 CdmBase cdmBaseObject
= getCdmBaseInstance(uuid
, response
, pathProperties
);
179 if(!clazz
.isAssignableFrom(cdmBaseObject
.getClass())){
180 HttpStatusMessage
.UUID_REFERENCES_WRONG_TYPE
.send(response
);
182 return (SUB_T
) cdmBaseObject
;
190 * @param pathProperty
192 * @throws IOException
194 @SuppressWarnings("unchecked")
195 protected final <SUB_T
extends T
> SUB_T
getCdmBaseInstance(Class
<SUB_T
> clazz
, UUID uuid
, HttpServletResponse response
, String pathProperty
)
198 CdmBase cdmBaseObject
= getCdmBaseInstance(uuid
, response
, pathProperty
);
199 if(!clazz
.isAssignableFrom(cdmBaseObject
.getClass())){
200 HttpStatusMessage
.UUID_REFERENCES_WRONG_TYPE
.send(response
);
202 return (SUB_T
) cdmBaseObject
;
208 * @param pathProperty
210 * @throws IOException
212 protected final T
getCdmBaseInstance(UUID uuid
, HttpServletResponse response
, String pathProperty
)
214 return getCdmBaseInstance(baseClass
, uuid
, response
, Arrays
215 .asList(new String
[] { pathProperty
}));
222 * @param pathProperties
224 * @throws IOException
226 protected final T
getCdmBaseInstance(UUID uuid
, HttpServletResponse response
, List
<String
> pathProperties
)
228 return getCdmBaseInstance(baseClass
, service
, uuid
, response
, pathProperties
);
237 * @param pathProperties
239 * @throws IOException
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
)
244 CDM_BASE cdmBaseObject
= service
.load(uuid
, pathProperties
);
245 if (cdmBaseObject
== null) {
246 HttpStatusMessage
.UUID_NOT_FOUND
.send(response
);
248 return cdmBaseObject
;
256 * @throws IOException
258 private final Object
invokeProperty(T instance
,
259 String baseName
, HttpServletResponse response
) throws IOException
{
261 Object result
= null;
263 PropertyDescriptor propertyDescriptor
= PropertyUtils
.getPropertyDescriptor(instance
, baseName
);
264 Method method
= propertyDescriptor
.getReadMethod();
266 Class
<?
> returnType
= method
.getReturnType();
268 if(CdmBase
.class.isAssignableFrom(returnType
)
269 || Collection
.class.isAssignableFrom(returnType
)
270 || Map
.class.isAssignableFrom(returnType
)){
271 result
= method
.invoke(instance
, (Object
[])null);
273 HttpStatusMessage
.UUID_REFERENCES_WRONG_TYPE
.send(response
);
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
);
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
++){
307 private Validator validator;
309 private javax.validation.Validator javaxValidator;
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()) {
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
320 // requires merging detached object ?gilead?
321 service.save(object);
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());
333 Set<ConstraintViolation<T>> constraintViolations = javaxValidator.validate(object);
334 if (!constraintViolations.isEmpty()) {
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
339 // requires merging detached object ?gilead?
340 service.save(object);
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
353 service.save(object);
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