Project

General

Profile

Download (17.4 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.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.ITaxonService;
44
import eu.etaxonomy.cdm.api.service.ITermService;
45
import eu.etaxonomy.cdm.api.service.pager.impl.DefaultPagerImpl;
46
import eu.etaxonomy.cdm.common.DocUtils;
47
import eu.etaxonomy.cdm.common.monitor.IRestServiceProgressMonitor;
48
import eu.etaxonomy.cdm.io.common.CdmApplicationAwareDefaultExport;
49
import eu.etaxonomy.cdm.io.csv.redlist.demo.CsvDemoExportConfigurator;
50
import eu.etaxonomy.cdm.io.csv.redlist.demo.CsvDemoRecord;
51
import eu.etaxonomy.cdm.model.description.Feature;
52
import eu.etaxonomy.cdm.model.location.NamedArea;
53
import eu.etaxonomy.cdm.model.taxon.Classification;
54
import eu.etaxonomy.cdm.model.taxon.Taxon;
55
import eu.etaxonomy.cdm.remote.controller.AbstractController;
56
import eu.etaxonomy.cdm.remote.controller.ProgressMonitorController;
57
import eu.etaxonomy.cdm.remote.controller.util.PagerParameters;
58
import eu.etaxonomy.cdm.remote.controller.util.ProgressMonitorUtil;
59
import eu.etaxonomy.cdm.remote.editor.TermBaseListPropertyEditor;
60
import eu.etaxonomy.cdm.remote.editor.UUIDListPropertyEditor;
61
import eu.etaxonomy.cdm.remote.editor.UuidList;
62
import eu.etaxonomy.cdm.remote.view.FileDownloadView;
63
import eu.etaxonomy.cdm.remote.view.HtmlView;
64

    
65
/**
66
 * @author a.oppermann
67
 * @created 20.09.2012
68
 * <p>
69
 *  This controller enables an export of the cdm platform via a
70
 *  REST request. It is debatable if this a wanted behavior.
71
 *  For the time being it serves its purpose.
72
 */
73
@Controller
74
@RequestMapping(value = { "/checklist" })
75
public class ChecklistDemoController extends AbstractController implements ResourceLoaderAware{
76

    
77
    @Autowired
78
    private ApplicationContext appContext;
79

    
80
    @Autowired
81
    private ITermService termService;
82

    
83
    @Autowired
84
    private ITaxonService taxonService;
85

    
86
    @Autowired
87
    private IClassificationService classificationService;
88

    
89
    @Autowired
90
    public ProgressMonitorController progressMonitorController;
91

    
92
    private ResourceLoader resourceLoader;
93

    
94

    
95
    /**
96
     * There should only be one processes operating on the export
97
     * therefore the according progress monitor uuid is stored in this static
98
     * field.
99
     */
100
    private static UUID indexMonitorUuid = null;
101

    
102
    private final static long DAY_IN_MILLIS = 86400000;
103

    
104

    
105

    
106
    private static final Logger logger = Logger.getLogger(ChecklistDemoController.class);
107

    
108
    /**
109
     * Helper method, which allows to convert strings directly into uuids.
110
     *
111
     * @param binder Special DataBinder for data binding from web request parameters to JavaBean objects.
112
     */
113
    @InitBinder
114
    public void initBinder(WebDataBinder binder) {
115
        binder.registerCustomEditor(UuidList.class, new UUIDListPropertyEditor());
116
        binder.registerCustomEditor(NamedArea.class, new TermBaseListPropertyEditor<NamedArea>(termService));
117
        binder.registerCustomEditor(UUID.class, new UUIDEditor());
118
    }
119

    
120

    
121

    
122

    
123
    @RequestMapping(value = {""}, method = { RequestMethod.GET})
124
    public ModelAndView exportGetExplanation(HttpServletResponse response,
125
            HttpServletRequest request) throws IOException{
126
        ModelAndView mv = new ModelAndView();
127
        // Read apt documentation file.
128
        Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/checklist-catalogue-default.apt");
129
        // using input stream as this works for both files in the classes directory
130
        // as well as files inside jars
131
        InputStream aptInputStream = resource.getInputStream();
132
        // Build Html View
133
        Map<String, String> modelMap = new HashMap<String, String>();
134
        // Convert Apt to Html
135
        modelMap.put("html", DocUtils.convertAptToHtml(aptInputStream));
136
        mv.addAllObjects(modelMap);
137

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

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

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

    
172
    /**
173
     *
174
     * This service endpoint generates a json and xml view of the exported list.
175
     * It takes advantage of pagination.
176
     *
177
     * @param classificationUUID
178
     * @param pageNumber
179
     * @param pageSize
180
     * @param response
181
     * @param request
182
     * @return
183
     * @throws IOException
184
     */
185
    @RequestMapping(value = { "export" }, method = { RequestMethod.GET })
186
    public ModelAndView doGeneralExport(
187
            @RequestParam(value = "classification", required = false) String classificationUUID,
188
            @RequestParam(value = "pageNumber", required = false) Integer pageNumber,
189
            @RequestParam(value = "pageSize", required = false) Integer pageSize,
190
            HttpServletResponse response,
191
            HttpServletRequest request) throws IOException {
192

    
193
        try{
194
            if(pageSize == null) {
195
                pageSize = 20;
196
            }
197
            if(pageNumber == null) {
198
                pageNumber = 0;
199
            }
200

    
201
            PagerParameters pagerParams = new PagerParameters(pageSize, pageNumber);
202
            pagerParams.normalizeAndValidate(response);
203

    
204
            List<CsvDemoRecord> recordList = new ArrayList<CsvDemoRecord>();
205

    
206
            CsvDemoExportConfigurator config = setTaxExportConfigurator(null ,classificationUUID, null, null, null, false, false);
207
            config.setPageSize(pagerParams.getPageSize());
208
            config.setPageNumber(pagerParams.getPageIndex());
209
            config.setRecordList(recordList);
210

    
211
            CdmApplicationAwareDefaultExport<?> defaultExport = (CdmApplicationAwareDefaultExport<?>) appContext.getBean("defaultExport");
212
            defaultExport.invoke(config);
213

    
214
            DefaultPagerImpl<CsvDemoRecord> dpi = new DefaultPagerImpl<CsvDemoRecord>(pagerParams.getPageIndex(), config.getTaxonNodeListSize(), pagerParams.getPageSize(), recordList);
215
            ModelAndView mv = new ModelAndView();
216
//            mv.addObject(recordList);f
217
            mv.addObject(dpi);
218
            return mv;
219
        }catch(Exception e){
220
            Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/checklist-catalogue-export.apt");
221
            return exportGetExplanation(response, request, resource);
222
        }
223

    
224
    }
225

    
226

    
227
    /**
228
     *
229
     * This Service endpoint will offer a csv file. It caches the csv-file in the system temp directory
230
     * and will only generate a new one after 24 hours. Or if explicitly triggerd by noCache parameter.
231
     *
232
     * @param featureUuids List of uuids to download/select {@link Feature feature}features
233
     * @param clearCache will trigger export and avoids cached file
234
     * @param classificationUUID Selected {@link Classification classification} to iterate the {@link Taxon}
235
     * @param response HttpServletResponse which returns the ByteArrayOutputStream
236
     * @throws Exception
237
     */
238
    @RequestMapping(value = { "exportCSV" }, method = { RequestMethod.GET })
239
    public synchronized ModelAndView doExportRedlist(
240
            @RequestParam(value = "features", required = false) final UuidList featureUuids,
241
            @RequestParam(value = "clearCache", required = false) final boolean clearCache,
242
            @RequestParam(value = "demoExport", required = false) final boolean demoExport,
243
            @RequestParam(value = "conceptExport", required = false) final boolean conceptExport,
244
            @RequestParam(value = "classification", required = false) final String classificationUUID,
245
            @RequestParam(value = "area", required = false) final UuidList areas,
246
            @RequestParam(value = "downloadTokenValueId", required = false) final String downloadTokenValueId,
247
            @RequestParam(value = "priority", required = false) Integer priority,
248
            final HttpServletResponse response,
249
            final HttpServletRequest request) throws Exception {
250
        /**
251
         * ========================================
252
         * progress monitor & new thread for export
253
         * ========================================
254
         */
255
        try{
256
            ModelAndView mv = new ModelAndView();
257
            String fileName = classificationService.find(UUID.fromString(classificationUUID)).getTitleCache();
258
            final File cacheFile = new File(new File(System.getProperty("java.io.tmpdir")), classificationUUID);
259
            final String origin = request.getRequestURL().append('?').append(request.getQueryString()).toString();
260

    
261
            Long result = null;
262
            if(cacheFile.exists()){
263
                result = System.currentTimeMillis() - cacheFile.lastModified();
264
            }
265
            //if file exists return file instantly
266
            //timestamp older than one day?
267
            if(clearCache == false && result != null){ //&& result < 7*(DAY_IN_MILLIS)
268
                logger.info("result of calculation: " + result);
269
                Map<String, File> modelMap = new HashMap<String, File>();
270
                modelMap.put("file", cacheFile);
271
                mv.addAllObjects(modelMap);
272
                FileDownloadView fdv = new FileDownloadView("text/csv", fileName, "txt", "UTF-8");
273
                mv.setView(fdv);
274
                return mv;
275
            }else{//trigger progress monitor and performExport()
276
                String processLabel = "Exporting...";
277
                final String frontbaseUrl = null;
278
                ProgressMonitorUtil progressUtil = new ProgressMonitorUtil(progressMonitorController);
279
                if (!progressMonitorController.isMonitorRunning(indexMonitorUuid)) {
280
                    indexMonitorUuid = progressUtil.registerNewMonitor();
281
                    Thread subThread = new Thread() {
282
                        @Override
283
                        public void run() {
284
                            try {
285
                                cacheFile.createNewFile();
286
                            } catch (IOException e) {
287
                                logger.info("Could not create file "+ e);
288
                            }
289
                            performExport(cacheFile, featureUuids, classificationUUID, areas, downloadTokenValueId, demoExport, conceptExport, origin, response, progressMonitorController.getMonitor(indexMonitorUuid));
290
                        }
291
                    };
292
                    if (priority == null) {
293
                        priority = AbstractController.DEFAULT_BATCH_THREAD_PRIORITY;
294
                    }
295
                    subThread.setPriority(priority);
296
                    subThread.start();
297
                }
298
                mv = progressUtil.respondWithMonitorOrDownload(frontbaseUrl, origin, request, response, processLabel, indexMonitorUuid);
299
            }
300
            return mv;
301
        }catch(Exception e){
302
            //TODO: Write an specific documentation for this service endpoint
303
           Resource resource = resourceLoader.getResource("classpath:eu/etaxonomy/cdm/doc/remote/apt/checklist-catalogue-exportCSV.apt");
304
           return exportGetExplanation(response, request, resource);
305
        }
306
    }
307

    
308

    
309
    //=========== Helper Methods ===============//
310

    
311
    /**
312
     *
313
     * This private methods finally triggers the export back in the io-package and will create a cache file
314
     * in system temp directory.
315
     *
316
     * @param downloadTokenValueId
317
     * @param conceptExport
318
     * @param demoExport
319
     * @param response
320
     * @param byteArrayOutputStream
321
     * @param config
322
     * @param defaultExport
323
     */
324
    private void performExport(File cacheFile, UuidList featureUuids,String classificationUUID, UuidList areas,
325
            String downloadTokenValueId, boolean demoExport, boolean conceptExport, String origin, HttpServletResponse response, IRestServiceProgressMonitor progressMonitor) {
326

    
327
        progressMonitor.subTask("configure export");
328
        CsvDemoExportConfigurator config = setTaxExportConfigurator(cacheFile, classificationUUID, featureUuids, areas, progressMonitor, demoExport, conceptExport);
329
        CdmApplicationAwareDefaultExport<?> defaultExport = (CdmApplicationAwareDefaultExport<?>) appContext.getBean("defaultExport");
330
        progressMonitor.subTask("invoke export");
331
        defaultExport.invoke(config);  //triggers export
332
        progressMonitor.subTask("wrote results to cache");
333
        progressMonitor.done();
334
        progressMonitor.setOrigin(origin);
335
    }
336

    
337
    /**
338
     * Cofiguration method to set the configuration details for the defaultExport in the application context.
339
     * @param cacheFile
340
     *
341
     * @param classificationUUID pass-through the selected {@link Classification classification}
342
     * @param featureUuids pass-through the selected {@link Feature feature} of a {@link Taxon}, in order to fetch it.
343
     * @param areas
344
     * @param byteArrayOutputStream pass-through the stream to write out the data later.
345
     * @param progressMonitor
346
     * @param conceptExport
347
     * @param demoExport
348
     * @return the CsvTaxExportConfiguratorRedlist config
349
     */
350
    private CsvDemoExportConfigurator setTaxExportConfigurator(File cacheFile, String classificationUUID, UuidList featureUuids, UuidList areas, IRestServiceProgressMonitor progressMonitor, boolean demoExport, boolean conceptExport) {
351

    
352
        @SuppressWarnings({ "unchecked", "rawtypes" })
353
        Set<UUID> classificationUUIDS = new HashSet
354
        (Arrays.asList(new UUID[] {UUID.fromString(classificationUUID)}));
355
        if(cacheFile == null){
356
            String destination = System.getProperty("java.io.tmpdir");
357
            cacheFile = new File(destination);
358
        }
359
        List<Feature> features = new ArrayList<Feature>();
360
        if(featureUuids != null){
361
            for(UUID uuid : featureUuids) {
362
                features.add((Feature) termService.find(uuid));
363
            }
364
        }
365
        List<NamedArea> selectedAreas = new ArrayList<NamedArea>();
366
        if(areas != null){
367
            for(UUID area:areas){
368
                logger.info(area);
369
                selectedAreas.add((NamedArea)termService.find(area));
370
            }
371
        }
372

    
373
        CsvDemoExportConfigurator config = CsvDemoExportConfigurator.NewInstance(null, cacheFile);
374
        config.setDestination(cacheFile);
375
        config.setProgressMonitor(progressMonitor);
376
        config.setHasHeaderLines(true);
377
        config.setFieldsTerminatedBy("\t");
378
        config.setClassificationUuids(classificationUUIDS);
379
        if(demoExport == false && conceptExport == false){
380
        	config.createPreSelectedExport(false, true);
381
        }else{
382
        	config.createPreSelectedExport(demoExport, conceptExport);
383
        }
384
        if(features != null) {
385
            config.setFeatures(features);
386
        }
387
        config.setNamedAreas(selectedAreas);
388
        return config;
389
    }
390

    
391
    /* (non-Javadoc)
392
     * @see eu.etaxonomy.cdm.remote.controller.AbstractController#setService(eu.etaxonomy.cdm.api.service.IService)
393
     */
394
    @Override
395
    public void setService(IService service) {
396
        // TODO Auto-generated method stub
397

    
398
    }
399

    
400
    /* (non-Javadoc)
401
     * @see org.springframework.context.ResourceLoaderAware#setResourceLoader(org.springframework.core.io.ResourceLoader)
402
     */
403
    @Override
404
    public void setResourceLoader(ResourceLoader resourceLoader) {
405
        this.resourceLoader = resourceLoader;
406
    }
407

    
408
}
(1-1/2)