Project

General

Profile

Download (15.9 KB) Statistics
| Branch: | Tag: | Revision:
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.HashMap;
15
import java.util.HashSet;
16
import java.util.List;
17
import java.util.Map;
18
import java.util.Set;
19
import java.util.UUID;
20

    
21
import javax.servlet.http.HttpServletRequest;
22
import javax.servlet.http.HttpServletResponse;
23

    
24
import org.apache.log4j.Logger;
25
import org.springframework.beans.factory.annotation.Autowired;
26
import org.springframework.context.ApplicationContext;
27
import org.springframework.context.ResourceLoaderAware;
28
import org.springframework.core.io.Resource;
29
import org.springframework.core.io.ResourceLoader;
30
import org.springframework.stereotype.Controller;
31
import org.springframework.web.bind.WebDataBinder;
32
import org.springframework.web.bind.annotation.InitBinder;
33
import org.springframework.web.bind.annotation.RequestMapping;
34
import org.springframework.web.bind.annotation.RequestMethod;
35
import org.springframework.web.bind.annotation.RequestParam;
36
import org.springframework.web.servlet.ModelAndView;
37

    
38
import eu.etaxonomy.cdm.api.service.IClassificationService;
39
import eu.etaxonomy.cdm.api.service.IService;
40
import eu.etaxonomy.cdm.api.service.ITaxonNodeService;
41
import eu.etaxonomy.cdm.common.CdmUtils;
42
import eu.etaxonomy.cdm.common.DocUtils;
43
import eu.etaxonomy.cdm.common.monitor.IRestServiceProgressMonitor;
44
import eu.etaxonomy.cdm.io.common.CdmApplicationAwareDefaultExport;
45
import eu.etaxonomy.cdm.io.dwca.out.DwcaEmlRecord;
46
import eu.etaxonomy.cdm.io.dwca.out.DwcaTaxExportConfigurator;
47
import eu.etaxonomy.cdm.model.description.Feature;
48
import eu.etaxonomy.cdm.model.taxon.Classification;
49
import eu.etaxonomy.cdm.model.taxon.Taxon;
50
import eu.etaxonomy.cdm.model.taxon.TaxonNode;
51
import eu.etaxonomy.cdm.remote.controller.AbstractController;
52
import eu.etaxonomy.cdm.remote.controller.ProgressMonitorController;
53
import eu.etaxonomy.cdm.remote.controller.util.ProgressMonitorUtil;
54
import eu.etaxonomy.cdm.remote.editor.UUIDListPropertyEditor;
55
import eu.etaxonomy.cdm.remote.editor.UuidList;
56
import eu.etaxonomy.cdm.remote.view.FileDownloadView;
57
import eu.etaxonomy.cdm.remote.view.HtmlView;
58

    
59
/**
60
 * @author a.mueller
61
 * @created 28.06.2017
62
 * <p>
63
 *  This controller exports taxonomies via Darwin Core Archive
64
 *  (https://en.wikipedia.org/wiki/Darwin_Core_Archive).
65
 */
66
@Controller
67
@RequestMapping(value = { "/dwca" })
68
public class DwcaExportController extends AbstractController implements ResourceLoaderAware{
69

    
70

    
71
    private static final String DWCA_TAX_EXPORT_DOC_RESSOURCE = "classpath:eu/etaxonomy/cdm/doc/remote/apt/dwca-tax-export-default.apt";
72

    
73
    @Autowired
74
    private ApplicationContext appContext;
75

    
76
    @Autowired
77
    private IClassificationService classificationService;
78

    
79
    @Autowired
80
    private ITaxonNodeService taxonNodeService;
81

    
82
    @Autowired
83
    public ProgressMonitorController progressMonitorController;
84

    
85
    private ResourceLoader resourceLoader;
86

    
87

    
88
    /**
89
     * There should only be one processes operating on the export
90
     * therefore the according progress monitor uuid is stored in this static
91
     * field.
92
     */
93
    private static UUID indexMonitorUuid = null;
94

    
95
    private final static long DAY_IN_MILLIS = 86400000;
96

    
97

    
98

    
99
    private static final Logger logger = Logger.getLogger(DwcaExportController.class);
100

    
101
    /**
102
     * Helper method, which allows to convert strings directly into uuids.
103
     *
104
     * @param binder Special DataBinder for data binding from web request parameters to JavaBean objects.
105
     */
106
    @InitBinder
107
    public void initBinder(WebDataBinder binder) {
108
        binder.registerCustomEditor(UuidList.class, new UUIDListPropertyEditor());
109
//        binder.registerCustomEditor(NamedArea.class, new TermBaseListPropertyEditor<>(termService));
110
//        binder.registerCustomEditor(UUID.class, new UUIDEditor());
111
    }
112

    
113

    
114

    
115

    
116
    /**
117
     * Documentation webservice for this controller.
118
     *
119
     * @param response unused
120
     * @param request unused
121
     * @return
122
     * @throws IOException
123
     */
124
    @RequestMapping(value = {""}, method = { RequestMethod.GET})
125
    public ModelAndView exportGetExplanation(HttpServletResponse response,
126
            HttpServletRequest request) throws IOException{
127
        ModelAndView mv = new ModelAndView();
128
        // Read apt documentation file.
129
        Resource resource = resourceLoader.getResource(DWCA_TAX_EXPORT_DOC_RESSOURCE);
130
        // using input stream as this works for both files in the classes directory
131
        // as well as files inside jars
132
        InputStream aptInputStream = resource.getInputStream();
133
        // Build Html View
134
        Map<String, String> modelMap = new HashMap<>();
135
        // Convert Apt to Html
136
        modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
137
        mv.addAllObjects(modelMap);
138

    
139
        HtmlView hv = new HtmlView();
140
        mv.setView(hv);
141
        return mv;
142
    }
143

    
144
    /**
145
     * This service endpoint is for generating the documentation site.
146
     * If any request of the other endpoint below is incomplete or false
147
     * then this method will be triggered.
148
     *
149
     * @param response
150
     * @param request
151
     * @return
152
     * @throws IOException
153
     */
154
    public ModelAndView exportGetExplanation(HttpServletResponse response,
155
            HttpServletRequest request, Resource res) throws IOException{
156
        ModelAndView mv = new ModelAndView();
157
        // Read apt documentation file.
158
        Resource resource = (res!= null) ? res : resourceLoader.getResource(DWCA_TAX_EXPORT_DOC_RESSOURCE);
159
        // using input stream as this works for both files in the classes directory
160
        // as well as files inside jars
161
        InputStream aptInputStream = resource.getInputStream();
162
        // Build Html View
163
        Map<String, String> modelMap = new HashMap<>();
164
        // Convert Apt to Html
165
        modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
166
        mv.addAllObjects(modelMap);
167

    
168
        HtmlView hv = new HtmlView();
169
        mv.setView(hv);
170
        return mv;
171
    }
172

    
173

    
174

    
175
    /**
176
     *
177
     * This Service endpoint will offer a csv file. It caches the csv-file in the system temp directory
178
     * and will only generate a new one after 24 hours. Or if explicitly triggerd by noCache parameter.
179
     *
180
     * @param featureUuids List of uuids to download/select {@link Feature feature}features
181
     * @param clearCache will trigger export and avoids cached file
182
     * @param classificationUUID Selected {@link Classification classification} to iterate the {@link Taxon}
183
     * @param response HttpServletResponse which returns the ByteArrayOutputStream
184
     * @throws Exception
185
     */
186
    @RequestMapping(value = { "dwcaTaxExport" }, method = { RequestMethod.GET })
187
    public synchronized ModelAndView doDwcaTaxExport(
188
            @RequestParam(value = "subtrees", required = false) final UuidList subtreeUuids,
189
            @RequestParam(value = "clearCache", required = false) final boolean clearCache,
190
//            @RequestParam(value = "demoExport", required = false) final boolean demoExport,
191
//            @RequestParam(value = "conceptExport", required = false) final boolean conceptExport,
192
//            @RequestParam(value = "classification", required = false) final String classificationUUID,
193
//            @RequestParam(value = "area", required = false) final UuidList areas,
194
            @RequestParam(value = "downloadTokenValueId", required = false) final String downloadTokenValueId,
195
            @RequestParam(value = "priority", required = false) Integer priority,
196
            final HttpServletResponse response,
197
            final HttpServletRequest request) throws Exception {
198
        /**
199
         * ========================================
200
         * progress monitor & new thread for export
201
         * ========================================
202
         */
203
        try{
204
            ModelAndView mv = new ModelAndView();
205

    
206
            String fileName = makeFileName(response, subtreeUuids);
207

    
208
            final File cacheFile = new File(new File(System.getProperty("java.io.tmpdir")), fileName);
209
            final String origin = request.getRequestURL().append('?')
210
                    .append(request.getQueryString()).toString();
211

    
212
            Long result = null;
213
            if(cacheFile.exists()){
214
                result = System.currentTimeMillis() - cacheFile.lastModified();
215
            }
216
            //if file exists return file instantly
217
            //timestamp older than one day?
218
            if(clearCache == false && result != null){ //&& result < 7*(DAY_IN_MILLIS)
219
                logger.info("result of calculation: " + result);
220
                Map<String, File> modelMap = new HashMap<>();
221
                modelMap.put("file", cacheFile);
222
                mv.addAllObjects(modelMap);
223
                //application/zip
224
                FileDownloadView fdv = new FileDownloadView("application/octet-stream", fileName, "zip", "UTF-8");
225
                mv.setView(fdv);
226
                return mv;
227
            }else{//trigger progress monitor and performExport()
228
                String processLabel = "Exporting ...";
229
                final String frontbaseUrl = null;
230
                ProgressMonitorUtil progressUtil = new ProgressMonitorUtil(progressMonitorController);
231
                if (!progressMonitorController.isMonitorRunning(indexMonitorUuid)) {
232
                    indexMonitorUuid = progressUtil.registerNewMonitor();
233
                    Thread subThread = new Thread() {
234
                        @Override
235
                        public void run() {
236
                            try {
237
                                cacheFile.createNewFile();
238
                            } catch (IOException e) {
239
                                logger.info("Could not create file "+ e);
240
                            }
241
                            performExport(cacheFile, progressMonitorController.getMonitor(indexMonitorUuid),
242
                                    subtreeUuids, downloadTokenValueId, origin, response);
243
                        }
244
                    };
245
                    if (priority == null) {
246
                        priority = AbstractController.DEFAULT_BATCH_THREAD_PRIORITY;
247
                    }
248
                    subThread.setPriority(priority);
249
                    subThread.start();
250
                }
251
                mv = progressUtil.respondWithMonitorOrDownload(frontbaseUrl, origin, processLabel, indexMonitorUuid, false, request, response);
252
            }
253
            return mv;
254
        }catch(Exception e){
255
            //TODO: Write an specific documentation for this service endpoint
256
           Resource resource = resourceLoader.getResource(DWCA_TAX_EXPORT_DOC_RESSOURCE);
257
           return exportGetExplanation(response, request, resource);
258
        }
259
    }
260

    
261

    
262

    
263
    //=========== Helper Methods ===============//
264

    
265
    /**
266
     *
267
     * This private methods finally triggers the export back in the io-package and will create a cache file
268
     * in system temp directory.
269
     *
270
     * @param downloadTokenValueId
271
     * @param response
272
     * @param byteArrayOutputStream
273
     * @param config
274
     * @param defaultExport
275
     */
276
    private void performExport(File cacheFile, IRestServiceProgressMonitor progressMonitor,
277
            UuidList featureUuids, String downloadTokenValueId, String origin,
278
            HttpServletResponse response
279
            ) {
280

    
281
        progressMonitor.subTask("configure export");
282
        DwcaTaxExportConfigurator config = setDwcaTaxExportConfigurator(cacheFile, progressMonitor, featureUuids);
283
        @SuppressWarnings("unchecked")
284
        CdmApplicationAwareDefaultExport<DwcaTaxExportConfigurator> defaultExport =
285
                (CdmApplicationAwareDefaultExport<DwcaTaxExportConfigurator>)appContext.getBean("defaultExport");
286
        progressMonitor.subTask("invoke export");
287
        defaultExport.invoke(config);  //triggers export
288
        progressMonitor.subTask("wrote results to cache");
289
        progressMonitor.done();
290
        progressMonitor.setOrigin(origin);
291
    }
292

    
293
    /**
294
     * Cofiguration method to set the configuration details for the defaultExport in the application context.
295
     * @param cacheFile
296
     *
297
     * @param classificationUUID pass-through the selected {@link Classification classification}
298
     * @param featureUuids pass-through the selected {@link Feature feature} of a {@link Taxon}, in order to fetch it.
299
     * @param areas
300
     * @param byteArrayOutputStream pass-through the stream to write out the data later.
301
     * @param progressMonitor
302
     * @param conceptExport
303
     * @param demoExport
304
     * @return the CsvTaxExportConfiguratorRedlist config
305
     */
306
    private DwcaTaxExportConfigurator setDwcaTaxExportConfigurator(File cacheFile, IRestServiceProgressMonitor progressMonitor,
307
            UuidList subtreeUuids) {
308

    
309
        if(cacheFile == null){
310
            String destination = System.getProperty("java.io.tmpdir");
311
            cacheFile = new File(destination);
312
        }
313

    
314
        DwcaEmlRecord emlRecord = null;
315
        DwcaTaxExportConfigurator config = DwcaTaxExportConfigurator.NewInstance(
316
                null, cacheFile, emlRecord);
317

    
318
        Set<UUID> subtreeSet = new HashSet<>(subtreeUuids);
319
        config.setProgressMonitor(progressMonitor);
320
        config.setSubtreeUuids(subtreeSet);
321

    
322
//        config.setHasHeaderLines(true);
323
//        config.setFieldsTerminatedBy("\t");
324
//        config.setClassificationUuids(classificationUUIDS);
325

    
326
//        if(demoExport == false && conceptExport == false){
327
//        	config.createPreSelectedExport(false, true);
328
//        }else{
329
//        	config.createPreSelectedExport(demoExport, conceptExport);
330
//        }
331

    
332
        return config;
333
    }
334

    
335

    
336
    /**
337
     * @param response
338
     * @param subtreeUuids
339
     * @throws IOException
340
     */
341
    private String makeFileName(HttpServletResponse response, UuidList subtreeUuids) throws IOException {
342
        String fileName;
343
        if (subtreeUuids != null && ! subtreeUuids.isEmpty()){
344
            UUID firstUuid = subtreeUuids.get(0);
345
            TaxonNode node = taxonNodeService.find(firstUuid);
346
            if (node != null && node.getTaxon() != null){
347
                if (node.getTaxon().getName() != null){
348
                    fileName = node.getTaxon().getName().getTitleCache();
349
                }else{
350
                    fileName = node.getTaxon().getTitleCache();
351
                }
352
            }else if (node != null){
353
                fileName = node.getClassification().getTitleCache();
354
            }else{
355
                Classification classification = classificationService.find(firstUuid);
356
                if (classification != null){
357
                    fileName = classification.getTitleCache();
358
                }else{
359
                    //handle via repso
360
                    response.sendError(404, "Subtree uuid does not exist: " + firstUuid);
361
                    fileName = "Error";
362
                }
363
            }
364
        }else{
365
            List<Classification> classificationList = classificationService.list(null, 1, null
366
                    , null, null);
367
            if (!classificationList.isEmpty()){
368
                fileName = classificationList.get(0).getTitleCache();
369
            }else{
370
               //handle via repso
371
                response.sendError(404, "No classification found");
372
                fileName = "Error";
373
            }
374
        }
375

    
376

    
377
        fileName = fileName + "_" + uuidListToString(subtreeUuids, 40) + ".zip";
378

    
379
        return fileName;
380

    
381
    }
382

    
383
    private String uuidListToString(UuidList uuidList, Integer truncate) {
384
        String result = null;
385
        for (UUID uuid : uuidList){
386
            result = CdmUtils.concat("_", uuid.toString());
387
        }
388
        if (result != null && result.length() > truncate){
389
            result = result.substring(0, truncate);
390
        }
391
        return result;
392
    }
393

    
394
    @Override
395
    public void setService(IService service) {}
396

    
397
    @Override
398
    public void setResourceLoader(ResourceLoader resourceLoader) {
399
        this.resourceLoader = resourceLoader;
400
    }
401

    
402
}
(3-3/3)