Project

General

Profile

Download (5.9 KB) Statistics
| Branch: | Tag: | Revision:
1
// $Id$
2
/**
3
* Copyright (C) 2012 EDIT
4
* European Distributed Institute of Taxonomy
5
* http://www.e-taxonomy.eu
6
*
7
* The contents of this file are subject to the Mozilla Public License Version 1.1
8
* See LICENSE.TXT at the top of this package for the full license terms.
9
*/
10
package eu.etaxonomy.cdm.remote.controller;
11

    
12
import java.io.IOException;
13
import java.util.ArrayList;
14
import java.util.HashMap;
15
import java.util.List;
16
import java.util.Map;
17
import java.util.UUID;
18

    
19
import javax.servlet.http.HttpServletRequest;
20
import javax.servlet.http.HttpServletResponse;
21

    
22
import org.apache.commons.io.FilenameUtils;
23
import org.springframework.stereotype.Controller;
24
import org.springframework.web.bind.WebDataBinder;
25
import org.springframework.web.bind.annotation.InitBinder;
26
import org.springframework.web.bind.annotation.PathVariable;
27
import org.springframework.web.bind.annotation.RequestMapping;
28
import org.springframework.web.bind.annotation.RequestMethod;
29
import org.springframework.web.servlet.ModelAndView;
30

    
31
import com.wordnik.swagger.annotations.Api;
32

    
33
import eu.etaxonomy.cdm.common.monitor.IRestServiceProgressMonitor;
34
import eu.etaxonomy.cdm.remote.editor.UUIDPropertyEditor;
35

    
36
/**
37
 * @author Andreas Kohlbecker
38
 * @date Jul 16, 2012
39
 *
40
 */
41
@Controller
42
@Api(value="progress", description="provides access to information on long running processes. "
43
        + "URIs to the resources exposed by this controller are provided in the responses to the"
44
        + "HTTP requests that trigger long term processes.")
45
@RequestMapping(value="/progress/")
46
public class ProgressMonitorController {
47

    
48
    private final Map<UUID, IRestServiceProgressMonitor> monitors = new HashMap<UUID, IRestServiceProgressMonitor>();
49

    
50
    private final Map<UUID, Long> timeoutMap = new HashMap<UUID, Long>();
51

    
52
    private Thread cleanUpThread = null;
53

    
54
    /**
55
     * Time out in minutes for monitors which are done.
56
     * A monitor which is set done will be removed after this interval.
57
     */
58
    private final int cleanUpTimeout = 1;
59

    
60
    /**
61
     *
62
     */
63
    private final int cleanUpInterval = 1000 * 10; // 10 seconds
64

    
65
    @InitBinder
66
    public void initBinder(WebDataBinder binder) {
67
        binder.registerCustomEditor(UUID.class, new UUIDPropertyEditor());
68
    }
69

    
70
    public ProgressMonitorController(){
71

    
72
        this.cleanUpThread = new Thread(){
73

    
74
            @Override
75
            public void run() {
76
                while(true){
77
                    scheduledCeanUp();
78
                    try {
79
                        sleep(cleanUpInterval);
80
                    } catch (InterruptedException e) {
81
                        /* IGNORE */
82
                    }
83
                }
84
            }
85

    
86
        };
87
        cleanUpThread.start();
88
    }
89

    
90

    
91
    /**
92
     * run every n minutes clean up monitors which have been marked done x minutes ago
93
     */
94
    private void scheduledCeanUp() {
95

    
96
        List<UUID> timedOutMonitors = new ArrayList<UUID>();
97
        IRestServiceProgressMonitor monitor;
98

    
99
        long now = System.currentTimeMillis();
100
        long nextTimeout = now + cleanUpTimeout * 1000 * 60;
101

    
102

    
103
        // add monitors which are stopped or done to the timeoutMap
104
        for(UUID uuid : monitors.keySet()){
105
            monitor = monitors.get(uuid);
106
            if((monitor.isFailed() || monitor.isDone())){
107
                if(!timeoutMap.containsKey(uuid)){
108
                    timeoutMap.put(uuid, nextTimeout);
109
                }
110
            }
111
        }
112

    
113
        // check with monitor has timed out
114
        for(UUID uuid : timeoutMap.keySet()){
115
            if(timeoutMap.get(uuid) <= now){
116
                timedOutMonitors.add(uuid);
117
            }
118
        }
119

    
120
        //finally remove the monitors
121
        for(UUID uuid : timedOutMonitors){
122
            timeoutMap.remove(uuid);
123
            monitors.remove(uuid);
124
        }
125

    
126
    }
127

    
128
    public UUID registerMonitor(IRestServiceProgressMonitor monitor){
129
        UUID uuid = UUID.randomUUID();
130
        monitors.put(uuid, monitor);
131
        return uuid;
132
    }
133

    
134
    public IRestServiceProgressMonitor getMonitor(UUID uuid) {
135
        return monitors.get(uuid);
136
    }
137

    
138
    /**
139
     * returns true if the {@link IRestServiceProgressMonitor} identified by the <code>uuid</code>
140
     * exists and if it is still indicating a running thread
141
     * @param uuid
142
     * @return
143
     */
144
    public boolean isMonitorRunning(UUID uuid) {
145
        IRestServiceProgressMonitor monitor = getMonitor(uuid);
146
        return monitor != null && !monitor.isCanceled() && !monitor.isDone() && !monitor.isFailed();
147
    }
148

    
149
    /**
150
     * provides the relative path to the ProgressMonitor specified by its UUID.
151
     * File extensions like .xml, .json used during the initial request will be
152
     * preserved in order to not to break the content type negotiation.
153
     *
154
     * @param request
155
     *            the request for which to create he path for. The file
156
     *            extension will be read from the servlet path and is appended
157
     *            to the resulting path.
158
     * @param uuid
159
     *            the uuid key of the monitor
160
     * @return the path of the ProgressMonitor
161
     */
162
    public String pathFor(HttpServletRequest request, UUID uuid){
163
        String fileExtension = FilenameUtils.getExtension(request.getServletPath());
164
        return "/progress/" + uuid.toString() + (fileExtension.length() > 0 ? '.': "") + fileExtension;
165
    }
166

    
167
    @RequestMapping(value = "{uuid}", method = RequestMethod.GET)
168
    public ModelAndView doProgressMonitor(@PathVariable("uuid") UUID uuid, HttpServletRequest request, HttpServletResponse response)
169
            throws IOException {
170

    
171
        ModelAndView mv = new ModelAndView();
172

    
173
        if (monitors.containsKey(uuid)) {
174
            mv.addObject(monitors.get(uuid));
175
        } else {
176
            response.sendError(404, "No such progress monitor found. The process being monitored may "
177
                    + "have been completed and the according monitor may have been removed due to "
178
                    + "the clean up timepout.");
179
        }
180

    
181
        return mv;
182
    }
183
}
(43-43/57)