source: trunk/autoquest-httpmonitor/src/main/java/de/ugoe/cs/autoquest/httpmonitor/proxy/ExchangeListenerManager.java

Last change on this file was 2246, checked in by pharms, 7 years ago
File size: 8.7 KB
Line 
1//   Copyright 2012 Georg-August-Universität Göttingen, Germany
2//
3//   Licensed under the Apache License, Version 2.0 (the "License");
4//   you may not use this file except in compliance with the License.
5//   You may obtain a copy of the License at
6//
7//       http://www.apache.org/licenses/LICENSE-2.0
8//
9//   Unless required by applicable law or agreed to in writing, software
10//   distributed under the License is distributed on an "AS IS" BASIS,
11//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12//   See the License for the specific language governing permissions and
13//   limitations under the License.
14
15package de.ugoe.cs.autoquest.httpmonitor.proxy;
16
17import java.nio.ByteBuffer;
18import java.util.ArrayList;
19import java.util.HashMap;
20import java.util.List;
21import java.util.Map;
22import java.util.Timer;
23import java.util.TimerTask;
24import java.util.logging.Level;
25
26import javax.servlet.http.HttpServletRequest;
27import javax.servlet.http.HttpServletResponse;
28
29import de.ugoe.cs.autoquest.httpmonitor.HttpMonitorComponent;
30import de.ugoe.cs.autoquest.httpmonitor.HttpMonitorException;
31import de.ugoe.cs.autoquest.httpmonitor.HttpMonitorExchangeHandler;
32import de.ugoe.cs.autoquest.httpmonitor.IdGenerator;
33import de.ugoe.cs.autoquest.plugin.http.logdata.Status;
34import de.ugoe.cs.util.console.Console;
35
36/**
37 * <p>
38 * {@link ExchangeListener}s need to be managed, as there is one instantiated for each proxied
39 * request/response. They need to be timed out, if for a longer time, no update is received. All
40 * this is done by the ExchangeListenerManager.
41 * </p>
42 *
43 * @author Patrick Harms
44 */
45class ExchangeListenerManager implements HttpMonitorComponent {
46
47    /**
48     * <p>
49     * the timeout after which a writer of an inactive client is closed
50     * </p>
51     */
52    private static final int SESSION_TIMEOUT = 10 * 60 * 1000;
53   
54    /**
55     * <p>
56     * the exchange handler forwarded to the exchange listeners
57     * </p>
58     */
59    private HttpMonitorExchangeHandler exchangeHandler;
60   
61    /**
62     * <p>
63     * the id generator used to generate ordering ids for requests and responses
64     * </p>
65     */
66    private IdGenerator idGenerator;
67   
68    /**
69     * <p>
70     * the mapping of requests handled by the proxy to the respective exchange listeners
71     * </p>
72     */
73    private Map<HttpServletRequest, ExchangeListener> listeners;
74
75    /**
76     * <p>
77     * a timer used to detect exchange listener timeouts
78     * </p>
79     */
80    private Timer listenerTimer;
81
82    /**
83     * <p>
84     * creates the exchange listener manager with the exchange handler to be forwarded to the
85     * exchange listeners
86     * </p>
87     *
88     * @param exchangeHandler the exchange handler to be forwarded to the exchange listeners
89     * @param idGenerator     the id generator to used for generating ordering ids
90     */
91    ExchangeListenerManager(HttpMonitorExchangeHandler exchangeHandler, IdGenerator idGenerator) {
92        this.exchangeHandler = exchangeHandler;
93        this.idGenerator = idGenerator;
94    }
95
96    /* (non-Javadoc)
97     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitorComponent#init()
98     */
99    @Override
100    public synchronized void init() throws IllegalStateException, HttpMonitorException {
101        Console.traceln(Level.FINER, "initializing exchange listener manager");
102       
103        if (listeners != null) {
104            throw new IllegalStateException("already initialized");
105        }
106       
107        listeners = new HashMap<HttpServletRequest, ExchangeListener>();
108        listenerTimer = new Timer();
109    }
110
111    /* (non-Javadoc)
112     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitorComponent#start()
113     */
114    @Override
115    public synchronized void start() throws IllegalStateException, HttpMonitorException {
116        Console.traceln(Level.FINER, "starting exchange listener manager");
117
118        if (listeners == null) {
119            throw new IllegalStateException("not initialized");
120        }
121       
122        listenerTimer.schedule
123            (new ListenerMonitorTimerTask(), SESSION_TIMEOUT / 2, SESSION_TIMEOUT / 2);
124    }
125
126    /* (non-Javadoc)
127     * @see de.ugoe.cs.autoquest.htmlmonitor.HtmlMonitorComponent#stop()
128     */
129    @Override
130    public synchronized void stop() {
131        Console.traceln(Level.FINER, "stopping exchange listener manager");
132
133        if (listenerTimer != null) {
134            listenerTimer.cancel();
135        }
136       
137        if (listeners != null) {
138            for (ExchangeListener listener : listeners.values()) {
139                listener.onFinish(Status.TIMEOUT);
140            }
141        }
142       
143        listeners = null;
144    }
145
146    /**
147     * <p>
148     * called, when the request was received by the proxy. Calls the appropriate method on
149     * the exchange listener.
150     * </p>
151     *
152     * @param request the request of the exchange
153     */
154    public void onRequest(HttpServletRequest request) throws IllegalStateException {
155        ensureListener(request).onRequest(request);
156    }
157
158    /**
159     * <p>
160     * called, when some content of the request was processed by the proxy. Calls the appropriate
161     * method on the exchange listener.
162     * </p>
163     *
164     * @param request the request of the exchange
165     * @param data    the processed content of the request of the exchange
166     */
167    public void onRequestContent(HttpServletRequest request, ByteBuffer data)
168        throws IllegalStateException
169    {
170        ensureListener(request).onRequestContent(data);
171    }
172
173    /**
174     * <p>
175     * called, when the response is to be returned by the proxy. Calls the appropriate
176     * method on the exchange listener.
177     * </p>
178     *
179     * @param request  the request of the exchange
180     * @param response the response of the exchange
181     */
182    public void onResponse(HttpServletRequest request, HttpServletResponse response)
183        throws IllegalStateException
184    {
185        ensureListener(request).onResponse(response);
186    }
187
188    /**
189     * <p>
190     * called, when some content of the response was processed by the proxy. Calls the appropriate
191     * method on the exchange listener.
192     * </p>
193     *
194     * @param request the request of the exchange
195     * @param data    the processed content of the response of the exchange
196     */
197    public void onResponseContent(HttpServletRequest request, ByteBuffer data)
198        throws IllegalStateException
199    {
200        ensureListener(request).onResponseContent(data);
201    }
202
203    /**
204     * <p>
205     * called, when proxy finished proxying a request. Calls the appropriate method on the
206     * exchange listener and afterwards cleans up the listener.
207     * </p>
208     *
209     * @param request the request of the exchange
210     * @param status  the status of the proxying after finalization
211     */
212    public void onFinish(HttpServletRequest request, Status status) throws IllegalStateException {
213        ensureListener(request).onFinish(status);
214        Console.traceln(Level.FINEST, "removing exchange listener for " + request);
215        synchronized (this) {
216            listeners.remove(request);
217        }
218    }
219
220    /**
221     * <p>
222     * convenience method to ensure a listener for a specific HTTP servlet request to be handled.
223     * </p>
224     */
225    private ExchangeListener ensureListener(HttpServletRequest request) {
226        synchronized (this) {
227            ExchangeListener listener = listeners.get(request);
228       
229            if (listener == null) {
230                Console.traceln(Level.FINEST, "creating exchange listener for " + request);
231                listener = new ExchangeListener(exchangeHandler, idGenerator);
232                listeners.put(request, listener);
233            }
234
235            return listener;
236        }
237    }
238
239    /**
240     * <p>
241     * internal timer task used for detecting exchange timeouts
242     * </p>
243     *
244     * @author Patrick Harms
245     */
246    public class ListenerMonitorTimerTask extends TimerTask {
247
248        /* (non-Javadoc)
249         * @see java.lang.Runnable#run()
250         */
251        @Override
252        public void run() {
253            synchronized (ExchangeListenerManager.this) {
254                List<HttpServletRequest> timeoutRequests = new ArrayList<HttpServletRequest>();
255                for (Map.Entry<HttpServletRequest, ExchangeListener> entry : listeners.entrySet()) {
256                    ExchangeListener listener = entry.getValue();
257                   
258                    if (System.currentTimeMillis() - listener.getLastUpdate() > SESSION_TIMEOUT) {
259                        timeoutRequests.add(entry.getKey());
260                    }
261                }
262               
263                for (HttpServletRequest request : timeoutRequests) {
264                    ExchangeListener listener = listeners.remove(request);
265                    listener.onFinish(Status.TIMEOUT);
266                }
267            }
268        }
269
270    }
271
272}
Note: See TracBrowser for help on using the repository browser.