2 * Copyright (C) 2009 EDIT
3 * European Distributed Institute of Taxonomy
4 * http://www.e-taxonomy.eu
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.
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
;
17 import java
.util
.UUID
;
19 import javax
.servlet
.http
.HttpServletRequest
;
20 import javax
.servlet
.http
.HttpServletResponse
;
22 import org
.apache
.logging
.log4j
.LogManager
;
23 import org
.apache
.logging
.log4j
.Logger
;
24 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
25 import org
.springframework
.context
.ResourceLoaderAware
;
26 import org
.springframework
.core
.io
.Resource
;
27 import org
.springframework
.core
.io
.ResourceLoader
;
28 import org
.springframework
.stereotype
.Controller
;
29 import org
.springframework
.web
.bind
.annotation
.RequestMapping
;
30 import org
.springframework
.web
.bind
.annotation
.RequestMethod
;
31 import org
.springframework
.web
.bind
.annotation
.RequestParam
;
32 import org
.springframework
.web
.servlet
.ModelAndView
;
34 import eu
.etaxonomy
.cdm
.api
.facade
.DerivedUnitFacade
;
35 import eu
.etaxonomy
.cdm
.api
.facade
.DerivedUnitFacadeNotSupportedException
;
36 import eu
.etaxonomy
.cdm
.api
.service
.IOccurrenceService
;
37 import eu
.etaxonomy
.cdm
.api
.service
.ITaxonService
;
38 import eu
.etaxonomy
.cdm
.api
.service
.pager
.Pager
;
39 import eu
.etaxonomy
.cdm
.api
.service
.pager
.impl
.DefaultPagerImpl
;
40 import eu
.etaxonomy
.cdm
.common
.DocUtils
;
41 import eu
.etaxonomy
.cdm
.model
.common
.CdmBase
;
42 import eu
.etaxonomy
.cdm
.model
.occurrence
.DerivedUnit
;
43 import eu
.etaxonomy
.cdm
.model
.occurrence
.SpecimenOrObservationBase
;
44 import eu
.etaxonomy
.cdm
.model
.taxon
.Taxon
;
45 import eu
.etaxonomy
.cdm
.persistence
.query
.OrderHint
;
46 import eu
.etaxonomy
.cdm
.remote
.controller
.AbstractController
;
47 import eu
.etaxonomy
.cdm
.remote
.controller
.util
.PagerParameters
;
48 import eu
.etaxonomy
.cdm
.remote
.dto
.common
.ErrorResponse
;
49 import eu
.etaxonomy
.cdm
.remote
.dto
.occurrencecatalogue
.OccurrenceSearch
;
50 import eu
.etaxonomy
.cdm
.remote
.dto
.occurrencecatalogue
.OccurrenceSearch
.OccurrenceSearchResponse
;
51 import eu
.etaxonomy
.cdm
.remote
.view
.HtmlView
;
52 import io
.swagger
.annotations
.Api
;
55 * The controller class for the namespace 'occurrence_catalogue'. This web service namespace
56 * is an add-on to the already existing CDM REST API and provides information relating
57 * to scientific names as well as taxa present in the underlying datasource.
63 @Api("occurrence_catalogue")
64 @RequestMapping(value
= { "/occurrence_catalogue" })
65 public class OccurrenceCatalogueController
extends AbstractController
<SpecimenOrObservationBase
, IOccurrenceService
> implements ResourceLoaderAware
{
67 private static final Logger logger
= LogManager
.getLogger();
69 private ResourceLoader resourceLoader
;
71 public static final String DEFAULT_PAGE_NUMBER
= "0";
73 public static final String DEFAULT_PAGE_SIZE
= "50";
76 private ITaxonService taxonService
;
79 private IOccurrenceService occurrenceService
;
82 private static final List
<String
> OCCURRENCE_INIT_STRATEGY
= Arrays
.asList(new String
[]{
86 private static final List
<String
> FACADE_INIT_STRATEGY
= Arrays
.asList(new String
[]{
88 "collection.institute.name",
94 "absoluteElevationMaximum",
96 "distanceToGroundMax",
97 "gatheringPeriod.start",
98 "gatheringPeriod.end",
99 "exactLocation.latitude",
100 "exactLocation.longitude",
101 "exactLocation.errorRadius",
102 "exactLocation.referenceSystem",
106 "sources.citationMicroReference",
107 "rights.abbreviatedText"
112 public OccurrenceCatalogueController() {
114 setInitializationStrategy(Arrays
.asList(new String
[] { "$" }));
118 * Returns a documentation page for the Occurrence Search API.
120 * URI: <b>/{datasource-name}/occurrence_catalogue</b>
124 * @return Html page describing the Name Search API
125 * @throws IOException
127 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {})
128 public ModelAndView
doGetNameSearchDocumentation(
129 HttpServletRequest request
, HttpServletResponse response
)
132 logger
.info("doGetNameSearchDocumentation() " + requestPathAndQuery(request
));
134 ModelAndView mv
= new ModelAndView();
135 // Read apt documentation file.
136 Resource resource
= resourceLoader
.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/occurrence-catalogue-default.apt");
137 // using input stream as this works for both files in the classes directory
138 // as well as files inside jars
139 InputStream aptInputStream
= resource
.getInputStream();
141 Map
<String
, String
> modelMap
= new HashMap
<String
, String
>();
142 // Convert Apt to Html
143 modelMap
.put("html", DocUtils
.convertAptToHtml(aptInputStream
));
144 mv
.addAllObjects(modelMap
);
146 HtmlView hv
= new HtmlView();
152 * Returns a list of occurrences matching the <code>{query}</code>
155 * Endpoint documentation can be found <a href="{@docRoot}/../remote/occurrence-catalogue-default.html">here</a>
157 * URI: <b>/{datasource-name}/occurrence_catalogue</b>
160 * The UUID of the taxon to query for. The query can
161 * not contain wildcard characters.
163 * @param request Http servlet request.
164 * @param response Http servlet response.
165 * @return a List of {@link OccurrenceSearch} objects each corresponding to a
166 * single query. These are built from {@link SpecimenOrObservationBase} entities
167 * which are in turn initialized using the {@link #OCCURRENCE_INIT_STRATEGY} and {@link #FACADE_INIT_STRATEGY}
168 * Redirect the query with the default page size and the default page number.
169 * @throws IOException
171 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {"taxonUuid"})
172 public ModelAndView
doGetOccurrenceSearch(@RequestParam(value
= "taxonUuid", required
= true) String query
,
173 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
174 return doGetOccurrenceSearch(query
, DEFAULT_PAGE_NUMBER
, DEFAULT_PAGE_SIZE
, request
, response
);
178 * Returns a list of occurrences matching the <code>{query}</code>
181 * Endpoint documentation can be found <a href="{@docRoot}/../remote/occurrence-catalogue-default.html">here</a>
183 * URI: <b>/{datasource-name}/occurrence_catalogue</b>
186 * The UUID of the taxon to query for. The query can
187 * not contain wildcard characters.
190 * The number of the page to be returned.
192 * The number of responses per page to be returned.
193 * @param request Http servlet request.
194 * @param response Http servlet response.
195 * @return a List of {@link OccurrenceSearch} objects each corresponding to a
196 * single query. These are built from {@link SpecimenOrObservationBase} entities
197 * which are in turn initialized using the {@link #OCCURRENCE_INIT_STRATEGY} and {@link #FACADE_INIT_STRATEGY}
198 * @throws IOException
200 @SuppressWarnings({ "rawtypes", "unchecked" })
201 @RequestMapping(value
= { "" }, method
= RequestMethod
.GET
, params
= {"taxonUuid","pageNumber","pageSize"})
202 public ModelAndView
doGetOccurrenceSearch(
203 @RequestParam(value
= "taxonUuid", required
= true) String taxonUuid
,
204 @RequestParam(value
= "pageNumber", required
= false, defaultValue
= DEFAULT_PAGE_NUMBER
) String pageNumber
,
205 @RequestParam(value
= "pageSize", required
= false, defaultValue
= DEFAULT_PAGE_SIZE
) String pageSize
,
206 HttpServletRequest request
, HttpServletResponse response
) throws IOException
{
208 logger
.info("doGetOccurrenceSearch() " + requestPathAndQuery(request
));
210 ModelAndView mv
= new ModelAndView();
216 pN
=Integer
.valueOf(pageNumber
);
217 } catch (Exception e
) {
218 pN
=Integer
.valueOf(DEFAULT_PAGE_NUMBER
);
219 logger
.debug("pagenumber is not a number");
222 pS
=Integer
.valueOf(pageSize
);
223 } catch (Exception e
) {
224 pS
=Integer
.valueOf(DEFAULT_PAGE_SIZE
);
225 logger
.debug("pagesize is not a number");
228 PagerParameters pagerParams
= new PagerParameters(pS
, pN
);
229 pagerParams
.normalizeAndValidate(response
);
231 List
<DerivedUnit
> records
= new ArrayList
<DerivedUnit
>();
233 OccurrenceSearch ns
= new OccurrenceSearch();
234 ns
.setRequest(taxonUuid
);
236 Pager
<DerivedUnit
> specimenOrObs
= null;
237 OccurrenceSearch os
= new OccurrenceSearch();
239 if(taxonUuid
.equals("") || !isValid(taxonUuid
)) {
240 ErrorResponse er
= new ErrorResponse();
241 er
.setErrorMessage("Empty or invalid taxon uuid ");
247 taxon
= (Taxon
) taxonService
.find(UUID
.fromString(taxonUuid
));
248 } catch(ClassCastException cce
) {
249 ErrorResponse er
= new ErrorResponse();
250 er
.setErrorMessage("Given UUID is not that of an accepted taxon");
255 ErrorResponse er
= new ErrorResponse();
256 er
.setErrorMessage("No Taxon for given UUID : " + taxonUuid
);
260 pagerParams
.normalizeAndValidate(response
);
261 List
<OrderHint
> orderHints
= null;
263 // Only derived units are requested in the pager method since we expect
264 // occurrences to be of type DerivedUnit
265 // The only possibility for an occurrence to be of type FieldUnit is the
266 // describedSpecimenOrObservation in the DescriptionBase class, which ideally
267 // should not be used for storing occurrences
268 specimenOrObs
= service
.pageByAssociatedTaxon(DerivedUnit
.class,
272 pagerParams
.getPageSize(),
273 pagerParams
.getPageIndex(),
277 records
= specimenOrObs
.getRecords();
279 DerivedUnit derivedUnit
=null;
280 List
<DerivedUnitFacade
> facades
= new ArrayList
<DerivedUnitFacade
>();
281 for (SpecimenOrObservationBase
<?
> specimen
:records
){
282 derivedUnit
= CdmBase
.deproxy(specimen
, DerivedUnit
.class);
283 DerivedUnitFacade derivedUnitFacade
=null;
285 //TODO : This is a potential performance hurdle (especially for large number
286 // of derived units).
287 // A possible solution is to already initialise the required derived unit facade
288 // in the 'pageByAssociatedTaxon' call. This can be done either via the initialisation
289 // strategy technique or by writing a new optimised hql query for this.
290 // This will ensure that the DerivedUnits are loaded with required fields already
291 // initialised and will not require the loading and initialisation of each DerivedUnit
292 // object as is done in the 'getDerivedUnitFacade' method.
293 derivedUnitFacade
= occurrenceService
.getDerivedUnitFacade(derivedUnit
, FACADE_INIT_STRATEGY
);
294 os
.addToResponse(taxon
.getTitleCache(), taxonUuid
, derivedUnitFacade
);
295 } catch (DerivedUnitFacadeNotSupportedException e
) {
296 derivedUnitFacade
=null;
302 if (os
.getResponse().isEmpty()){
303 ErrorResponse er
= new ErrorResponse();
304 er
.setErrorMessage("No specimen or observation for given taxon uuid");
308 DefaultPagerImpl
<OccurrenceSearchResponse
> dpi
=
309 new DefaultPagerImpl
<OccurrenceSearchResponse
>(specimenOrObs
.getCurrentIndex(),
310 specimenOrObs
.getCount(),
311 specimenOrObs
.getPageSize(),
320 private boolean isValid(String uuid
){
325 // we have to convert to object and back to string because the built in fromString does not have
326 // good validation logic.
328 UUID fromStringUUID
= UUID
.fromString(uuid
);
329 String toStringUUID
= fromStringUUID
.toString();
331 logger
.debug("input uuid : " + uuid
+ " , parsed uuid : " + toStringUUID
);
332 return toStringUUID
.equals(uuid
);
333 } catch(IllegalArgumentException e
) {
340 * @see eu.etaxonomy.cdm.remote.controller.BaseListController#setService(eu.etaxonomy.cdm.api.service.IService)
344 public void setService(IOccurrenceService service
) {
345 this.service
= service
;
349 public void setResourceLoader(ResourceLoader resourceLoader
) {
350 this.resourceLoader
= resourceLoader
;