1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package net.sf.beep4j.internal;
17
18 import java.io.IOException;
19 import java.io.Writer;
20 import java.util.Arrays;
21
22 import net.sf.beep4j.Channel;
23 import net.sf.beep4j.ChannelHandler;
24 import net.sf.beep4j.CloseChannelCallback;
25 import net.sf.beep4j.CloseChannelRequest;
26 import net.sf.beep4j.Message;
27 import net.sf.beep4j.MessageBuilder;
28 import net.sf.beep4j.ProfileInfo;
29 import net.sf.beep4j.ProtocolException;
30 import net.sf.beep4j.ReplyHandler;
31 import net.sf.beep4j.Reply;
32 import net.sf.beep4j.SessionHandler;
33 import net.sf.beep4j.StartChannelRequest;
34 import net.sf.beep4j.StartSessionRequest;
35 import net.sf.beep4j.internal.message.DefaultMessageBuilder;
36 import net.sf.beep4j.internal.profile.ChannelManagementMessageBuilder;
37 import net.sf.beep4j.internal.profile.SaxMessageBuilder;
38
39 import org.jmock.Mock;
40 import org.jmock.MockObjectTestCase;
41 import org.jmock.core.Invocation;
42 import org.jmock.core.Stub;
43
44 public class FunctionalSessionTest extends MockObjectTestCase {
45
46 private static final String PROFILE = "http://www.example.com/profiles/echo";
47
48 private Mock sessionHandlerMock;
49
50 private SessionHandler sessionHandler;
51
52 private Mock transportMappingMock;
53
54 private TransportMapping transportMapping;
55
56 @Override
57 protected void setUp() throws Exception {
58 sessionHandlerMock = mock(SessionHandler.class);
59 sessionHandler = (SessionHandler) sessionHandlerMock.proxy();
60 transportMappingMock = mock(TransportMapping.class);
61 transportMapping = (TransportMapping) transportMappingMock.proxy();
62 setupTransportMapping();
63 }
64
65 private void setupTransportMapping() {
66 transportMappingMock.expects(once()).method("channelStarted").with(eq(0));
67 }
68
69
70
71
72 public void testRejectSessionStart() throws Exception {
73 Stub rejector = new StartSessionRequestRejector();
74
75 sessionHandlerMock.expects(once()).method("connectionEstablished").will(rejector);
76 transportMappingMock.expects(once()).method("sendERR").with(eq(0), eq(0), eq(createErrorMessage(421, "service not available")));
77 transportMappingMock.expects(once()).method("closeTransport");
78
79 SessionImpl session = new SessionImpl(false, sessionHandler, transportMapping);
80 session.connectionEstablished(null);
81 }
82
83
84
85
86 public void testSessionStartRejected() throws Exception {
87 Stub acceptor = new StartSessionRequestAcceptor(new String[] { PROFILE });
88
89 sessionHandlerMock.expects(once()).method("connectionEstablished").will(acceptor);
90 sessionHandlerMock.expects(once()).method("sessionStartDeclined").with(eq(550), eq("listener not available"));
91 transportMappingMock.expects(once()).method("sendRPY")
92 .with(eq(0), eq(0), eq(createGreetingMessage(new String[] { PROFILE })));
93 transportMappingMock.expects(once()).method("closeTransport");
94
95 SessionImpl session = new SessionImpl(true, sessionHandler, transportMapping);
96 session.connectionEstablished(null);
97 session.receiveERR(0, 0, createErrorMessage(550, "listener not available"));
98 }
99
100
101
102
103
104
105
106
107
108 public void testNormalOperationChannelStartedByLocalPeer() throws Exception {
109 SessionImpl session = openSession(true, new String[0], new String[] { PROFILE });
110
111 ChannelStruct channel = startChannel(1, 1, new ProfileInfo[] { new ProfileInfo(PROFILE) }, session);
112
113 sendAndReceiveEcho(session, channel.channel, 1, 1, "abcdefghijklmnopqrstuvwxyz");
114 sendAndReceiveEcho(session, channel.channel, 1, 2, "abcdefghijk");
115
116 closeChannel(session, channel, 1, 2);
117 closeSession(session, 3);
118 }
119
120
121
122
123
124
125
126
127
128 public void testNormalOperationChannelStartedByRemotePeer() throws Exception {
129 SessionImpl session = openSession(false, new String[] { PROFILE }, new String[0]);
130
131 ChannelStruct channel = startChannelRequested(1, 1, new ProfileInfo[] { new ProfileInfo(PROFILE) }, session);
132
133 receiveAndReplyEcho(session, channel, 1, 1, "abcdefghijklmnopqrstuvwxyz");
134 receiveAndReplyEcho(session, channel, 1, 2, "abcdefghijk");
135
136 closeChannelRequested(session, channel, 1, 2);
137 closeSessionRequested(session, 3);
138 }
139
140
141
142
143
144
145
146
147
148
149
150 public void testRejectSessionAndChannelCloseAndCloseTransport() throws Exception {
151 SessionImpl session = openSession(false, new String[] { PROFILE }, new String[0]);
152
153 ChannelStruct channel = startChannelRequested(1, 1, new ProfileInfo[] { new ProfileInfo(PROFILE) }, session);
154
155 receiveAndReplyEcho(session, channel, 1, 1, "abcdefghijklmnopqrstuvwxyz");
156 closeChannelRejected(session, channel, 1, 1);
157 receiveAndReplyEcho(session, channel, 1, 2, "abcdefghijk");
158
159 closeSessionRequestedReject(session, 2);
160
161 connectionClosed(session);
162 }
163
164
165
166
167
168
169
170
171
172 public void testAbortSessionWhenReceivingOutOfSequenceResponse() throws Exception {
173 SessionImpl session = openSession(false, new String[] { PROFILE }, new String[0]);
174
175 ChannelStruct channel = startChannelRequested(1, 1, new ProfileInfo[] { new ProfileInfo(PROFILE) }, session);
176
177 sendEcho(channel.channel, 1, 1, "abcdefghijklmnopqrstuvwxyz");
178 sendEcho(channel.channel, 1, 2, "abcdefghijk");
179
180 try {
181 session.receiveRPY(1, 2, createEchoMessage("abcdefghijk"));
182 fail("expects receiving message 1 on channel 1, must result in ProtocolException");
183 } catch (ProtocolException e) {
184
185 }
186 }
187
188
189
190
191
192
193
194
195
196
197 public void testReceiveCloseChannelRequestWhenCloseAlreadyInitiated() throws Exception {
198 SessionImpl session = openSession(false, new String[] { PROFILE }, new String[0]);
199 ChannelStruct channel = startChannelRequested(1, 1, new ProfileInfo[] { new ProfileInfo(PROFILE) }, session);
200
201 Mock callbackMock = mock(CloseChannelCallback.class);
202 CloseChannelCallback callback = (CloseChannelCallback) callbackMock.proxy();
203 requestChannelClose(transportMappingMock, channel.channel, callback, 1, 1);
204
205
206 Message request = createCloseMessage(1);
207 Message reply = createOkMessage();
208
209 callbackMock.expects(once()).method("closeAccepted");
210 channel.channelHandlerMock.expects(once()).method("channelClosed");
211 transportMappingMock.expects(once()).method("sendRPY")
212 .with(eq(0), eq(1), eq(reply));
213 transportMappingMock.expects(once()).method("channelClosed")
214 .with(eq(1));
215 session.receiveMSG(0, 1, request);
216 }
217
218
219
220
221
222
223
224 public void testLocalPeerRejectsChannelStart() throws Exception {
225 SessionImpl session = openSession(false, new String[] { PROFILE }, new String[0]);
226 startChannelRequestedReject(session, 1, 1, new ProfileInfo[] { new ProfileInfo(PROFILE) });
227 }
228
229
230
231
232
233
234
235 public void testRemotePeerRejectsChannelStart() throws Exception {
236 SessionImpl session = openSession(true, new String[0], new String[] { PROFILE });
237 assertTrue(Arrays.equals(new String[] { PROFILE }, session.getProfiles()));
238 startChannelRejected(session, 1, 1, new ProfileInfo[] { new ProfileInfo(PROFILE) });
239 }
240
241
242
243
244
245
246
247
248
249
250 public void testReceiveCloseSessionRequestWhenCloseAlreadyInitiated() throws Exception {
251 SessionImpl session = openSession(false, new String[] { PROFILE }, new String[0]);
252 initiateCloseSession(session, 1);
253
254 transportMappingMock.expects(once()).method("sendRPY")
255 .with(eq(0), eq(1), eq(createOkMessage()));
256 transportMappingMock.expects(once()).method("closeTransport");
257 sessionHandlerMock.expects(once()).method("sessionClosed");
258
259 session.receiveMSG(0, 1, createCloseMessage(0));
260 }
261
262 private SessionImpl openSession(boolean initiator, String[] profiles, String[] remoteProfiles) {
263 transportMappingMock.expects(once()).method("sendRPY")
264 .with(eq(0), eq(0), eq(createGreetingMessage(profiles)));
265
266 SessionImpl session = new SessionImpl(initiator, sessionHandler, transportMapping);
267
268 Stub acceptor = new StartSessionRequestAcceptor(profiles);
269
270 sessionHandlerMock.expects(once()).method("connectionEstablished").will(acceptor);
271 sessionHandlerMock.expects(once()).method("sessionOpened").with(same(session));
272
273 session.connectionEstablished(null);
274 session.receiveRPY(0, 0, createGreetingMessage(remoteProfiles));
275
276 return session;
277 }
278
279 private ChannelStruct startChannel(int channelNumber, int messageNumber, ProfileInfo[] profiles, SessionImpl session) {
280 Mock channelHandlerMock = mock(ChannelHandler.class);
281 ChannelHandler channelHandler = (ChannelHandler) channelHandlerMock.proxy();
282
283 ParameterCaptureStub<Channel> channelExtractor =
284 new ParameterCaptureStub<Channel>(0, Channel.class, null);
285 channelHandlerMock.expects(once()).method("channelOpened").with(ANYTHING).will(channelExtractor);
286
287 transportMappingMock.expects(once()).method("sendMSG")
288 .with(eq(0), eq(1), eq(createStartMessage(1, new ProfileInfo[] { new ProfileInfo(PROFILE) })));
289 transportMappingMock.expects(once()).method("channelStarted").with(eq(1));
290 session.startChannel(PROFILE, channelHandler);
291 session.receiveRPY(0, 1, createProfileMessage(new ProfileInfo(PROFILE)));
292
293 Channel channel = channelExtractor.getParameter();
294 return new ChannelStruct(channel, channelHandlerMock);
295 }
296
297 private ChannelStruct startChannelRequested(int channelNumber, int messageNumber, ProfileInfo[] profiles, SessionImpl session) {
298 Mock channelHandlerMock = mock(ChannelHandler.class);
299 ChannelHandler channelHandler = (ChannelHandler) channelHandlerMock.proxy();
300
301 ProfileInfo profile = profiles[0];
302 StartChannelRequestAcceptor acceptor = new StartChannelRequestAcceptor(profile, channelHandler);
303
304 sessionHandlerMock.expects(once()).method("channelStartRequested")
305 .with(ANYTHING)
306 .will(acceptor);
307
308 transportMappingMock.expects(once()).method("channelStarted")
309 .with(eq(channelNumber));
310 transportMappingMock.expects(once()).method("sendRPY")
311 .with(eq(0), eq(messageNumber), eq(createProfileMessage(profile)));
312
313 ParameterCaptureStub<Channel> channelExtractor =
314 new ParameterCaptureStub<Channel>(0, Channel.class, null);
315 channelHandlerMock.expects(once()).method("channelOpened").with(ANYTHING).will(channelExtractor);
316
317 session.receiveMSG(0, messageNumber, createStartMessage(channelNumber, profiles));
318
319 Channel channel = channelExtractor.getParameter();
320 return new ChannelStruct(channel, channelHandlerMock);
321 }
322
323 private void startChannelRequestedReject(SessionImpl session, int channelNumber, int messageNumber, ProfileInfo[] profiles) {
324 Stub stub = new StartChannelRequestRejector(550, "no profiles supported");
325
326 sessionHandlerMock.expects(once()).method("channelStartRequested")
327 .with(ANYTHING).will(stub);
328 transportMappingMock.expects(once()).method("sendERR")
329 .with(eq(0), eq(messageNumber), eq(createErrorMessage(550, "no profiles supported")));
330 session.receiveMSG(0, messageNumber, createStartMessage(channelNumber, profiles));
331 }
332
333 private void startChannelRejected(SessionImpl session, int channelNumber, int messageNumber, ProfileInfo[] profiles) {
334 Mock channelHandlerMock = mock(ChannelHandler.class);
335 ChannelHandler channelHandler = (ChannelHandler) channelHandlerMock.proxy();
336
337 transportMappingMock.expects(once()).method("sendMSG")
338 .with(eq(0), eq(messageNumber), eq(createStartMessage(channelNumber, profiles)));
339 channelHandlerMock.expects(once()).method("channelStartFailed")
340 .with(eq(550), eq("no profiles supported"));
341
342 session.startChannel(profiles[0], channelHandler);
343 session.receiveERR(0, messageNumber, createErrorMessage(550, "no profiles supported"));
344 }
345
346 private void closeChannel(SessionImpl session, ChannelStruct channel,
347 int channelNumber, int messageNumber) {
348 Mock closeChannelCallbackMock = mock(CloseChannelCallback.class);
349 CloseChannelCallback closeChannelCallback = (CloseChannelCallback) closeChannelCallbackMock.proxy();
350
351 transportMappingMock.expects(once()).method("sendMSG")
352 .with(eq(0), eq(messageNumber), eq(createCloseMessage(channelNumber)));
353 channel.channel.close(closeChannelCallback);
354
355 channel.channelHandlerMock.expects(once()).method("channelClosed");
356 transportMappingMock.expects(once()).method("channelClosed").with(eq(channelNumber));
357 closeChannelCallbackMock.expects(once()).method("closeAccepted");
358 session.receiveRPY(0, messageNumber, createOkMessage());
359 }
360
361 private void closeChannelRejected(SessionImpl session, ChannelStruct channel,
362 int channelNumber, int messageNumber) {
363 Mock closeChannelCallbackMock = mock(CloseChannelCallback.class);
364 CloseChannelCallback closeChannelCallback = (CloseChannelCallback) closeChannelCallbackMock.proxy();
365
366 transportMappingMock.expects(once()).method("sendMSG")
367 .with(eq(0), eq(messageNumber), eq(createCloseMessage(channelNumber)));
368 channel.channel.close(closeChannelCallback);
369
370 closeChannelCallbackMock.expects(once()).method("closeDeclined")
371 .with(eq(550), eq("still working"));
372 session.receiveERR(0, messageNumber, createErrorMessage(550, "still working"));
373 }
374
375 private void requestChannelClose(
376 Mock transportMappingMock,
377 Channel channel,
378 CloseChannelCallback callback,
379 int channelNumber,
380 int messageNumber) {
381 Message request = createCloseMessage(channelNumber);
382
383 transportMappingMock.expects(once()).method("sendMSG")
384 .with(eq(0), eq(messageNumber), eq(request));
385 channel.close(callback);
386 }
387
388 private void closeChannelRequested(SessionImpl session, ChannelStruct channel,
389 int channelNumber, int messageNumber) {
390 Stub closeChannelAcceptor = new CloseChannelAcceptor();
391
392 Message request = createCloseMessage(1);
393 Message reply = createOkMessage();
394
395 channel.channelHandlerMock.expects(once()).method("channelCloseRequested")
396 .with(ANYTHING).will(closeChannelAcceptor);
397 channel.channelHandlerMock.expects(once()).method("channelClosed");
398 transportMappingMock.expects(once()).method("sendRPY")
399 .with(eq(0), eq(messageNumber), eq(reply));
400 transportMappingMock.expects(once()).method("channelClosed")
401 .with(eq(1));
402 session.receiveMSG(0, messageNumber, request);
403 }
404
405 private void closeSession(SessionImpl session, int messageNumber) {
406 initiateCloseSession(session, messageNumber);
407 sessionHandlerMock.expects(once()).method("sessionClosed");
408 transportMappingMock.expects(once()).method("closeTransport");
409 session.receiveRPY(0, messageNumber, createOkMessage());
410 }
411
412 private void initiateCloseSession(SessionImpl session, int messageNumber) {
413 transportMappingMock.expects(once()).method("sendMSG")
414 .with(eq(0), eq(messageNumber), eq(createCloseMessage(0)));
415 session.close();
416 }
417
418 private void closeSessionRequested(SessionImpl session, int messageNumber) {
419 Message request = createCloseMessage(0);
420 transportMappingMock.expects(once()).method("sendRPY")
421 .with(eq(0), eq(messageNumber), eq(createOkMessage()));
422 transportMappingMock.expects(once()).method("closeTransport");
423 sessionHandlerMock.expects(once()).method("sessionClosed");
424 session.receiveMSG(0, messageNumber, request);
425 }
426
427 private void closeSessionRequestedReject(SessionImpl session, int messageNumber) {
428 Message request = createCloseMessage(0);
429 transportMappingMock.expects(once()).method("sendERR")
430 .with(eq(0), eq(messageNumber), eq(createErrorMessage(550, "still working")));
431 session.receiveMSG(0, messageNumber, request);
432 }
433
434 private Mock sendEcho(Channel channel, int channelNumber, int messageNumber, String content) throws IOException {
435 Mock replyListenerMock = mock(ReplyHandler.class);
436 ReplyHandler replyListener = (ReplyHandler) replyListenerMock.proxy();
437
438 Message request = createEchoMessage(content);
439 transportMappingMock.expects(once()).method("sendMSG")
440 .with(eq(channelNumber), eq(messageNumber), same(request));
441 channel.sendMessage(request, replyListener);
442
443 return replyListenerMock;
444 }
445
446 private void receiveEcho(MessageHandler messageHandler, Mock listenerMock, int channelNumber, int messageNumber, String content) throws IOException {
447 Message reply = createEchoMessage(content);
448 listenerMock.expects(once()).method("receivedRPY").with(same(reply));
449 messageHandler.receiveRPY(channelNumber, messageNumber, reply);
450 }
451
452 private void sendAndReceiveEcho(MessageHandler messageHandler, Channel channel,
453 int channelNumber, int messageNumber, String content) throws IOException {
454 Mock replyListenerMock = sendEcho(channel, channelNumber, messageNumber, content);
455 receiveEcho(messageHandler, replyListenerMock, channelNumber, messageNumber, content);
456 }
457
458 private void receiveAndReplyEcho(MessageHandler messageHandler, ChannelStruct channel,
459 int channelNumber, int messageNumber, String content) throws IOException {
460 ParameterCaptureStub<Reply> extractor =
461 new ParameterCaptureStub<Reply>(1, Reply.class, null);
462
463 Message request = createEchoMessage(content);
464 Message reply = createEchoMessage(content);
465
466 channel.channelHandlerMock.expects(once()).method("messageReceived")
467 .with(same(request), ANYTHING).will(extractor);
468
469 messageHandler.receiveMSG(channelNumber, messageNumber, request);
470
471 Reply responseHandler = extractor.getParameter();
472 transportMappingMock.expects(once()).method("sendRPY").with(eq(channelNumber), eq(messageNumber), same(reply));
473 responseHandler.sendRPY(reply);
474 }
475
476 private void connectionClosed(SessionImpl session) {
477 sessionHandlerMock.expects(once()).method("sessionClosed");
478 session.connectionClosed();
479 }
480
481 private static Message createEchoMessage(String content) throws IOException {
482 MessageBuilder messageBuilder = new DefaultMessageBuilder();
483 messageBuilder.setCharsetName("UTF-8");
484 messageBuilder.setContentType("text", "plain");
485 Writer writer = messageBuilder.getWriter();
486 writer.write(content);
487 writer.close();
488 return messageBuilder.getMessage();
489 }
490
491 private static Message createErrorMessage(int code, String message) {
492 MessageBuilder messageBuilder = new DefaultMessageBuilder();
493 messageBuilder.setCharsetName("UTF-8");
494 messageBuilder.setContentType("application", "beep+xml");
495 ChannelManagementMessageBuilder builder = new SaxMessageBuilder();
496 return builder.createError(messageBuilder, code, message);
497 }
498
499 private static Message createOkMessage() {
500 MessageBuilder messageBuilder = new DefaultMessageBuilder();
501 messageBuilder.setCharsetName("UTF-8");
502 messageBuilder.setContentType("application", "beep+xml");
503 ChannelManagementMessageBuilder builder = new SaxMessageBuilder();
504 return builder.createOk(messageBuilder);
505 }
506
507 private static Message createGreetingMessage(String[] profiles) {
508 MessageBuilder messageBuilder = new DefaultMessageBuilder();
509 messageBuilder.setCharsetName("UTF-8");
510 messageBuilder.setContentType("application", "beep+xml");
511 ChannelManagementMessageBuilder builder = new SaxMessageBuilder();
512 return builder.createGreeting(messageBuilder, profiles);
513 }
514
515 private static Message createProfileMessage(ProfileInfo profile) {
516 MessageBuilder messageBuilder = new DefaultMessageBuilder();
517 messageBuilder.setCharsetName("UTF-8");
518 messageBuilder.setContentType("application", "beep+xml");
519 ChannelManagementMessageBuilder builder = new SaxMessageBuilder();
520 return builder.createProfile(messageBuilder, profile);
521 }
522
523 private static Message createStartMessage(int channelNumber, ProfileInfo[] profiles) {
524 MessageBuilder messageBuilder = new DefaultMessageBuilder();
525 messageBuilder.setCharsetName("UTF-8");
526 messageBuilder.setContentType("application", "beep+xml");
527 ChannelManagementMessageBuilder builder = new SaxMessageBuilder();
528 return builder.createStart(messageBuilder, channelNumber, profiles);
529 }
530
531 private static Message createCloseMessage(int channelNumber) {
532 MessageBuilder messageBuilder = new DefaultMessageBuilder();
533 messageBuilder.setCharsetName("UTF-8");
534 messageBuilder.setContentType("application", "beep+xml");
535 ChannelManagementMessageBuilder builder = new SaxMessageBuilder();
536 return builder.createClose(messageBuilder, channelNumber, 200);
537 }
538
539 private static class ChannelStruct {
540 private final Mock channelHandlerMock;
541 private final Channel channel;
542 private ChannelStruct(Channel channel, Mock channelHandlerMock) {
543 this.channel = channel;
544 this.channelHandlerMock = channelHandlerMock;
545 }
546 }
547
548 private static class StartSessionRequestRejector implements Stub {
549 public StringBuffer describeTo(StringBuffer buffer) {
550 return buffer.append("rejects session start");
551 }
552 public Object invoke(Invocation invocation) throws Throwable {
553 StartSessionRequest request = (StartSessionRequest) invocation.parameterValues.get(0);
554 request.cancel();
555 return null;
556 }
557 }
558
559 private static class StartSessionRequestAcceptor implements Stub {
560 private final String[] profileUris;
561 public StartSessionRequestAcceptor(String[] profileUris) {
562 this.profileUris = profileUris;
563 }
564 public StringBuffer describeTo(StringBuffer buffer) {
565 return buffer.append("accepts session start");
566 }
567 public Object invoke(Invocation invocation) throws Throwable {
568 StartSessionRequest request = (StartSessionRequest) invocation.parameterValues.get(0);
569 for (int i = 0; i < profileUris.length; i++) {
570 request.registerProfile(profileUris[i]);
571 }
572 return null;
573 }
574 }
575
576 private static class StartChannelRequestAcceptor implements Stub {
577 private final ProfileInfo profile;
578 private final ChannelHandler channelHandler;
579 private StartChannelRequestAcceptor(ProfileInfo profile, ChannelHandler channelHandler) {
580 this.profile = profile;
581 this.channelHandler = channelHandler;
582 }
583 public StringBuffer describeTo(StringBuffer buffer) {
584 return buffer.append("accepts channel start");
585 }
586 public Object invoke(Invocation invocation) throws Throwable {
587 StartChannelRequest request = (StartChannelRequest) invocation.parameterValues.get(0);
588 request.selectProfile(profile, channelHandler);
589 return null;
590 }
591 }
592
593 private static class StartChannelRequestRejector implements Stub {
594 private final int code;
595 private final String message;
596 private StartChannelRequestRejector(int code, String message) {
597 this.code = code;
598 this.message = message;
599 }
600 public StringBuffer describeTo(StringBuffer buffer) {
601 return buffer.append("rejects channel start");
602 }
603 public Object invoke(Invocation invocation) throws Throwable {
604 StartChannelRequest request = (StartChannelRequest) invocation.parameterValues.get(0);
605 request.cancel(code, message);
606 return null;
607 }
608 }
609
610 private static class CloseChannelAcceptor implements Stub {
611 public StringBuffer describeTo(StringBuffer buffer) {
612 return buffer.append("accepts close channel");
613 }
614 public Object invoke(Invocation invocation) throws Throwable {
615 CloseChannelRequest callback = (CloseChannelRequest) invocation.parameterValues.get(0);
616 callback.accept();
617 return null;
618 }
619 }
620
621 }