28bfdae9fff00ac9ce7c5ec7697387d73979b39a
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / service / RestController.java
1 /**
2 * Copyright (C) 2007 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.service;
10
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;
20 import java.util.Map;
21 import java.util.Set;
22 import java.util.UUID;
23
24 import javax.servlet.http.HttpServletRequest;
25 import javax.servlet.http.HttpServletResponse;
26
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;
33
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;
45
46
47 /**
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.
51 */
52 @Controller("restController")
53 public class RestController extends AbstractController
54 {
55 Log log = LogFactory.getLog(RestController.class);
56
57 @Autowired
58 private ICdmService service;
59
60 /*
61 * return page not found http error (404) for unknown or incorrect UUIDs
62 * (non-Javadoc)
63 * @see org.springframework.web.servlet.mvc.AbstractController#handleRequestInternal(javax.servlet.http.HttpServletRequest, javax.servlet.http.HttpServletResponse)
64 */
65 protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse resp) throws Exception
66 {
67 try{
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);
75
76 Enumeration<Locale> locales = req.getLocales();
77
78 log.info(String.format("Request received: act=%s op=%s dto=%s uuid=%s sec=%s", action, op, dto, uuid, sec));
79
80 if(action==""){
81 // get Object by UUID
82 if(dto.equalsIgnoreCase("name")){
83 NameTO n = service.getName( getUuid(uuid), locales);
84 mv.addObject(n);
85 }else if(dto.equalsIgnoreCase("taxon")){
86 UUID taxonUuid = getUuid(uuid);
87
88 String ftree = this.getStringPara("ftree", req);
89
90 UUID featureTreeUuid = null;
91 if(ftree != null){
92 featureTreeUuid = getUuid(ftree);
93 }
94
95 TaxonTO t = service.getTaxon(taxonUuid, featureTreeUuid, locales);
96 mv.addObject(t);
97 }else if(dto.equalsIgnoreCase("ref")){
98 ReferenceTO r = service.getReference(getUuid(uuid), locales);
99 mv.addObject(r);
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));
103 mv.addObject(cl);
104 }
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);
109 mv.addObject(n);
110 }else if(dto.equalsIgnoreCase("taxon")){
111 if(op.equalsIgnoreCase("acceptedfor")){
112 Hashtable<String, List<TaxonSTO>> t = service.getAcceptedTaxa(uuids, locales);
113 mv.addObject(t);
114 } else {
115 List<TaxonSTO> t = service.getSimpleTaxa(uuids, locales);
116 mv.addObject(t);
117 }
118 }else if(dto.equalsIgnoreCase("ref")){
119 List<ReferenceSTO> r = service.getSimpleReferences(uuids, locales);
120 mv.addObject(r);
121 }
122 }else if(action.equalsIgnoreCase("find")){
123 //
124 // TODO handle multiple sec
125 Set<UUID> secundum = null;
126 try {
127 secundum = new HashSet<UUID>();
128 List<String> secs = getListPara("sec", req);
129 if(secs != null){
130 for (String secString : secs){
131 secundum.add(getUuid(secString));
132 }
133 }
134 log.info(secundum);
135 } catch (CdmObjectNonExisting e) {
136 log.warn("Concept sec reference UUID is not valid. Ignore");
137 }
138
139 Set<UUID> higherTaxa = new HashSet<UUID>();
140 // TODO: take higher taxa UUIDs from "higherTaxa"
141 //
142
143 MATCH_MODE matchMode = null;
144 try{
145 String matchModeStr = getStringPara("mode",req);
146 matchMode = MATCH_MODE.valueOf(matchModeStr.toUpperCase());
147 } catch(Exception e){
148 matchMode = MATCH_MODE.BEGINNING;
149 }
150
151 String featureTree = getStringPara("feature", req);
152 logger.info("FeatureTree: " + featureTree);
153
154 Boolean onlyAccepted = getBoolPara("onlyAccepted",req);
155 if (onlyAccepted==null){
156 onlyAccepted=false;
157 };
158 Integer page = getIntPara("page",req);
159 if (page==null){
160 page=1;
161 };
162
163
164 Integer pagesize = getIntPara("pagesize",req);
165 if (pagesize==null){
166 pagesize=25;
167 };
168
169 //
170 // search for taxa
171
172 Object obj = service.findTaxa(q, secundum, higherTaxa, matchMode, onlyAccepted, page, pagesize, locales);
173 mv.addObject(obj);
174 }else if(action.equalsIgnoreCase("taxonomy")){
175 List results = null;
176 if(op.equalsIgnoreCase("children")){
177 results = service.getChildrenTaxa(getUuid(uuid));
178 }
179 else if(op.equalsIgnoreCase("parents")){
180 results = service.getParentTaxa(getUuid(uuid));
181 }
182 else if(op.equalsIgnoreCase("root")){
183 UUID u = null;
184 if(sec != null && sec.length() == 36 ){
185 try {
186 u = getUuid(sec);
187 } catch (CdmObjectNonExisting e) {
188 log.warn("Concept sec reference UUID is not valid. Ignore");
189 }
190 }
191 results = service.getRootTaxa(u);
192 }
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);
200 }else{
201 // return a list of features this community store supports
202 List<FeatureTO> feature = service.getFeatures(locales);
203 mv.addObject(feature);
204 }
205
206 }else if(action.equalsIgnoreCase("annotations")){
207
208 logger.info("Annotation action requested.");
209
210 UUID annotatableEntityUuid = getUuid(uuid);
211
212 String requestMethod = req.getMethod();
213
214 if(requestMethod.equalsIgnoreCase("GET")){
215 List<AnnotationTO> annotations = service.getAnnotations(annotatableEntityUuid, locales);
216 mv.addObject(annotations);
217 }else if(requestMethod.equalsIgnoreCase("POST")){
218
219 String annotationText = req.getParameter("annotation");
220
221 // TODO set locale
222
223
224 Annotation annotation = Annotation.NewInstance(annotationText, null);
225
226 service.saveAnnotation(annotatableEntityUuid, annotation);
227 //log.info(service.saveAnnotation(annotatableEntityUuid, annotation));
228
229 }
230 }else{
231 // nothing matches
232 mv.addObject("status", "Controller does not know this operation");
233 }
234 // set xml or json view
235 mv.setViewName(getLogicalView(req));
236 return mv;
237 }catch(CdmObjectNonExisting e){
238 sendNonExistingUuidError(resp, e);
239 return null;
240 }
241 }
242
243 /**
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
246 * @param uuid
247 * @return
248 * @throws CdmObjectNonExisting
249 */
250 private UUID getUuid(String uuid) throws CdmObjectNonExisting{
251 UUID u=null;
252 try{
253 u = UUID.fromString(uuid);
254 }catch(IllegalArgumentException e){
255 throw new CdmObjectNonExisting(uuid);
256 }
257 return u;
258 }
259 /**
260 * Turns a string of uuids concatenated with comma characters '<code>,</code>' into a Set of UUID instances
261 * @param uuid
262 * @return
263 */
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){
268 try {
269 uuids.add(getUuid(u));
270 } catch (CdmObjectNonExisting e) {
271 logger.warn(u+" is not valid UUID!");
272 }
273 }
274 return uuids;
275 }
276
277 private void sendNonExistingUuidError(HttpServletResponse resp, CdmObjectNonExisting e) throws IOException{
278 resp.sendError(404, e.getMessage() );
279 }
280 /**
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
284 * @param req
285 * @return
286 */
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;
291 if (map!=null){
292 // first look into url parameters
293 Map<String,String> urlParas = (Map<String, String>) map;
294 result = urlParas.get(parameterName);
295 }
296 if (result == null){
297 // alternatively try querystring parameters
298 result = req.getParameter(parameterName);
299 }
300 return result;
301 }
302 private List<String> getListPara(String parameterName, HttpServletRequest req){
303 ArrayList<String> list = new ArrayList<String>();
304 String[] map = req.getParameterValues(parameterName);
305 if(map != null){
306 for(String param : map){
307 list.add(param);
308 }
309 }
310 return list;
311 }
312 private String getNonNullPara(String parameterName, HttpServletRequest req){
313 String val = getStringPara(parameterName, req);
314 if (val==null){
315 return "";
316 }
317 return val;
318 }
319 private Integer getIntPara(String parameterName, HttpServletRequest req){
320 // first try URL parameters set by org.springframework.web.servlet.handler.SimpleUrlHandlerMapping controller mapping
321 Integer result;
322 String tmp = getStringPara(parameterName, req);
323 try{
324 result = Integer.valueOf(tmp);
325 }catch (Exception e){
326 result = null;
327 }
328 return result;
329 }
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);
334 }
335 /**
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.
339 * @param request
340 * @return
341 */
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")){
348 return "jsonView";
349 }else if (ct.endsWith("xml")){
350 return "xmlView";
351 }
352 }
353 return DEFAULT_VIEW;
354 }
355
356
357 }
358