Project

General

Profile

Download (14.7 KB) Statistics
| Branch: | Tag: | Revision:
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.lang.reflect.ParameterizedType;
18
import java.lang.reflect.Type;
19
import java.util.ArrayList;
20
import java.util.Arrays;
21
import java.util.Collection;
22
import java.util.List;
23
import java.util.UUID;
24

    
25
import javax.servlet.http.HttpServletRequest;
26
import javax.servlet.http.HttpServletResponse;
27

    
28
import org.apache.commons.beanutils.PropertyUtils;
29
import org.apache.commons.io.FilenameUtils;
30
import org.apache.commons.lang.StringUtils;
31
import org.hibernate.mapping.Map;
32
import org.springframework.web.bind.WebDataBinder;
33
import org.springframework.web.bind.annotation.InitBinder;
34
import org.springframework.web.bind.annotation.PathVariable;
35
import org.springframework.web.bind.annotation.RequestMapping;
36
import org.springframework.web.bind.annotation.RequestMethod;
37
import org.springframework.web.bind.annotation.RequestParam;
38
import org.springframework.web.servlet.ModelAndView;
39

    
40
import eu.etaxonomy.cdm.api.service.IService;
41
import eu.etaxonomy.cdm.api.service.pager.Pager;
42
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
43
import eu.etaxonomy.cdm.hibernate.HibernateProxyHelper;
44
import eu.etaxonomy.cdm.model.common.CdmBase;
45
import eu.etaxonomy.cdm.model.reference.INomenclaturalReference;
46
import eu.etaxonomy.cdm.remote.controller.util.PagerParameters;
47
import eu.etaxonomy.cdm.remote.editor.UUIDPropertyEditor;
48

    
49
/**
50
 * based on org.cateproject.controller.common
51
 * @author b.clark
52
 * @author a.kohlbecker
53
 *
54
 * @param <T>
55
 * @param <SERVICE>
56
 */
57

    
58
public abstract class BaseController<T extends CdmBase, SERVICE extends IService<T>> extends AbstractController<T, SERVICE> {
59

    
60
/*	protected SERVICE service;
61

    
62
    public abstract void setService(SERVICE service);*/
63

    
64
    protected Class<T> baseClass;
65

    
66
    public BaseController (){
67

    
68
        Type superClass = this.getClass().getGenericSuperclass();
69
        if(superClass instanceof ParameterizedType){
70
            ParameterizedType parametrizedSuperClass = (ParameterizedType) superClass;
71
            Type[] typeArguments = parametrizedSuperClass.getActualTypeArguments();
72

    
73
            if(typeArguments.length > 1 && typeArguments[0] instanceof Class<?>){
74
                baseClass = (Class<T>) typeArguments[0];
75
            } else {
76
                logger.error("unable to find baseClass");
77
            }
78
        }
79
    }
80

    
81
    @InitBinder
82
    public void initBinder(WebDataBinder binder) {
83
        binder.registerCustomEditor(UUID.class, new UUIDPropertyEditor());
84
    }
85

    
86
    //TODO implement bulk version of this method
87
    @RequestMapping(method = RequestMethod.GET)
88
    public T doGet(@PathVariable("uuid") UUID uuid,
89
                HttpServletRequest request,
90
                HttpServletResponse response) throws IOException {
91
        if(request != null) {
92
            logger.info("doGet() " + request.getRequestURI());
93
        }
94
        T obj = getCdmBaseInstance(uuid, response, initializationStrategy);
95
        return obj;
96
    }
97

    
98
    /**
99
     * @param uuid
100
     * @param request
101
     * @param response
102
     * @return
103
     * @throws IOException
104
     *
105
     * TODO implement bulk version of this method
106
     */
107
    @RequestMapping(value = "*", method = RequestMethod.GET)
108
    public ModelAndView doGetMethod(
109
            @PathVariable("uuid") UUID uuid,
110
            // doPage request parametes
111
            @RequestParam(value = "pageNumber", required = false) Integer pageNumber,
112
            @RequestParam(value = "pageSize", required = false) Integer pageSize,
113
            // doList request parametes
114
            @RequestParam(value = "start", required = false) Integer start,
115
            @RequestParam(value = "limit", required = false) Integer limit,
116
            HttpServletRequest request,
117
            HttpServletResponse response) throws IOException {
118

    
119
        ModelAndView modelAndView = new ModelAndView();
120

    
121
        String servletPath = request.getServletPath();
122
        String baseName = FilenameUtils.getBaseName(servletPath);
123

    
124
        if(request != null) {
125
            logger.info("doGetMethod()[doGet" + StringUtils.capitalize(baseName) + "] " + request.getRequestURI());
126
        }
127

    
128
        // <CUT
129
//		T instance = getCdmBaseInstance(uuid, response, (List<String>)null);
130

    
131
        //Class<?> propertyClass = propertyClass(instance, baseName);
132

    
133
        Object objectFromProperty = getCdmBaseProperty(uuid, baseName, response);//   invokeProperty(instance, baseName, response);
134

    
135
        // CUT>
136

    
137
        if(objectFromProperty != null){
138

    
139
            if( Collection.class.isAssignableFrom(objectFromProperty.getClass())){
140
                // Map types cannot be returned as list or in a pager!
141

    
142
                Collection c = (Collection)objectFromProperty;
143
                if(start != null){
144
                    // return list
145
                    limit = (limit == null ? DEFAULT_PAGE_SIZE : limit);
146
                    Collection sub_c = subCollection(c, start, limit);
147
                    modelAndView.addObject(sub_c);
148

    
149
                } else {
150
                    //FIXME use real paging mechanism of according service class instead of subCollection()
151
                    //FIXME use BaseListController.normalizeAndValidatePagerParameters(pageNumber, pageSize, response);
152
                    PagerParameters pagerParameters = new PagerParameters(pageSize, pageNumber);
153
                    pagerParameters.normalizeAndValidate(response);
154

    
155
                    start = pagerParameters.getPageIndex() * pagerParameters.getPageSize();
156
                    List sub_c = subCollection(c, start, pagerParameters.getPageSize());
157
                    Pager p = new DefaultPagerImpl(pageNumber, c.size(), pagerParameters.getPageSize(), sub_c);
158
                    modelAndView.addObject(p);
159
                }
160

    
161
            } else {
162
                modelAndView.addObject(objectFromProperty);
163
            }
164

    
165
        }
166

    
167
        if(modelAndView.isEmpty()){
168
            return null;
169
        } else {
170

    
171
            return modelAndView;
172
        }
173
    }
174

    
175
    public Object getCdmBaseProperty(UUID uuid, String property, HttpServletResponse response) throws IOException{
176

    
177
        T instance = HibernateProxyHelper.deproxy(getCdmBaseInstance(uuid, response, property));
178

    
179
        Object objectFromProperty = invokeProperty(instance, property, response);
180

    
181
        return objectFromProperty;
182
    }
183

    
184
    private Class<?> propertyClass(T instance, String baseName) {
185
        PropertyDescriptor propertyDescriptor = null;
186
        Class<?> c = null;
187
        try {
188
            propertyDescriptor = PropertyUtils.getPropertyDescriptor(instance, baseName);
189
            if(propertyDescriptor != null){
190
                c =  propertyDescriptor.getClass();
191
            }
192
        } catch (IllegalAccessException e) {
193
            // TODO Auto-generated catch block
194
            e.printStackTrace();
195
        } catch (InvocationTargetException e) {
196
            // TODO Auto-generated catch block
197
            e.printStackTrace();
198
        } catch (NoSuchMethodException e) {
199
            // TODO Auto-generated catch block
200
            e.printStackTrace();
201
        }
202
        return c;
203
    }
204

    
205
    /**
206
     * @param <SUB_T>
207
     * @param clazz
208
     * @param uuid
209
     * @param response
210
     * @param pathProperties
211
     * @return
212
     * @throws IOException
213
     */
214
    @SuppressWarnings("unchecked")
215
    protected final <SUB_T extends T> SUB_T getCdmBaseInstance(Class<SUB_T> clazz, UUID uuid, HttpServletResponse response, List<String> pathProperties)
216
    throws IOException {
217

    
218
        CdmBase cdmBaseObject = getCdmBaseInstance(uuid, response, pathProperties);
219
        if(!clazz.isAssignableFrom(cdmBaseObject.getClass())){
220
            HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
221
        }
222
        return (SUB_T) cdmBaseObject;
223
    }
224

    
225
    /**
226
     * @param <SUB_T>
227
     * @param clazz
228
     * @param uuid
229
     * @param response
230
     * @param pathProperty
231
     * @return
232
     * @throws IOException
233
     */
234
    @SuppressWarnings("unchecked")
235
    protected final <SUB_T extends T> SUB_T getCdmBaseInstance(Class<SUB_T> clazz, UUID uuid, HttpServletResponse response, String pathProperty)
236
    throws IOException {
237

    
238
        CdmBase cdmBaseObject = getCdmBaseInstance(uuid, response, pathProperty);
239
        if(!clazz.isAssignableFrom(cdmBaseObject.getClass())){
240
            HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
241
        }
242
        return (SUB_T) cdmBaseObject;
243
    }
244

    
245
    /**
246
     * @param uuid
247
     * @param response
248
     * @param pathProperty
249
     * @return
250
     * @throws IOException
251
     */
252
    protected final T getCdmBaseInstance(UUID uuid, HttpServletResponse response, String pathProperty)
253
            throws IOException {
254
        return getCdmBaseInstance(baseClass, uuid, response, Arrays
255
                .asList(new String[] { pathProperty }));
256
    }
257

    
258

    
259
    /**
260
     * @param uuid
261
     * @param response
262
     * @param pathProperties
263
     * @return
264
     * @throws IOException
265
     */
266
    protected final T getCdmBaseInstance(UUID uuid, HttpServletResponse response, List<String> pathProperties)
267
            throws IOException {
268
        return getCdmBaseInstance(baseClass, service, uuid, response, pathProperties);
269
    }
270

    
271
    /**
272
     * @param <CDM_BASE>
273
     * @param clazz
274
     * @param service
275
     * @param uuid
276
     * @param response
277
     * @param pathProperties
278
     * @return
279
     * @throws IOException
280
     */
281
    protected final <CDM_BASE extends CdmBase> CDM_BASE getCdmBaseInstance(Class<CDM_BASE> clazz, IService<CDM_BASE> service, UUID uuid, HttpServletResponse response, List<String> pathProperties)
282
    throws IOException {
283

    
284
        CDM_BASE cdmBaseObject = service.load(uuid, pathProperties);
285
        if (cdmBaseObject == null) {
286
            HttpStatusMessage.UUID_NOT_FOUND.send(response);
287
        }
288
        return cdmBaseObject;
289
        }
290

    
291
    /**
292
     * @param instance
293
     * @param baseName
294
     * @param response
295
     * @return
296
     * @throws IOException
297
     */
298
    private final Object invokeProperty(T instance,
299
            String baseName, HttpServletResponse response) throws IOException {
300

    
301
        Object result = null;
302
        try {
303
            PropertyDescriptor propertyDescriptor = PropertyUtils.getPropertyDescriptor(instance, baseName);
304
            if(propertyDescriptor == null){
305
                throw new NoSuchMethodException("No such method: " + instance.getClass().getSimpleName() + ".get" + baseName);
306
            }
307
            Method method = propertyDescriptor.getReadMethod();
308

    
309
            Class<?> returnType = method.getReturnType();
310

    
311
            if(CdmBase.class.isAssignableFrom(returnType)
312
                    || Collection.class.isAssignableFrom(returnType)
313
                    || Map.class.isAssignableFrom(returnType)
314
                    || INomenclaturalReference.class.isAssignableFrom(returnType)){
315

    
316
                result = method.invoke(instance, (Object[])null);
317

    
318
                result = HibernateProxyHelper.deproxy(result);
319

    
320
            }else{
321
                HttpStatusMessage.UUID_REFERENCES_WRONG_TYPE.send(response);
322
            }
323
        } catch (SecurityException e) {
324
            logger.error("SecurityException: ", e);
325
            HttpStatusMessage.INTERNAL_ERROR.send(response);
326
        } catch (NoSuchMethodException e) {
327
            HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
328
        } catch (IllegalArgumentException e) {
329
            HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
330
        } catch (IllegalAccessException e) {
331
            HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
332
        } catch (InvocationTargetException e) {
333
            HttpStatusMessage.PROPERTY_NOT_FOUND.send(response);
334
        }
335
        return result;
336
    }
337

    
338
    private <E> List<E> subCollection(Collection<? extends E> c, Integer start, Integer length){
339
        List<E> sub_c = new ArrayList<E>(length);
340
        if(c.size() > length){
341
            E[] a = (E[]) c.toArray();
342
            for(int i = start; i < start + length; i++){
343
                sub_c.add(a[i]);
344
            }
345
        } else {
346
            sub_c.addAll(c);
347
        }
348
        return sub_c;
349

    
350
    }
351

    
352

    
353
      /* TODO implement
354

    
355
      private Validator validator;
356

    
357
      private javax.validation.Validator javaxValidator;
358

    
359
      @RequestMapping(method = RequestMethod.PUT, headers="content-type=multipart/form-data")
360
      public T doPutForm(@PathVariable(value = "uuid") UUID uuid, @ModelAttribute("object") T object, BindingResult result) {
361
          object.setUuid(uuid);
362
          validator.validate(object, result);
363
          if (result.hasErrors()) {
364
              throw new Error();
365
                // set http status code depending upon what happened, possibly return
366
                // the put object and errors so that they can be rendered into a suitable error response
367
          } else {
368
             // requires merging detached object ?gilead?
369
             service.save(object);
370
          }
371

    
372
            return object;
373
      }
374

    
375
      @RequestMapping(method = RequestMethod.PUT, headers="content-type=text/json")
376
      public T doPutJSON(@PathVariable(value = "uuid") UUID uuid, @RequestBody String jsonMessage) {
377
          JSONObject jsonObject = JSONObject.fromObject(jsonMessage);
378
          T object = (T)JSONObject.toBean(jsonObject, this.getClass());
379

    
380

    
381
          Set<ConstraintViolation<T>> constraintViolations = javaxValidator.validate(object);
382
            if (!constraintViolations.isEmpty()) {
383
                throw new Error();
384
                    // set http status code depending upon what happened, possibly return
385
                // the put object and errors so that they can be rendered into a suitable error response
386
            } else {
387
              // requires merging detached object ?gilead?
388
              service.save(object);
389
            }
390

    
391
            return object;
392
      }
393

    
394
      @RequestMapping(method = RequestMethod.PUT) // the cdm-server may not allow clients to specify the uuid for resources
395
      public T doPut(@PathVariable(value = "uuid") UUID uuid, @ModelAttribute("object") T object, BindingResult result) {
396
            validator.validate(object, result);
397
            if (result.hasErrors()) {
398
                    // set http status code depending upon what happened, possibly return
399
                // the put object and errors so that they can be rendered into a suitable error response
400
            } else {
401
              service.save(object);
402
            }
403
      }
404

    
405
       @RequestMapping(method = RequestMethod.DELETE)
406
       public void doDelete(@PathVariable(value = "uuid") UUID uuid) {
407
           T object = service.find(uuid);
408
           // provided the object exists
409
           service.delete(uuid);
410
           // might return 204 or 200
411
       }
412
    }
413
*/
414

    
415

    
416
}
(7-7/63)