Project

General

Profile

Download (14.3 KB) Statistics
| Branch: | Tag: | Revision:
1
/**
2
 * Copyright (C) 2009 EDIT
3
 * European Distributed Institute of Taxonomy
4
 * http://www.e-taxonomy.eu
5
 *
6
 * The contents of this file are subject to the Mozilla Public License Version 1.1
7
 * See LICENSE.TXT at the top of this package for the full license terms.
8
 */
9
package eu.etaxonomy.cdm.remote.controller.dto;
10
import java.io.IOException;
11
import java.io.InputStream;
12
import java.util.ArrayList;
13
import java.util.Arrays;
14
import java.util.HashMap;
15
import java.util.List;
16
import java.util.Map;
17
import java.util.UUID;
18

    
19
import javax.servlet.http.HttpServletRequest;
20
import javax.servlet.http.HttpServletResponse;
21

    
22
import org.springframework.beans.factory.annotation.Autowired;
23
import org.springframework.context.ResourceLoaderAware;
24
import org.springframework.core.io.Resource;
25
import org.springframework.core.io.ResourceLoader;
26
import org.springframework.stereotype.Controller;
27
import org.springframework.web.bind.annotation.RequestMapping;
28
import org.springframework.web.bind.annotation.RequestMethod;
29
import org.springframework.web.bind.annotation.RequestParam;
30
import org.springframework.web.servlet.ModelAndView;
31

    
32
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacade;
33
import eu.etaxonomy.cdm.api.facade.DerivedUnitFacadeNotSupportedException;
34
import eu.etaxonomy.cdm.api.service.IOccurrenceService;
35
import eu.etaxonomy.cdm.api.service.ITaxonService;
36
import eu.etaxonomy.cdm.api.service.pager.Pager;
37
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
38
import eu.etaxonomy.cdm.common.DocUtils;
39
import eu.etaxonomy.cdm.model.common.CdmBase;
40
import eu.etaxonomy.cdm.model.occurrence.DerivedUnit;
41
import eu.etaxonomy.cdm.model.occurrence.SpecimenOrObservationBase;
42
import eu.etaxonomy.cdm.model.taxon.Taxon;
43
import eu.etaxonomy.cdm.persistence.query.OrderHint;
44
import eu.etaxonomy.cdm.remote.controller.AbstractController;
45
import eu.etaxonomy.cdm.remote.controller.util.PagerParameters;
46
import eu.etaxonomy.cdm.remote.dto.common.ErrorResponse;
47
import eu.etaxonomy.cdm.remote.dto.occurrencecatalogue.OccurrenceSearch;
48
import eu.etaxonomy.cdm.remote.dto.occurrencecatalogue.OccurrenceSearch.OccurrenceSearchResponse;
49
import eu.etaxonomy.cdm.remote.view.HtmlView;
50
import io.swagger.annotations.Api;
51

    
52
/**
53
 * The controller class for the namespace 'occurrence_catalogue'. This web service namespace
54
 * is an add-on to the already existing CDM REST API and provides information relating
55
 * to scientific names as well as taxa present in the underlying datasource.
56
 *
57
 * @author p.kelbert
58
 * @since March-2014
59
 */
60

    
61
@Controller
62
@Api("occurrence_catalogue")
63
@RequestMapping(value = { "/occurrence_catalogue" })
64
public class OccurrenceCatalogueController extends AbstractController<SpecimenOrObservationBase, IOccurrenceService> implements ResourceLoaderAware{
65

    
66
    private ResourceLoader resourceLoader;
67

    
68
    public static final String DEFAULT_PAGE_NUMBER = "0";
69

    
70
    public static final String DEFAULT_PAGE_SIZE = "50";
71

    
72
    @Autowired
73
    private ITaxonService taxonService;
74

    
75
    @Autowired
76
    private IOccurrenceService occurrenceService;
77

    
78

    
79
    private static final List<String> OCCURRENCE_INIT_STRATEGY = Arrays.asList(new String []{
80
            "sources"
81
    });
82

    
83
    private static final List<String> FACADE_INIT_STRATEGY = Arrays.asList(new String []{
84
            "collector",
85
            "collection.institute.name",
86
            "fieldNotes",
87
            "type",
88
            "individualCount",
89
            "kindOfUnit.label",
90
            "absoluteElevation",
91
            "absoluteElevationMaximum",
92
            "distanceToGround",
93
            "distanceToGroundMax",
94
            "gatheringPeriod.start",
95
            "gatheringPeriod.end",
96
            "exactLocation.latitude",
97
            "exactLocation.longitude",
98
            "exactLocation.errorRadius",
99
            "exactLocation.referenceSystem",
100
            "country",
101
            "locality",
102
            "sources.citation",
103
            "sources.citationMicroReference",
104
            "rights.abbreviatedText"
105
    });
106

    
107

    
108

    
109
    public OccurrenceCatalogueController() {
110
        super();
111
        setInitializationStrategy(Arrays.asList(new String[] { "$" }));
112
    }
113

    
114
    /**
115
     * Returns a documentation page for the Occurrence Search API.
116
     * <p>
117
     * URI: <b>&#x002F;{datasource-name}&#x002F;occurrence_catalogue</b>
118
     *
119
     * @param request
120
     * @param response
121
     * @return Html page describing the Name Search API
122
     * @throws IOException
123
     */
124
    @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {})
125
    public ModelAndView doGetNameSearchDocumentation(
126
            HttpServletRequest request, HttpServletResponse response)
127
            throws IOException {
128
        ModelAndView mv = new ModelAndView();
129
        // Read apt documentation file.
130
        Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/occurrence-catalogue-default.apt");
131
        // using input stream as this works for both files in the classes directory
132
        // as well as files inside jars
133
        InputStream aptInputStream = resource.getInputStream();
134
        // Build Html View
135
        Map<String, String> modelMap = new HashMap<String, String>();
136
        // Convert Apt to Html
137
        modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
138
        mv.addAllObjects(modelMap);
139

    
140
        HtmlView hv = new HtmlView();
141
        mv.setView(hv);
142
        return mv;
143
    }
144

    
145
    /**
146
     * Returns a list of occurrences matching the <code>{query}</code>
147
     * Taxon UUID.
148
     * <p>
149
     * Endpoint documentation can be found <a href="{@docRoot}/../remote/occurrence-catalogue-default.html">here</a>
150
     * <p>
151
     * URI: <b>&#x002F;{datasource-name}&#x002F;occurrence_catalogue</b>
152
     *
153
     * @param query
154
     *  The UUID of the taxon to query for. The query can
155
     *  not contain wildcard characters.
156
     *
157
     * @param request Http servlet request.
158
     * @param response Http servlet response.
159
     * @return a List of {@link OccurrenceSearch} objects each corresponding to a
160
     * single query. These are built from {@link SpecimenOrObservationBase} entities
161
     * which are in turn initialized using the {@link #OCCURRENCE_INIT_STRATEGY} and {@link #FACADE_INIT_STRATEGY}
162
     * Redirect the query with the default page size and the default page number.
163
     * @throws IOException
164
     */
165
    @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"taxonUuid"})
166
    public ModelAndView doGetOccurrenceSearch(@RequestParam(value = "taxonUuid", required = true) String query,
167
            HttpServletRequest request, HttpServletResponse response) throws IOException {
168
        return doGetOccurrenceSearch(query, DEFAULT_PAGE_NUMBER, DEFAULT_PAGE_SIZE, request, response);
169
    }
170

    
171
    /**
172
     * Returns a list of occurrences matching the <code>{query}</code>
173
     * Taxon UUID.
174
     * <p>
175
     * Endpoint documentation can be found <a href="{@docRoot}/../remote/occurrence-catalogue-default.html">here</a>
176
     * <p>
177
     * URI: <b>&#x002F;{datasource-name}&#x002F;occurrence_catalogue</b>
178
     *
179
     * @param query
180
     * 	The UUID of the taxon to query for. The query can
181
     * 	not contain wildcard characters.
182
     *
183
     * @param pageNumber
184
     * 	The number of the page to be returned.
185
     *  * @param pageSize
186
     *  The number of responses per page to be returned.
187
     * @param request Http servlet request.
188
     * @param response Http servlet response.
189
     * @return a List of {@link OccurrenceSearch} objects each corresponding to a
190
     * single query. These are built from {@link SpecimenOrObservationBase} entities
191
     * which are in turn initialized using the {@link #OCCURRENCE_INIT_STRATEGY} and {@link #FACADE_INIT_STRATEGY}
192
     * @throws IOException
193
     */
194
    @SuppressWarnings({ "rawtypes", "unchecked" })
195
    @RequestMapping(value = { "" }, method = RequestMethod.GET, params = {"taxonUuid","pageNumber","pageSize"})
196
    public ModelAndView doGetOccurrenceSearch(
197
            @RequestParam(value = "taxonUuid", required = true) String taxonUuid,
198
            @RequestParam(value = "pageNumber", required = false, defaultValue = DEFAULT_PAGE_NUMBER) String pageNumber,
199
            @RequestParam(value = "pageSize", required = false, defaultValue = DEFAULT_PAGE_SIZE) String pageSize,
200
            HttpServletRequest request, HttpServletResponse response) throws IOException {
201
        logger.info("doGetOccurrenceSearch, " + "taxonUuid : " + taxonUuid + " - pageSize : " + pageSize + " - " + "pageNumber : " + pageNumber);
202
        ModelAndView mv = new ModelAndView();
203

    
204
        Integer pS = null;
205
        Integer pN = null;
206

    
207
        try {
208
            pN=Integer.valueOf(pageNumber);
209
        } catch (Exception e) {
210
            pN=Integer.valueOf(DEFAULT_PAGE_NUMBER);
211
            logger.info("pagenumber is not a number");
212
        }
213
        try {
214
            pS=Integer.valueOf(pageSize);
215
        } catch (Exception e) {
216
            pS=Integer.valueOf(DEFAULT_PAGE_SIZE);
217
            logger.info("pagesize is not a number");
218
        }
219

    
220
        PagerParameters pagerParams = new PagerParameters(pS, pN);
221
        pagerParams.normalizeAndValidate(response);
222

    
223
        List<DerivedUnit> records = new ArrayList<DerivedUnit>();
224

    
225
        OccurrenceSearch ns = new OccurrenceSearch();
226
        ns.setRequest(taxonUuid);
227

    
228
        Pager<DerivedUnit> specimenOrObs = null;
229
        OccurrenceSearch os = new OccurrenceSearch();
230

    
231
        if(taxonUuid.equals("") || !isValid(taxonUuid)) {
232
            ErrorResponse er = new ErrorResponse();
233
            er.setErrorMessage("Empty or invalid taxon uuid ");
234
            mv.addObject(er);
235
            return mv;
236
        } else {
237
            Taxon taxon = null;
238
            try {
239
                taxon = (Taxon) taxonService.find(UUID.fromString(taxonUuid));
240
            } catch(ClassCastException cce) {
241
                ErrorResponse er = new ErrorResponse();
242
                er.setErrorMessage("Given UUID is not that of an accepted taxon");
243
                mv.addObject(er);
244
                return mv;
245
            }
246
            if(taxon == null) {
247
                ErrorResponse er = new ErrorResponse();
248
                er.setErrorMessage("No Taxon for given UUID : " + taxonUuid);
249
                mv.addObject(er);
250
                return mv;
251
            }
252
            pagerParams.normalizeAndValidate(response);
253
            List<OrderHint> orderHints = null;
254

    
255
            // Only derived units are requested in the pager method since we expect
256
            // occurrences to be of type DerivedUnit
257
            // The only possibility for an occurrence to be of type FieldUnit is the
258
            // describedSpecimenOrObservation in the DescriptionBase class, which ideally
259
            // should not be used for storing occurrences
260
            specimenOrObs= service.pageByAssociatedTaxon(DerivedUnit.class,
261
                    null,
262
                    taxon,
263
                    null,
264
                    pagerParams.getPageSize(),
265
                    pagerParams.getPageIndex(),
266
                    orderHints,
267
                    null);
268

    
269
            records = specimenOrObs.getRecords();
270

    
271
            DerivedUnit derivedUnit=null;
272
            List<DerivedUnitFacade> facades = new ArrayList<DerivedUnitFacade>();
273
            for (SpecimenOrObservationBase<?> specimen:records){
274
                derivedUnit = CdmBase.deproxy(specimen, DerivedUnit.class);
275
                DerivedUnitFacade derivedUnitFacade =null;
276
                try {
277
                    //TODO : This is a potential performance hurdle (especially for large number
278
                    //       of derived units).
279
                    //       A possible solution is to already initialise the required derived unit facade
280
                    //       in the 'pageByAssociatedTaxon' call. This can be done either via the initialisation
281
                    //       strategy technique or by writing a new optimised hql query for this.
282
                    //       This will ensure that the DerivedUnits are loaded with required fields already
283
                    //       initialised and will not require the loading and initialisation of each DerivedUnit
284
                    //       object as is done in the 'getDerivedUnitFacade' method.
285
                    derivedUnitFacade = occurrenceService.getDerivedUnitFacade(derivedUnit, FACADE_INIT_STRATEGY);
286
                    os.addToResponse(taxon.getTitleCache(), taxonUuid, derivedUnitFacade);
287
                } catch (DerivedUnitFacadeNotSupportedException e) {
288
                    derivedUnitFacade=null;
289
                }
290
            }
291
        }
292

    
293

    
294
        if (os.getResponse().isEmpty()){
295
            ErrorResponse er = new ErrorResponse();
296
            er.setErrorMessage("No specimen or observation for given taxon uuid");
297
            mv.addObject(er);
298
            return mv;
299
        } else {
300
            DefaultPagerImpl<OccurrenceSearchResponse> dpi =
301
                    new DefaultPagerImpl<OccurrenceSearchResponse>(specimenOrObs.getCurrentIndex(),
302
                                specimenOrObs.getCount(),
303
                                specimenOrObs.getPageSize(),
304
                                os.getResponse());
305
            mv.addObject(dpi);
306
        }
307

    
308
        return mv;
309
    }
310

    
311

    
312
    private boolean isValid(String uuid){
313
        if( uuid == null) {
314
            return false;
315
        }
316
        try {
317
            // we have to convert to object and back to string because the built in fromString does not have
318
            // good validation logic.
319

    
320
            UUID fromStringUUID = UUID.fromString(uuid);
321
            String toStringUUID = fromStringUUID.toString();
322

    
323
            System.out.println("input uuid : " + uuid + " , parsed uuid : " + toStringUUID);
324
            return toStringUUID.equals(uuid);
325
        } catch(IllegalArgumentException e) {
326
            return false;
327
        }
328
    }
329

    
330

    
331
    /* (non-Javadoc)
332
     * @see eu.etaxonomy.cdm.remote.controller.BaseListController#setService(eu.etaxonomy.cdm.api.service.IService)
333
     */
334
    @Override
335
    @Autowired
336
    public void setService(IOccurrenceService service) {
337
        this.service = service;
338
    }
339

    
340
    @Override
341
    public void setResourceLoader(ResourceLoader resourceLoader) {
342
         this.resourceLoader = resourceLoader;
343

    
344
    }
345
}
(2-2/3)