1   /*
2    *  Copyright 2006 Simon Raess
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
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  	 * Scenario: session accepted, rejected by local peer
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  	 * Scenario: session initiated, rejected by remote peer.
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 	 * Test Scenario 1: open session, start channel, send message, close channel and session
102 	 * - open session
103 	 * - initiator successfully initiates channel
104 	 * - send message and receive response (twice)
105 	 * - close channel
106 	 * - close session
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 	 * Test Scenario 2: open session, start channel, send message, close channel and session
122 	 * - open session (listener)
123 	 * - remote peer (initiator) successfully initiate channel
124 	 * - receive message and send response (twice)
125 	 * - close channel requested and accepted
126 	 * - close session requested and accepted
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 	 * Test Scenario 3: reject session/channel close and closing transport
142 	 * - open session (listener)
143 	 * - remote peer (initiator) successfully initiates channel
144 	 * - receive message and send response
145 	 * - request close channel, rejected
146 	 * - receive message and send response
147 	 * - close session requested, rejected (there are still open channels)
148 	 * - drop connection
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 	 * Test Scenario 4: abort session when receiving out of sequence response
166 	 * - open session (listener)
167 	 * - remote peer (initiator) successfully initiates channel
168 	 * - send message 1
169 	 * - send message 2
170 	 * - receive reply to message 2 (results in ProtocolException)
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 			// expected
185 		}
186 	}
187 	
188 	/*
189 	 * Test Scenario 5: receive close channel request when close already initiated
190 	 * - open session (listener)
191 	 * - remote peer (initiator) successfully initiates channel
192 	 * - local peer initiates channel close
193 	 * - receive close reqest from remote peer
194 	 * -> results in immediate ok message
195 	 * -> results in callback from initial request beeing called back
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 		// close requested and immediately accepted without calling back the application
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 	 * Test Scenario 6: local peer rejects a channel start request
220 	 * - open session (listener)
221 	 * - remote peer (initiator) tries to initiate channel
222 	 * - local peer rejects channel creation
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 	 * Test Scenario 7: remote peer rejects a channel start request
231 	 * - open session (initiator)
232 	 * - local peer tries to start channel
233 	 * - remote peer rejects channel creation
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 	 * Test Scenario 8: receive close session request when close session already initiated
243 	 * - open session
244 	 * - local peer tries to close session
245 	 * - receive close session request from remote peer
246 	 * -> immediate ok message
247 	 * -> close transport
248 	 * -> callback? notified
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 }