1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 package org.apache.mina.filter.reqres;
21
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.Iterator;
25 import java.util.LinkedHashSet;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Set;
29 import java.util.concurrent.ConcurrentHashMap;
30 import java.util.concurrent.ScheduledExecutorService;
31 import java.util.concurrent.ScheduledFuture;
32 import java.util.concurrent.TimeUnit;
33
34 import org.apache.mina.core.filterchain.IoFilterChain;
35 import org.apache.mina.core.session.AttributeKey;
36 import org.apache.mina.core.session.IoSession;
37 import org.apache.mina.core.write.WriteRequest;
38 import org.apache.mina.filter.util.WriteRequestFilter;
39 import org.slf4j.Logger;
40 import org.slf4j.LoggerFactory;
41
42
43
44
45
46
47
48 public class RequestResponseFilter extends WriteRequestFilter {
49
50 private final AttributeKey RESPONSE_INSPECTOR = new AttributeKey(getClass(), "responseInspector");
51
52 private final AttributeKey REQUEST_STORE = new AttributeKey(getClass(), "requestStore");
53
54 private final AttributeKey UNRESPONDED_REQUEST_STORE = new AttributeKey(getClass(), "unrespondedRequestStore");
55
56 private final ResponseInspectorFactory responseInspectorFactory;
57
58 private final ScheduledExecutorService timeoutScheduler;
59
60 private final static Logger LOGGER = LoggerFactory.getLogger(RequestResponseFilter.class);
61
62 public RequestResponseFilter(final ResponseInspector responseInspector, ScheduledExecutorService timeoutScheduler) {
63 if (responseInspector == null) {
64 throw new IllegalArgumentException("responseInspector");
65 }
66 if (timeoutScheduler == null) {
67 throw new IllegalArgumentException("timeoutScheduler");
68 }
69 this.responseInspectorFactory = new ResponseInspectorFactory() {
70 public ResponseInspector getResponseInspector() {
71 return responseInspector;
72 }
73 };
74 this.timeoutScheduler = timeoutScheduler;
75 }
76
77 public RequestResponseFilter(ResponseInspectorFactory responseInspectorFactory,
78 ScheduledExecutorService timeoutScheduler) {
79 if (responseInspectorFactory == null) {
80 throw new IllegalArgumentException("responseInspectorFactory");
81 }
82 if (timeoutScheduler == null) {
83 throw new IllegalArgumentException("timeoutScheduler");
84 }
85 this.responseInspectorFactory = responseInspectorFactory;
86 this.timeoutScheduler = timeoutScheduler;
87 }
88
89 @Override
90 public void onPreAdd(IoFilterChain parent, String name, NextFilter nextFilter) throws Exception {
91 if (parent.contains(this)) {
92 throw new IllegalArgumentException(
93 "You can't add the same filter instance more than once. Create another instance and add it.");
94 }
95
96 IoSession session = parent.getSession();
97 session.setAttribute(RESPONSE_INSPECTOR, responseInspectorFactory.getResponseInspector());
98 session.setAttribute(REQUEST_STORE, createRequestStore(session));
99 session.setAttribute(UNRESPONDED_REQUEST_STORE, createUnrespondedRequestStore(session));
100 }
101
102 @Override
103 public void onPostRemove(IoFilterChain parent, String name, NextFilter nextFilter) throws Exception {
104 IoSession session = parent.getSession();
105
106 destroyUnrespondedRequestStore(getUnrespondedRequestStore(session));
107 destroyRequestStore(getRequestStore(session));
108
109 session.removeAttribute(UNRESPONDED_REQUEST_STORE);
110 session.removeAttribute(REQUEST_STORE);
111 session.removeAttribute(RESPONSE_INSPECTOR);
112 }
113
114 @Override
115 public void messageReceived(NextFilter nextFilter, IoSession session, Object message) throws Exception {
116 ResponseInspector responseInspector = (ResponseInspector) session.getAttribute(RESPONSE_INSPECTOR);
117 Object requestId = responseInspector.getRequestId(message);
118 if (requestId == null) {
119
120 nextFilter.messageReceived(session, message);
121 return;
122 }
123
124
125 ResponseType type = responseInspector.getResponseType(message);
126 if (type == null) {
127 nextFilter.exceptionCaught(session, new IllegalStateException(responseInspector.getClass().getName()
128 + "#getResponseType() may not return null."));
129 }
130
131 Map<Object, Request> requestStore = getRequestStore(session);
132
133 Request request;
134 switch (type) {
135 case WHOLE:
136 case PARTIAL_LAST:
137 synchronized (requestStore) {
138 request = requestStore.remove(requestId);
139 }
140 break;
141 case PARTIAL:
142 synchronized (requestStore) {
143 request = requestStore.get(requestId);
144 }
145 break;
146 default:
147 throw new InternalError();
148 }
149
150 if (request == null) {
151
152
153 if (LOGGER.isWarnEnabled()) {
154 LOGGER.warn("Unknown request ID '" + requestId + "' for the response message. Timed out already?: "
155 + message);
156 }
157 } else {
158
159
160 if (type != ResponseType.PARTIAL) {
161 ScheduledFuture<?> scheduledFuture = request.getTimeoutFuture();
162 if (scheduledFuture != null) {
163 scheduledFuture.cancel(false);
164 Set<Request> unrespondedRequests = getUnrespondedRequestStore(session);
165 synchronized (unrespondedRequests) {
166 unrespondedRequests.remove(request);
167 }
168 }
169 }
170
171
172 Response response = new Response(request, message, type);
173 request.signal(response);
174 nextFilter.messageReceived(session, response);
175 }
176 }
177
178 @Override
179 protected Object doFilterWrite(final NextFilter nextFilter, IoSession session, WriteRequest writeRequest)
180 throws Exception {
181 Object message = writeRequest.getMessage();
182 if (!(message instanceof Request)) {
183 return null;
184 }
185
186 final Request request = (Request) message;
187 if (request.getTimeoutFuture() != null) {
188 throw new IllegalArgumentException("Request can not be reused.");
189 }
190
191 Map<Object, Request> requestStore = getRequestStore(session);
192 Object oldValue = null;
193 Object requestId = request.getId();
194 synchronized (requestStore) {
195 oldValue = requestStore.get(requestId);
196 if (oldValue == null) {
197 requestStore.put(requestId, request);
198 }
199 }
200 if (oldValue != null) {
201 throw new IllegalStateException("Duplicate request ID: " + request.getId());
202 }
203
204
205 TimeoutTask timeoutTask = new TimeoutTask(nextFilter, request, session);
206 ScheduledFuture<?> timeoutFuture = timeoutScheduler.schedule(timeoutTask, request.getTimeoutMillis(),
207 TimeUnit.MILLISECONDS);
208 request.setTimeoutTask(timeoutTask);
209 request.setTimeoutFuture(timeoutFuture);
210
211
212 Set<Request> unrespondedRequests = getUnrespondedRequestStore(session);
213 synchronized (unrespondedRequests) {
214 unrespondedRequests.add(request);
215 }
216
217 return request.getMessage();
218 }
219
220 @Override
221 public void sessionClosed(NextFilter nextFilter, IoSession session) throws Exception {
222
223
224 Set<Request> unrespondedRequests = getUnrespondedRequestStore(session);
225 List<Request> unrespondedRequestsCopy;
226 synchronized (unrespondedRequests) {
227 unrespondedRequestsCopy = new ArrayList<Request>(unrespondedRequests);
228 unrespondedRequests.clear();
229 }
230
231
232 for (Request r : unrespondedRequestsCopy) {
233 if (r.getTimeoutFuture().cancel(false)) {
234 r.getTimeoutTask().run();
235 }
236 }
237
238
239 Map<Object, Request> requestStore = getRequestStore(session);
240 synchronized (requestStore) {
241 requestStore.clear();
242 }
243
244
245 nextFilter.sessionClosed(session);
246 }
247
248 @SuppressWarnings("unchecked")
249 private Map<Object, Request> getRequestStore(IoSession session) {
250 return (Map<Object, Request>) session.getAttribute(REQUEST_STORE);
251 }
252
253 @SuppressWarnings("unchecked")
254 private Set<Request> getUnrespondedRequestStore(IoSession session) {
255 return (Set<Request>) session.getAttribute(UNRESPONDED_REQUEST_STORE);
256 }
257
258
259
260
261
262
263
264 protected Map<Object, Request> createRequestStore(IoSession session) {
265 return new ConcurrentHashMap<Object, Request>();
266 }
267
268
269
270
271
272
273
274
275
276
277
278
279
280 protected Set<Request> createUnrespondedRequestStore(IoSession session) {
281 return new LinkedHashSet<Request>();
282 }
283
284
285
286
287
288
289
290
291 protected void destroyRequestStore(Map<Object, Request> requestStore) {
292
293 }
294
295
296
297
298
299
300
301
302 protected void destroyUnrespondedRequestStore(Set<Request> unrespondedRequestStore) {
303
304 }
305
306 private class TimeoutTask implements Runnable {
307 private final NextFilter filter;
308
309 private final Request request;
310
311 private final IoSession session;
312
313 private TimeoutTask(NextFilter filter, Request request, IoSession session) {
314 this.filter = filter;
315 this.request = request;
316 this.session = session;
317 }
318
319 public void run() {
320 Set<Request> unrespondedRequests = getUnrespondedRequestStore(session);
321 if (unrespondedRequests != null) {
322 synchronized (unrespondedRequests) {
323 unrespondedRequests.remove(request);
324 }
325 }
326
327 Map<Object, Request> requestStore = getRequestStore(session);
328 Object requestId = request.getId();
329 boolean timedOut;
330 synchronized (requestStore) {
331 if (requestStore.get(requestId) == request) {
332 requestStore.remove(requestId);
333 timedOut = true;
334 } else {
335 timedOut = false;
336 }
337 }
338
339 if (timedOut) {
340
341 RequestTimeoutException e = new RequestTimeoutException(request);
342 request.signal(e);
343 filter.exceptionCaught(session, e);
344 }
345 }
346 }
347 }