1
|
/*
|
2
|
* Copyright 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.checklist;
|
10
|
|
11
|
import java.io.File;
|
12
|
import java.io.IOException;
|
13
|
import java.io.InputStream;
|
14
|
import java.util.ArrayList;
|
15
|
import java.util.Arrays;
|
16
|
import java.util.HashMap;
|
17
|
import java.util.HashSet;
|
18
|
import java.util.List;
|
19
|
import java.util.Map;
|
20
|
import java.util.Set;
|
21
|
import java.util.UUID;
|
22
|
|
23
|
import javax.servlet.http.HttpServletRequest;
|
24
|
import javax.servlet.http.HttpServletResponse;
|
25
|
|
26
|
import org.apache.log4j.Logger;
|
27
|
import org.springframework.beans.factory.annotation.Autowired;
|
28
|
import org.springframework.beans.propertyeditors.UUIDEditor;
|
29
|
import org.springframework.context.ApplicationContext;
|
30
|
import org.springframework.context.ResourceLoaderAware;
|
31
|
import org.springframework.core.io.Resource;
|
32
|
import org.springframework.core.io.ResourceLoader;
|
33
|
import org.springframework.stereotype.Controller;
|
34
|
import org.springframework.web.bind.WebDataBinder;
|
35
|
import org.springframework.web.bind.annotation.InitBinder;
|
36
|
import org.springframework.web.bind.annotation.RequestMapping;
|
37
|
import org.springframework.web.bind.annotation.RequestMethod;
|
38
|
import org.springframework.web.bind.annotation.RequestParam;
|
39
|
import org.springframework.web.servlet.ModelAndView;
|
40
|
|
41
|
import eu.etaxonomy.cdm.api.service.IClassificationService;
|
42
|
import eu.etaxonomy.cdm.api.service.IService;
|
43
|
import eu.etaxonomy.cdm.api.service.ITermService;
|
44
|
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
|
45
|
import eu.etaxonomy.cdm.common.DocUtils;
|
46
|
import eu.etaxonomy.cdm.common.monitor.IRestServiceProgressMonitor;
|
47
|
import eu.etaxonomy.cdm.io.common.CdmApplicationAwareDefaultExport;
|
48
|
import eu.etaxonomy.cdm.io.csv.redlist.demo.CsvDemoExportConfigurator;
|
49
|
import eu.etaxonomy.cdm.io.csv.redlist.demo.CsvDemoRecord;
|
50
|
import eu.etaxonomy.cdm.model.description.Feature;
|
51
|
import eu.etaxonomy.cdm.model.location.NamedArea;
|
52
|
import eu.etaxonomy.cdm.model.taxon.Classification;
|
53
|
import eu.etaxonomy.cdm.model.taxon.Taxon;
|
54
|
import eu.etaxonomy.cdm.remote.controller.AbstractController;
|
55
|
import eu.etaxonomy.cdm.remote.controller.ProgressMonitorController;
|
56
|
import eu.etaxonomy.cdm.remote.controller.util.PagerParameters;
|
57
|
import eu.etaxonomy.cdm.remote.controller.util.ProgressMonitorUtil;
|
58
|
import eu.etaxonomy.cdm.remote.editor.TermBaseListPropertyEditor;
|
59
|
import eu.etaxonomy.cdm.remote.editor.UUIDListPropertyEditor;
|
60
|
import eu.etaxonomy.cdm.remote.editor.UuidList;
|
61
|
import eu.etaxonomy.cdm.remote.view.FileDownloadView;
|
62
|
import eu.etaxonomy.cdm.remote.view.HtmlView;
|
63
|
|
64
|
/**
|
65
|
* @author a.oppermann
|
66
|
* @since 20.09.2012
|
67
|
* <p>
|
68
|
* This controller enables an export of the cdm platform via a
|
69
|
* REST request. It is debatable if this a wanted behavior.
|
70
|
* For the time being it serves its purpose.
|
71
|
*/
|
72
|
@Controller
|
73
|
@RequestMapping(value = { "/checklist" })
|
74
|
public class ChecklistDemoController extends AbstractController implements ResourceLoaderAware{
|
75
|
|
76
|
@Autowired
|
77
|
private ApplicationContext appContext;
|
78
|
|
79
|
@Autowired
|
80
|
private ITermService termService;
|
81
|
|
82
|
@Autowired
|
83
|
private IClassificationService classificationService;
|
84
|
|
85
|
@Autowired
|
86
|
public ProgressMonitorController progressMonitorController;
|
87
|
|
88
|
private ResourceLoader resourceLoader;
|
89
|
|
90
|
|
91
|
/**
|
92
|
* There should only be one processes operating on the export
|
93
|
* therefore the according progress monitor uuid is stored in this static
|
94
|
* field.
|
95
|
*/
|
96
|
private static UUID indexMonitorUuid = null;
|
97
|
|
98
|
private final static long DAY_IN_MILLIS = 86400000;
|
99
|
|
100
|
|
101
|
|
102
|
private static final Logger logger = Logger.getLogger(ChecklistDemoController.class);
|
103
|
|
104
|
/**
|
105
|
* Helper method, which allows to convert strings directly into uuids.
|
106
|
*
|
107
|
* @param binder Special DataBinder for data binding from web request parameters to JavaBean objects.
|
108
|
*/
|
109
|
@InitBinder
|
110
|
public void initBinder(WebDataBinder binder) {
|
111
|
binder.registerCustomEditor(UuidList.class, new UUIDListPropertyEditor());
|
112
|
binder.registerCustomEditor(NamedArea.class, new TermBaseListPropertyEditor<>(termService));
|
113
|
binder.registerCustomEditor(UUID.class, new UUIDEditor());
|
114
|
}
|
115
|
|
116
|
|
117
|
|
118
|
|
119
|
/**
|
120
|
* Documentation webservice for this controller.
|
121
|
*
|
122
|
* @param response unused
|
123
|
* @param request unused
|
124
|
* @return
|
125
|
* @throws IOException
|
126
|
*/
|
127
|
@RequestMapping(value = {""}, method = { RequestMethod.GET})
|
128
|
public ModelAndView exportGetExplanation(HttpServletResponse response,
|
129
|
HttpServletRequest request) throws IOException{
|
130
|
ModelAndView mv = new ModelAndView();
|
131
|
// Read apt documentation file.
|
132
|
Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/checklist-catalogue-default.apt");
|
133
|
// using input stream as this works for both files in the classes directory
|
134
|
// as well as files inside jars
|
135
|
InputStream aptInputStream = resource.getInputStream();
|
136
|
// Build Html View
|
137
|
Map<String, String> modelMap = new HashMap<>();
|
138
|
// Convert Apt to Html
|
139
|
modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
|
140
|
mv.addAllObjects(modelMap);
|
141
|
|
142
|
HtmlView hv = new HtmlView();
|
143
|
mv.setView(hv);
|
144
|
return mv;
|
145
|
}
|
146
|
|
147
|
/**
|
148
|
* This service endpoint is for generating the documentation site.
|
149
|
* If any request of the other endpoint below is incomplete or false
|
150
|
* then this method will be triggered.
|
151
|
*
|
152
|
* @param response
|
153
|
* @param request
|
154
|
* @return
|
155
|
* @throws IOException
|
156
|
*/
|
157
|
public ModelAndView exportGetExplanation(HttpServletResponse response,
|
158
|
HttpServletRequest request, Resource res) throws IOException{
|
159
|
ModelAndView mv = new ModelAndView();
|
160
|
// Read apt documentation file.
|
161
|
Resource resource = (res!= null) ? res : resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/checklist-catalogue-default.apt");
|
162
|
// using input stream as this works for both files in the classes directory
|
163
|
// as well as files inside jars
|
164
|
InputStream aptInputStream = resource.getInputStream();
|
165
|
// Build Html View
|
166
|
Map<String, String> modelMap = new HashMap<>();
|
167
|
// Convert Apt to Html
|
168
|
modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
|
169
|
mv.addAllObjects(modelMap);
|
170
|
|
171
|
HtmlView hv = new HtmlView();
|
172
|
mv.setView(hv);
|
173
|
return mv;
|
174
|
}
|
175
|
|
176
|
/**
|
177
|
*
|
178
|
* This service endpoint generates a json and xml view of the exported list.
|
179
|
* It takes advantage of pagination.
|
180
|
*
|
181
|
* @param classification uuid of the classification to export
|
182
|
* @param pageIndex
|
183
|
* @param pageSize
|
184
|
* @param response
|
185
|
* @param request
|
186
|
* @return
|
187
|
* @throws IOException
|
188
|
*/
|
189
|
@RequestMapping(value = { "export" }, method = { RequestMethod.GET })
|
190
|
public ModelAndView doGeneralExport(
|
191
|
@RequestParam(value = "classification", required = false) String classificationUUID,
|
192
|
@RequestParam(value = "pageIndex", required = false) Integer pageIndex,
|
193
|
@RequestParam(value = "pageSize", required = false) Integer pageSize,
|
194
|
HttpServletResponse response,
|
195
|
HttpServletRequest request) throws IOException {
|
196
|
|
197
|
try{
|
198
|
if(pageSize == null) {
|
199
|
pageSize = 20;
|
200
|
}
|
201
|
if(pageIndex == null) {
|
202
|
pageIndex = 0;
|
203
|
}
|
204
|
|
205
|
PagerParameters pagerParams = new PagerParameters(pageSize, pageIndex);
|
206
|
pagerParams.normalizeAndValidate(response);
|
207
|
|
208
|
List<CsvDemoRecord> recordList = new ArrayList<>();
|
209
|
|
210
|
CsvDemoExportConfigurator config = setTaxExportConfigurator(null ,classificationUUID,
|
211
|
null, null, null, false, false);
|
212
|
config.setPageSize(pagerParams.getPageSize());
|
213
|
config.setPageNumber(pagerParams.getPageIndex());
|
214
|
config.setRecordList(recordList);
|
215
|
|
216
|
@SuppressWarnings("unchecked")
|
217
|
CdmApplicationAwareDefaultExport<CsvDemoExportConfigurator> defaultExport =
|
218
|
(CdmApplicationAwareDefaultExport<CsvDemoExportConfigurator>) appContext.getBean("defaultExport");
|
219
|
defaultExport.invoke(config);
|
220
|
|
221
|
DefaultPagerImpl<CsvDemoRecord> dpi = new DefaultPagerImpl<>(pagerParams.getPageIndex(), config.getTaxonNodeListSize(), pagerParams.getPageSize(), recordList);
|
222
|
ModelAndView mv = new ModelAndView();
|
223
|
// mv.addObject(recordList);f
|
224
|
mv.addObject(dpi);
|
225
|
return mv;
|
226
|
}catch(Exception e){
|
227
|
Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/checklist-catalogue-export.apt");
|
228
|
return exportGetExplanation(response, request, resource);
|
229
|
}
|
230
|
|
231
|
}
|
232
|
|
233
|
|
234
|
/**
|
235
|
*
|
236
|
* This Service endpoint will offer a csv file. It caches the csv-file in the system temp directory
|
237
|
* and will only generate a new one after 24 hours. Or if explicitly triggerd by noCache parameter.
|
238
|
*
|
239
|
* @param featureUuids List of uuids to download/select {@link Feature feature}features
|
240
|
* @param clearCache will trigger export and avoids cached file
|
241
|
* @param classificationUUID Selected {@link Classification classification} to iterate the {@link Taxon}
|
242
|
* @param response HttpServletResponse which returns the ByteArrayOutputStream
|
243
|
* @throws Exception
|
244
|
*/
|
245
|
@RequestMapping(value = { "exportCSV" }, method = { RequestMethod.GET })
|
246
|
public synchronized ModelAndView doExportRedlist(
|
247
|
@RequestParam(value = "features", required = false) final UuidList featureUuids,
|
248
|
@RequestParam(value = "clearCache", required = false) final boolean clearCache,
|
249
|
@RequestParam(value = "demoExport", required = false) final boolean demoExport,
|
250
|
@RequestParam(value = "conceptExport", required = false) final boolean conceptExport,
|
251
|
@RequestParam(value = "classification", required = false) final String classificationUUID,
|
252
|
@RequestParam(value = "area", required = false) final UuidList areas,
|
253
|
@RequestParam(value = "downloadTokenValueId", required = false) final String downloadTokenValueId,
|
254
|
@RequestParam(value = "priority", required = false) Integer priority,
|
255
|
final HttpServletResponse response,
|
256
|
final HttpServletRequest request) throws Exception {
|
257
|
/**
|
258
|
* ========================================
|
259
|
* progress monitor & new thread for export
|
260
|
* ========================================
|
261
|
*/
|
262
|
try{
|
263
|
ModelAndView mv = new ModelAndView();
|
264
|
String fileName = classificationService.find(UUID.fromString(classificationUUID)).getTitleCache();
|
265
|
final File cacheFile = new File(new File(System.getProperty("java.io.tmpdir")), classificationUUID);
|
266
|
final String origin = request.getRequestURL().append('?').append(request.getQueryString()).toString();
|
267
|
|
268
|
Long result = null;
|
269
|
if(cacheFile.exists()){
|
270
|
result = System.currentTimeMillis() - cacheFile.lastModified();
|
271
|
}
|
272
|
//if file exists return file instantly
|
273
|
//timestamp older than one day?
|
274
|
if(clearCache == false && result != null){ //&& result < 7*(DAY_IN_MILLIS)
|
275
|
logger.info("result of calculation: " + result);
|
276
|
Map<String, File> modelMap = new HashMap<String, File>();
|
277
|
modelMap.put("file", cacheFile);
|
278
|
mv.addAllObjects(modelMap);
|
279
|
FileDownloadView fdv = new FileDownloadView("text/csv", fileName, "txt", "UTF-8");
|
280
|
mv.setView(fdv);
|
281
|
return mv;
|
282
|
}else{//trigger progress monitor and performExport()
|
283
|
String processLabel = "Exporting...";
|
284
|
final String frontbaseUrl = null;
|
285
|
ProgressMonitorUtil progressUtil = new ProgressMonitorUtil(progressMonitorController);
|
286
|
if (!progressMonitorController.isMonitorRunning(indexMonitorUuid)) {
|
287
|
indexMonitorUuid = progressUtil.registerNewMonitor();
|
288
|
Thread subThread = new Thread() {
|
289
|
@Override
|
290
|
public void run() {
|
291
|
try {
|
292
|
cacheFile.createNewFile();
|
293
|
} catch (IOException e) {
|
294
|
logger.info("Could not create file "+ e);
|
295
|
}
|
296
|
performExport(cacheFile, featureUuids, classificationUUID, areas, downloadTokenValueId, demoExport, conceptExport, origin, response, progressMonitorController.getMonitor(indexMonitorUuid));
|
297
|
}
|
298
|
};
|
299
|
if (priority == null) {
|
300
|
priority = AbstractController.DEFAULT_BATCH_THREAD_PRIORITY;
|
301
|
}
|
302
|
subThread.setPriority(priority);
|
303
|
subThread.start();
|
304
|
}
|
305
|
mv = progressUtil.respondWithMonitorOrDownload(frontbaseUrl, origin, processLabel, indexMonitorUuid, false, request, response);
|
306
|
}
|
307
|
return mv;
|
308
|
}catch(Exception e){
|
309
|
//TODO: Write an specific documentation for this service endpoint
|
310
|
Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/checklist-catalogue-exportCSV.apt");
|
311
|
return exportGetExplanation(response, request, resource);
|
312
|
}
|
313
|
}
|
314
|
|
315
|
|
316
|
//=========== Helper Methods ===============//
|
317
|
|
318
|
/**
|
319
|
*
|
320
|
* This private methods finally triggers the export back in the io-package and will create a cache file
|
321
|
* in system temp directory.
|
322
|
*
|
323
|
* @param downloadTokenValueId
|
324
|
* @param conceptExport
|
325
|
* @param demoExport
|
326
|
* @param response
|
327
|
* @param byteArrayOutputStream
|
328
|
* @param config
|
329
|
* @param defaultExport
|
330
|
*/
|
331
|
private void performExport(File cacheFile, UuidList featureUuids,String classificationUUID, UuidList areas,
|
332
|
String downloadTokenValueId, boolean demoExport, boolean conceptExport, String origin, HttpServletResponse response, IRestServiceProgressMonitor progressMonitor) {
|
333
|
|
334
|
progressMonitor.subTask("configure export");
|
335
|
CsvDemoExportConfigurator config = setTaxExportConfigurator(cacheFile, classificationUUID, featureUuids, areas, progressMonitor, demoExport, conceptExport);
|
336
|
CdmApplicationAwareDefaultExport<CsvDemoExportConfigurator> defaultExport =
|
337
|
(CdmApplicationAwareDefaultExport<CsvDemoExportConfigurator>) appContext.getBean("defaultExport");
|
338
|
progressMonitor.subTask("invoke export");
|
339
|
defaultExport.invoke(config); //triggers export
|
340
|
progressMonitor.subTask("wrote results to cache");
|
341
|
progressMonitor.done();
|
342
|
progressMonitor.setOrigin(origin);
|
343
|
}
|
344
|
|
345
|
/**
|
346
|
* Cofiguration method to set the configuration details for the defaultExport in the application context.
|
347
|
* @param cacheFile
|
348
|
*
|
349
|
* @param classificationUUID pass-through the selected {@link Classification classification}
|
350
|
* @param featureUuids pass-through the selected {@link Feature feature} of a {@link Taxon}, in order to fetch it.
|
351
|
* @param areas
|
352
|
* @param byteArrayOutputStream pass-through the stream to write out the data later.
|
353
|
* @param progressMonitor
|
354
|
* @param conceptExport
|
355
|
* @param demoExport
|
356
|
* @return the CsvTaxExportConfiguratorRedlist config
|
357
|
*/
|
358
|
private CsvDemoExportConfigurator setTaxExportConfigurator(File cacheFile, String classificationUUID, UuidList featureUuids, UuidList areas, IRestServiceProgressMonitor progressMonitor, boolean demoExport, boolean conceptExport) {
|
359
|
|
360
|
@SuppressWarnings({ "unchecked", "rawtypes" })
|
361
|
Set<UUID> classificationUUIDS = new HashSet
|
362
|
(Arrays.asList(new UUID[] {UUID.fromString(classificationUUID)}));
|
363
|
if(cacheFile == null){
|
364
|
String destination = System.getProperty("java.io.tmpdir");
|
365
|
cacheFile = new File(destination);
|
366
|
}
|
367
|
List<Feature> features = new ArrayList<>();
|
368
|
if(featureUuids != null){
|
369
|
for(UUID uuid : featureUuids) {
|
370
|
features.add((Feature) termService.find(uuid));
|
371
|
}
|
372
|
}
|
373
|
List<NamedArea> selectedAreas = new ArrayList<>();
|
374
|
if(areas != null){
|
375
|
for(UUID area:areas){
|
376
|
logger.info(area);
|
377
|
selectedAreas.add((NamedArea)termService.find(area));
|
378
|
}
|
379
|
}
|
380
|
|
381
|
CsvDemoExportConfigurator config = CsvDemoExportConfigurator.NewInstance(null, cacheFile);
|
382
|
config.setDestination(cacheFile);
|
383
|
config.setProgressMonitor(progressMonitor);
|
384
|
config.setHasHeaderLines(true);
|
385
|
config.setFieldsTerminatedBy("\t");
|
386
|
config.setClassificationUuids(classificationUUIDS);
|
387
|
if(demoExport == false && conceptExport == false){
|
388
|
config.createPreSelectedExport(false, true);
|
389
|
}else{
|
390
|
config.createPreSelectedExport(demoExport, conceptExport);
|
391
|
}
|
392
|
config.setFeatures(features);
|
393
|
config.setNamedAreas(selectedAreas);
|
394
|
return config;
|
395
|
}
|
396
|
|
397
|
@Override
|
398
|
public void setService(IService service) {
|
399
|
// TODO Auto-generated method stub
|
400
|
}
|
401
|
|
402
|
@Override
|
403
|
public void setResourceLoader(ResourceLoader resourceLoader) {
|
404
|
this.resourceLoader = resourceLoader;
|
405
|
}
|
406
|
|
407
|
}
|