Merge branch 'release/5.45.0'
[cdmlib.git] / cdmlib-remote / src / main / java / eu / etaxonomy / cdm / remote / controller / BaseListController.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.controller;
10
11 import java.io.IOException;
12 import java.util.ArrayList;
13 import java.util.List;
14 import java.util.UUID;
15
16 import javax.servlet.http.HttpServletRequest;
17 import javax.servlet.http.HttpServletResponse;
18
19 import org.apache.logging.log4j.LogManager;
20 import org.apache.logging.log4j.Logger;
21 import org.springframework.web.bind.WebDataBinder;
22 import org.springframework.web.bind.annotation.InitBinder;
23 import org.springframework.web.bind.annotation.RequestMapping;
24 import org.springframework.web.bind.annotation.RequestMethod;
25 import org.springframework.web.bind.annotation.RequestParam;
26
27 import eu.etaxonomy.cdm.api.service.IClassificationService;
28 import eu.etaxonomy.cdm.api.service.IService;
29 import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
30 import eu.etaxonomy.cdm.api.service.pager.Pager;
31 import eu.etaxonomy.cdm.model.common.CdmBase;
32 import eu.etaxonomy.cdm.model.taxon.Classification;
33 import eu.etaxonomy.cdm.model.taxon.TaxonNode;
34 import eu.etaxonomy.cdm.persistence.dao.common.Restriction;
35 import eu.etaxonomy.cdm.remote.controller.util.PagerParameters;
36 import eu.etaxonomy.cdm.remote.editor.CdmTypePropertyEditor;
37 import eu.etaxonomy.cdm.remote.editor.UUIDPropertyEditor;
38
39 /**
40 * @author a.kohlbecker
41 * @since 22.07.2009
42 */
43 public abstract class BaseListController <T extends CdmBase, SERVICE extends IService<T>> extends AbstractListController<T, SERVICE> {
44
45 private static final Logger logger = LogManager.getLogger();
46
47 @InitBinder
48 public void initBinder(WebDataBinder binder) {
49 binder.registerCustomEditor(UUID.class, new UUIDPropertyEditor());
50 binder.registerCustomEditor(Class.class, new CdmTypePropertyEditor());
51 }
52
53 /**
54 * NOTE: The indices for pages are 0-based see {@link Pager}
55 *
56 * @param pageIndex
57 * the index of the page to be returned, the first page has the
58 * pageIndex = 0 - <i>optional parameter</i>. Defaults to 0 if
59 * set to <code>NULL</code>.
60 * @param pageSize
61 * the maximum number of entities returned per page.
62 * The {@link #DEFAULT_PAGE_SIZE} will be used if pageSize is set to
63 * <code>null</code> - <i>optional parameter</i>
64 * @param type
65 * Further restricts the type of entities to be returned.
66 * If null the base type <code>&lt;T&gt;</code> is being used. - <i>optional parameter</i>
67 * @return
68 * @throws IOException
69 */
70 @SuppressWarnings("unchecked")
71 @RequestMapping(method = RequestMethod.GET)
72 public Pager<T> doPage(
73 @RequestParam(value = "pageIndex", required = false) Integer pageIndex,
74 @RequestParam(value = "pageSize", required = false) Integer pageSize,
75 @RequestParam(value = "class", required = false) Class type,
76 @RequestParam(name="orderBy", defaultValue="BY_TITLE_CACHE_ASC", required=true) OrderHintPreset orderBy,
77 HttpServletRequest request,
78 HttpServletResponse response) throws IOException
79 {
80
81 logger.info("doPage() " + requestPathAndQuery(request));
82 PagerParameters pagerParameters = new PagerParameters(pageSize, pageIndex).normalizeAndValidate(response);
83
84 if(type != null) {
85 orderBy = orderBy.checkSuitableFor(type);
86 // TODO how can we check in case type == null?
87 }
88 return service.page(type, pagerParameters.getPageSize(), pagerParameters.getPageIndex(), orderBy.orderHints(), getInitializationStrategy());
89 }
90
91 @SuppressWarnings("unchecked")
92 @RequestMapping(method = {RequestMethod.GET, RequestMethod.POST}, params={"restriction"})
93 public Pager<T> doPageByRestrictions(
94 @RequestParam(value = "pageIndex", required = false) Integer pageIndex,
95 @RequestParam(value = "pageSize", required = false) Integer pageSize,
96 @RequestParam(value = "class", required = false) Class type,
97 @RequestParam(value = "restriction", required = true) List<Restriction<?>> restrictions,
98 @RequestParam(value = "initStrategy", required = true) List<String> initStrategy,
99 @RequestParam(name="orderBy", defaultValue="BY_TITLE_CACHE_ASC", required=true) OrderHintPreset orderBy,
100 HttpServletRequest request,
101 HttpServletResponse response) throws IOException
102 {
103
104 // NOTE: for testing with httpi and jq:
105 // http GET :8080/portal/taxon.json restriction=='{"propertyName":"name.titleCache","matchMode":"EXACT","values":["Eunotia krammeri Metzeltin & Lange-Bert."]}' initStrategy=name.titleCache | jq '.records[].name.titleCache'
106 logger.info("doPageByRestrictions() " + requestPathAndQuery(request));
107 PagerParameters pagerParameters = new PagerParameters(pageSize, pageIndex).normalizeAndValidate(response);
108
109 if(type != null) {
110 orderBy = orderBy.checkSuitableFor(type);
111 }
112
113 return pageByRestrictions(type, initStrategy, orderBy, pagerParameters, new ArrayList<>(restrictions));
114 }
115
116 /**
117 * This method can be overwritten by subclasses, for example to apply additional filtering like for the publish flag.
118 *
119 * @param type
120 * @param initStrategy
121 * @param orderBy
122 * @param pagerParameters
123 * @param restrictions
124 * @return
125 */
126 protected Pager<T> pageByRestrictions(Class<T> type, List<String> initStrategy, OrderHintPreset orderBy,
127 PagerParameters pagerParameters, ArrayList<Restriction<?>> restrictions) {
128 return service.page(type, restrictions, pagerParameters.getPageSize(), pagerParameters.getPageIndex(), orderBy.orderHints(), initStrategy);
129 }
130
131 // /**
132 // * Parameter less method to be used as default when request without parameter are made. Otherwise
133 // * the nameless methods {@link #doPage(Integer, Integer, Class)} and {@link #doList(Integer, Integer, Class)}
134 // * are ambigous.
135 // * @return
136 // * @throws IOException
137 // */
138 // @RequestMapping(method = RequestMethod.GET)
139 // public Pager<T> doPage(HttpServletRequest request, HttpServletResponse response) throws IOException{
140 // return doPage(null, null, null, request, response);
141 // }
142
143 /**
144 * @param start
145 * The offset index from the start of the list. The first entity
146 * has the index = 0 - <i>required parameter</i>
147 * @param limit
148 * The maximum number of entities returned. - <i>optional parameter</i>
149 * If limit is set to a value < 1 all entities will be returned
150 * @param type
151 * Further restricts the type of entities to be returned.
152 * If null the base type <code>&lt;T&gt;</code> is being used. - <i>optional parameter</i>
153 * @return a List of entities
154 */
155 @RequestMapping(method = RequestMethod.GET, params = "start")
156 public List<T> doList(
157 @RequestParam(value = "start", required = true) Integer start,
158 @RequestParam(value = "limit", required = false) Integer limit,
159 @RequestParam(value = "class", required = false) Class<T> type,
160 HttpServletRequest request,
161 @SuppressWarnings("unused") HttpServletResponse response) {
162
163 if (request != null){
164 logger.info("doList() " + requestPathAndQuery(request));
165 }
166
167 //if(start == null){ start = 0;}
168 if(limit == null){
169 limit = PagerParameters.DEFAULT_PAGESIZE;
170 }else if(limit < 1){
171 limit = null;
172 }
173 return service.list(type, limit, start, null, getInitializationStrategy());
174 }
175
176 /**
177 * Retrieves the according taxon node representing the taxon subtree. If no such taxon node
178 * is found an 404 error response is sent.
179 */
180 // this is a copy from BaseController, should be unified
181 protected TaxonNode getSubtreeOrError(UUID subtreeUuid, ITaxonNodeService taxonNodeService, HttpServletResponse response) throws IOException {
182 TaxonNode subtree = null;
183 if (subtreeUuid != null){
184 subtree = taxonNodeService.find(subtreeUuid);
185 if(subtree == null) {
186 response.sendError(404 , "Taxon node for subtree not found: " + subtreeUuid );
187 //will not happen
188 return null;
189 }
190 }
191 return subtree;
192 }
193
194 // this is a copy from BaseController, should be unified
195 protected Classification getClassificationOrError(UUID classificationUuid,
196 IClassificationService classificationService, HttpServletResponse response) throws IOException {
197 Classification classification = null;
198 if (classificationUuid != null){
199 classification = classificationService.find(classificationUuid);
200 if(classification == null) {
201 response.sendError(404 , "Classification not found: " + classificationUuid );
202 //will not happen
203 return null;
204 }
205 }
206 return classification;
207 }
208
209 /* TODO
210 @RequestMapping(method = RequestMethod.POST)
211 public T doPost(@ModelAttribute("object") T object, BindingResult result) {
212 validator.validate(object, result);
213 if (result.hasErrors()) {
214 // set http status code depending upon what happened, possibly return
215 // the put object and errors so that they can be rendered into a suitable error response
216 } else {
217 // should set the status to 201 created and "Location" header to "/resource/uuid"
218 service.save(object);
219 }
220 }
221 */
222 }