Project

General

Profile

Download (14.9 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
import eu.etaxonomy.cdm.remote.exception.NoRecordsMatchException;
49

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

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

    
61
/*	protected SERVICE service;
62

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

    
65
    protected Class<T> baseClass;
66

    
67
    public BaseController (){
68

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

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

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

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

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

    
120
        ModelAndView modelAndView = new ModelAndView();
121

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

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

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

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

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

    
136
        // CUT>
137

    
138
        if(objectFromProperty != null){
139

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

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

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

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

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

    
166
        }
167

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

    
172
            return modelAndView;
173
        }
174
    }
175

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

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

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

    
182
        return objectFromProperty;
183
    }
184

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

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

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

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

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

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

    
259

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

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

    
285
        CDM_BASE cdmBaseObject = service.load(uuid, pathProperties);
286
        if (cdmBaseObject == null) {
287
            HttpStatusMessage.UUID_NOT_FOUND.send(response);
288
            throw new NoRecordsMatchException("No instance found for UUID " + uuid.toString());
289
        }
290
        return cdmBaseObject;
291
        }
292

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

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

    
311
            Class<?> returnType = method.getReturnType();
312

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

    
318
                result = method.invoke(instance, (Object[])null);
319

    
320
                result = HibernateProxyHelper.deproxy(result);
321

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

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

    
352
    }
353

    
354

    
355
      /* TODO implement
356

    
357
      private Validator validator;
358

    
359
      private javax.validation.Validator javaxValidator;
360

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

    
374
            return object;
375
      }
376

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

    
382

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

    
393
            return object;
394
      }
395

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

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

    
417

    
418
}
(7-7/63)