2 * Copyright (C) 2007 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
.service
;
11 import java
.io
.BufferedReader
;
12 import java
.io
.IOException
;
13 import java
.io
.StringReader
;
14 import java
.util
.ArrayList
;
15 import java
.util
.Enumeration
;
16 import java
.util
.HashSet
;
17 import java
.util
.Hashtable
;
18 import java
.util
.List
;
19 import java
.util
.Locale
;
22 import java
.util
.UUID
;
24 import javax
.servlet
.http
.HttpServletRequest
;
25 import javax
.servlet
.http
.HttpServletResponse
;
27 import org
.apache
.commons
.logging
.Log
;
28 import org
.apache
.commons
.logging
.LogFactory
;
29 import org
.springframework
.beans
.factory
.annotation
.Autowired
;
30 import org
.springframework
.stereotype
.Controller
;
31 import org
.springframework
.web
.servlet
.ModelAndView
;
32 import org
.springframework
.web
.servlet
.mvc
.AbstractController
;
34 import eu
.etaxonomy
.cdm
.model
.common
.Annotation
;
35 import eu
.etaxonomy
.cdm
.persistence
.dao
.common
.ITitledDao
.MATCH_MODE
;
36 import eu
.etaxonomy
.cdm
.remote
.dto
.AnnotationTO
;
37 import eu
.etaxonomy
.cdm
.remote
.dto
.FeatureTO
;
38 import eu
.etaxonomy
.cdm
.remote
.dto
.FeatureTreeTO
;
39 import eu
.etaxonomy
.cdm
.remote
.dto
.NameSTO
;
40 import eu
.etaxonomy
.cdm
.remote
.dto
.NameTO
;
41 import eu
.etaxonomy
.cdm
.remote
.dto
.ReferenceSTO
;
42 import eu
.etaxonomy
.cdm
.remote
.dto
.ReferenceTO
;
43 import eu
.etaxonomy
.cdm
.remote
.dto
.TaxonSTO
;
44 import eu
.etaxonomy
.cdm
.remote
.dto
.TaxonTO
;
48 * Controller to generate the Home Page basics to be rendered by a view.
49 * It extends the convenience class AbstractController that encapsulates most
50 * of the drudgery involved in handling HTTP requests.
52 @Controller("restController")
53 public class RestController
extends AbstractController
55 Log log
= LogFactory
.getLog(RestController
.class);
58 private ICdmService service
;
61 * return page not found http error (404) for unknown or incorrect UUIDs
63 * @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
65 protected ModelAndView
handleRequestInternal(HttpServletRequest req
, HttpServletResponse resp
) throws Exception
68 ModelAndView mv
= new ModelAndView();
69 String action
= getNonNullPara("action",req
);
70 String op
= getNonNullPara("operation",req
);
71 String dto
= getNonNullPara("dto",req
);
72 String uuid
= getNonNullPara("uuid",req
);
73 String sec
= getNonNullPara("sec",req
);
74 String q
= getNonNullPara("q",req
);
76 Enumeration
<Locale
> locales
= req
.getLocales();
78 log
.info(String
.format("Request received: act=%s op=%s dto=%s uuid=%s sec=%s", action
, op
, dto
, uuid
, sec
));
82 if(dto
.equalsIgnoreCase("name")){
83 NameTO n
= service
.getName( getUuid(uuid
), locales
);
85 }else if(dto
.equalsIgnoreCase("taxon")){
86 UUID taxonUuid
= getUuid(uuid
);
88 String ftree
= this.getStringPara("ftree", req
);
90 UUID featureTreeUuid
= null;
92 featureTreeUuid
= getUuid(ftree
);
95 TaxonTO t
= service
.getTaxon(taxonUuid
, featureTreeUuid
, locales
);
97 }else if(dto
.equalsIgnoreCase("ref")){
98 ReferenceTO r
= service
.getReference(getUuid(uuid
), locales
);
100 }else if(dto
.equalsIgnoreCase("whatis")){
101 //TODO: somehow the whatis url path is not delegated to this controller ?!#!??
102 Object cl
= service
.whatis(getUuid(uuid
));
105 }else if(action
.equalsIgnoreCase("simple")){
106 Set
<UUID
> uuids
= getUuids(uuid
);
107 if(dto
.equalsIgnoreCase("name")){
108 List
<NameSTO
> n
= service
.getSimpleNames(uuids
, locales
);
110 }else if(dto
.equalsIgnoreCase("taxon")){
111 if(op
.equalsIgnoreCase("acceptedfor")){
112 Hashtable
<String
, List
<TaxonSTO
>> t
= service
.getAcceptedTaxa(uuids
, locales
);
115 List
<TaxonSTO
> t
= service
.getSimpleTaxa(uuids
, locales
);
118 }else if(dto
.equalsIgnoreCase("ref")){
119 List
<ReferenceSTO
> r
= service
.getSimpleReferences(uuids
, locales
);
122 }else if(action
.equalsIgnoreCase("find")){
124 // TODO handle multiple sec
125 Set
<UUID
> secundum
= null;
127 secundum
= new HashSet
<UUID
>();
128 List
<String
> secs
= getListPara("sec", req
);
130 for (String secString
: secs
){
131 secundum
.add(getUuid(secString
));
135 } catch (CdmObjectNonExisting e
) {
136 log
.warn("Concept sec reference UUID is not valid. Ignore");
139 Set
<UUID
> higherTaxa
= new HashSet
<UUID
>();
140 // TODO: take higher taxa UUIDs from "higherTaxa"
143 MATCH_MODE matchMode
= null;
145 String matchModeStr
= getStringPara("mode",req
);
146 matchMode
= MATCH_MODE
.valueOf(matchModeStr
.toUpperCase());
147 } catch(Exception e
){
148 matchMode
= MATCH_MODE
.BEGINNING
;
151 String featureTree
= getStringPara("feature", req
);
152 logger
.info("FeatureTree: " + featureTree
);
154 Boolean onlyAccepted
= getBoolPara("onlyAccepted",req
);
155 if (onlyAccepted
==null){
158 Integer page
= getIntPara("page",req
);
164 Integer pagesize
= getIntPara("pagesize",req
);
172 Object obj
= service
.findTaxa(q
, secundum
, higherTaxa
, matchMode
, onlyAccepted
, page
, pagesize
, locales
);
174 }else if(action
.equalsIgnoreCase("taxonomy")){
176 if(op
.equalsIgnoreCase("children")){
177 results
= service
.getChildrenTaxa(getUuid(uuid
));
179 else if(op
.equalsIgnoreCase("parents")){
180 results
= service
.getParentTaxa(getUuid(uuid
));
182 else if(op
.equalsIgnoreCase("root")){
184 if(sec
!= null && sec
.length() == 36 ){
187 } catch (CdmObjectNonExisting e
) {
188 log
.warn("Concept sec reference UUID is not valid. Ignore");
191 results
= service
.getRootTaxa(u
);
193 mv
.addObject( (List
)results
);
194 }else if(action
.equalsIgnoreCase("features")){
195 logger
.info("Feature Request.");
196 if(op
!= null && op
.equals("tree")){
197 // return a list of feature trees stored in database
198 List
<FeatureTreeTO
> featureTree
= service
.getFeatureTrees(locales
);
199 mv
.addObject(featureTree
);
201 // return a list of features this community store supports
202 List
<FeatureTO
> feature
= service
.getFeatures(locales
);
203 mv
.addObject(feature
);
206 }else if(action
.equalsIgnoreCase("annotations")){
208 logger
.info("Annotation action requested.");
210 UUID annotatableEntityUuid
= getUuid(uuid
);
212 String requestMethod
= req
.getMethod();
214 if(requestMethod
.equalsIgnoreCase("GET")){
215 List
<AnnotationTO
> annotations
= service
.getAnnotations(annotatableEntityUuid
, locales
);
216 mv
.addObject(annotations
);
217 }else if(requestMethod
.equalsIgnoreCase("POST")){
219 String annotationText
= req
.getParameter("annotation");
224 Annotation annotation
= Annotation
.NewInstance(annotationText
, null);
226 service
.saveAnnotation(annotatableEntityUuid
, annotation
);
227 //log.info(service.saveAnnotation(annotatableEntityUuid, annotation));
232 mv
.addObject("status", "Controller does not know this operation");
234 // set xml or json view
235 mv
.setViewName(getLogicalView(req
));
237 }catch(CdmObjectNonExisting e
){
238 sendNonExistingUuidError(resp
, e
);
244 * return a proper UUID for a string resembling a UUID
245 * If the uuid string is not a valid UUID, a CdmObjectNonExisting exception is thrown
248 * @throws CdmObjectNonExisting
250 private UUID
getUuid(String uuid
) throws CdmObjectNonExisting
{
253 u
= UUID
.fromString(uuid
);
254 }catch(IllegalArgumentException e
){
255 throw new CdmObjectNonExisting(uuid
);
260 * Turns a string of uuids concatenated with comma characters '<code>,</code>' into a Set of UUID instances
264 private Set
<UUID
> getUuids(String uuid
) {
265 String
[] temp
= uuid
.trim().split(",");
266 Set
<UUID
> uuids
= new HashSet
<UUID
>();
267 for (String u
: temp
){
269 uuids
.add(getUuid(u
));
270 } catch (CdmObjectNonExisting e
) {
271 logger
.warn(u
+" is not valid UUID!");
277 private void sendNonExistingUuidError(HttpServletResponse resp
, CdmObjectNonExisting e
) throws IOException
{
278 resp
.sendError(404, e
.getMessage() );
281 * return the value for the given parameter name as a string.
282 * in case the parameters doesnt exist return an empty string "", not null.
283 * @param parameterName
287 private String
getStringPara(String parameterName
, HttpServletRequest req
){
288 // first try URL parameters set by org.springframework.web.servlet.handler.SimpleUrlHandlerMapping controller mapping
289 Object map
= req
.getAttribute("ParameterizedUrlHandlerMapping.path-parameters");
290 String result
= null;
292 // first look into url parameters
293 Map
<String
,String
> urlParas
= (Map
<String
, String
>) map
;
294 result
= urlParas
.get(parameterName
);
297 // alternatively try querystring parameters
298 result
= req
.getParameter(parameterName
);
302 private List
<String
> getListPara(String parameterName
, HttpServletRequest req
){
303 ArrayList
<String
> list
= new ArrayList
<String
>();
304 String
[] map
= req
.getParameterValues(parameterName
);
306 for(String param
: map
){
312 private String
getNonNullPara(String parameterName
, HttpServletRequest req
){
313 String val
= getStringPara(parameterName
, req
);
319 private Integer
getIntPara(String parameterName
, HttpServletRequest req
){
320 // first try URL parameters set by org.springframework.web.servlet.handler.SimpleUrlHandlerMapping controller mapping
322 String tmp
= getStringPara(parameterName
, req
);
324 result
= Integer
.valueOf(tmp
);
325 }catch (Exception e
){
330 private Boolean
getBoolPara(String parameterName
, HttpServletRequest req
){
331 // first try URL parameters set by org.springframework.web.servlet.handler.SimpleUrlHandlerMapping controller mapping
332 String tmp
= getStringPara(parameterName
, req
);
333 return Utils
.isTrue(tmp
);
336 * Return logical spring view name to be used for rendering
337 * Read http request parameter "Accept" and decide whether to use JSON or XML for the response.
338 * Defaults to XML in case no matching header can be identified.
342 private String
getLogicalView(HttpServletRequest request
){
343 String DEFAULT_VIEW
= "xmlView";
344 String ctype
= request
.getHeader("Accept");
345 String
[] ctypes
= ctype
.split("[,;]");
346 for (String ct
: ctypes
){
347 if (ct
.endsWith("json")){
349 }else if (ct
.endsWith("xml")){