001: package test.tck.msgflow.callflows.forkedinvite;
002:
003: import java.util.ArrayList;
004: import java.util.HashSet;
005:
006: import javax.sip.ClientTransaction;
007: import javax.sip.Dialog;
008: import javax.sip.DialogState;
009: import javax.sip.DialogTerminatedEvent;
010: import javax.sip.IOExceptionEvent;
011: import javax.sip.ListeningPoint;
012: import javax.sip.RequestEvent;
013: import javax.sip.ResponseEvent;
014: import javax.sip.ServerTransaction;
015: import javax.sip.SipListener;
016: import javax.sip.SipProvider;
017: import javax.sip.TransactionTerminatedEvent;
018: import javax.sip.address.Address;
019: import javax.sip.address.SipURI;
020: import javax.sip.header.CSeqHeader;
021: import javax.sip.header.CallIdHeader;
022: import javax.sip.header.ContactHeader;
023: import javax.sip.header.ContentTypeHeader;
024: import javax.sip.header.FromHeader;
025: import javax.sip.header.Header;
026: import javax.sip.header.MaxForwardsHeader;
027: import javax.sip.header.RouteHeader;
028: import javax.sip.header.ToHeader;
029: import javax.sip.header.ViaHeader;
030: import javax.sip.message.Request;
031: import javax.sip.message.Response;
032:
033: import org.apache.log4j.Logger;
034:
035: import test.tck.TestHarness;
036: import test.tck.msgflow.callflows.ProtocolObjects;
037:
038: /**
039: * This class is a UAC template. Shootist is the guy that shoots and shootme is
040: * the guy that gets shot.
041: *
042: * @author M. Ranganathan
043: */
044:
045: public class Shootist implements SipListener {
046:
047: private ContactHeader contactHeader;
048:
049: private ClientTransaction inviteTid;
050:
051: private SipProvider sipProvider;
052:
053: private String host = "127.0.0.1";
054:
055: private int port;
056:
057: private String peerHost = "127.0.0.1";
058:
059: private int peerPort;
060:
061: private ListeningPoint listeningPoint;
062:
063: private static String unexpectedException = "Unexpected exception ";
064:
065: private static Logger logger = Logger.getLogger(Shootist.class);
066:
067: private ProtocolObjects protocolObjects;
068:
069: private Dialog originalDialog;
070:
071: private HashSet forkedDialogs;
072:
073: private Dialog ackedDialog;
074:
075: private Shootist() {
076: this .forkedDialogs = new HashSet();
077: }
078:
079: public Shootist(int myPort, int proxyPort,
080: ProtocolObjects protocolObjects) {
081: this ();
082: this .protocolObjects = protocolObjects;
083: this .port = myPort;
084: this .peerPort = proxyPort;
085:
086: protocolObjects.logLevel = 32; // JvB
087: }
088:
089: public void processRequest(RequestEvent requestReceivedEvent) {
090: Request request = requestReceivedEvent.getRequest();
091: ServerTransaction serverTransactionId = requestReceivedEvent
092: .getServerTransaction();
093:
094: logger.info("\n\nRequest " + request.getMethod()
095: + " received at "
096: + protocolObjects.sipStack.getStackName()
097: + " with server transaction id " + serverTransactionId);
098:
099: // We are the UAC so the only request we get is the BYE.
100: if (request.getMethod().equals(Request.BYE))
101: processBye(request, serverTransactionId);
102: else
103: TestHarness.fail("Unexpected request ! : " + request);
104:
105: }
106:
107: public void processBye(Request request,
108: ServerTransaction serverTransactionId) {
109: try {
110: logger.info("shootist: got a bye .");
111: if (serverTransactionId == null) {
112: logger.info("shootist: null TID.");
113: return;
114: }
115: Dialog dialog = serverTransactionId.getDialog();
116: logger.info("Dialog State = " + dialog.getState());
117: Response response = protocolObjects.messageFactory
118: .createResponse(200, request);
119: serverTransactionId.sendResponse(response);
120: logger.info("shootist: Sending OK.");
121: logger.info("Dialog State = " + dialog.getState());
122:
123: } catch (Exception ex) {
124: ex.printStackTrace();
125: System.exit(0);
126:
127: }
128: }
129:
130: public synchronized void processResponse(
131: ResponseEvent responseReceivedEvent) {
132: logger.info("Got a response");
133: Response response = (Response) responseReceivedEvent
134: .getResponse();
135: ClientTransaction tid = responseReceivedEvent
136: .getClientTransaction();
137: CSeqHeader cseq = (CSeqHeader) response
138: .getHeader(CSeqHeader.NAME);
139:
140: logger.info("Response received : Status Code = "
141: + response.getStatusCode() + " " + cseq);
142: logger.info("Response = " + response + " class="
143: + response.getClass());
144:
145: Dialog dialog = responseReceivedEvent.getDialog();
146: TestHarness.assertNotNull(dialog);
147:
148: if (tid != null)
149: logger.info("transaction state is " + tid.getState());
150: else
151: logger.info("transaction = " + tid);
152:
153: logger.info("Dialog = " + dialog);
154:
155: logger.info("Dialog state is " + dialog.getState());
156:
157: try {
158: if (response.getStatusCode() == Response.OK) {
159: if (cseq.getMethod().equals(Request.INVITE)) {
160: TestHarness.assertEquals(DialogState.CONFIRMED,
161: dialog.getState());
162: Request ackRequest = dialog.createAck(cseq
163: .getSeqNumber());
164:
165: TestHarness.assertNotNull(ackRequest
166: .getHeader(MaxForwardsHeader.NAME));
167:
168: if (dialog == this .ackedDialog) {
169: dialog.sendAck(ackRequest);
170: return;
171: }
172: // Proxy will fork. I will accept the second dialog
173: // but not the first.
174: this .forkedDialogs.add(dialog);
175: if (forkedDialogs.size() == 2) {
176: logger.info("Sending ACK");
177: dialog.sendAck(ackRequest);
178: TestHarness
179: .assertTrue(
180: "Dialog state should be CONFIRMED",
181: dialog.getState() == DialogState.CONFIRMED);
182: this .ackedDialog = dialog;
183:
184: // TestHarness.assertNotNull( "JvB: Need CT to find original dialog", tid );
185:
186: } else {
187:
188: // Kill the first dialog by sending a bye.
189: SipProvider sipProvider = (SipProvider) responseReceivedEvent
190: .getSource();
191: Request byeRequest = dialog
192: .createRequest(Request.BYE);
193: ClientTransaction ct = sipProvider
194: .getNewClientTransaction(byeRequest);
195: dialog.sendRequest(ct);
196: }
197:
198: } else {
199: logger
200: .info("Response method = "
201: + cseq.getMethod());
202: }
203: } else if (response.getStatusCode() == Response.RINGING) {
204: //TestHarness.assertEquals( DialogState.EARLY, dialog.getState() );
205: }
206: } catch (Throwable ex) {
207: ex.printStackTrace();
208: // System.exit(0);
209: }
210:
211: }
212:
213: public SipProvider createSipProvider() {
214: try {
215: listeningPoint = protocolObjects.sipStack
216: .createListeningPoint(host, port,
217: protocolObjects.transport);
218:
219: logger
220: .info("listening point = " + host + " port = "
221: + port);
222: logger.info("listening point = " + listeningPoint);
223: sipProvider = protocolObjects.sipStack
224: .createSipProvider(listeningPoint);
225: return sipProvider;
226: } catch (Exception ex) {
227: logger.error(unexpectedException, ex);
228: TestHarness.fail(unexpectedException);
229: return null;
230: }
231:
232: }
233:
234: public void checkState() {
235: TestHarness.assertEquals("Should see two distinct dialogs", 2,
236: this .forkedDialogs.size());
237: TestHarness
238: .assertTrue(
239: "Should see the original (default) dialog in the forked set",
240: this .forkedDialogs
241: .contains(this .originalDialog));
242:
243: // cleanup
244: forkedDialogs.clear();
245: }
246:
247: public void processTimeout(javax.sip.TimeoutEvent timeoutEvent) {
248:
249: logger.info("Transaction Time out");
250: }
251:
252: public void sendInvite() {
253: try {
254:
255: String fromName = "BigGuy";
256: String fromSipAddress = "here.com";
257: String fromDisplayName = "The Master Blaster";
258:
259: String toSipAddress = "there.com";
260: String toUser = "LittleGuy";
261: String toDisplayName = "The Little Blister";
262:
263: // create >From Header
264: SipURI fromAddress = protocolObjects.addressFactory
265: .createSipURI(fromName, fromSipAddress);
266:
267: Address fromNameAddress = protocolObjects.addressFactory
268: .createAddress(fromAddress);
269: fromNameAddress.setDisplayName(fromDisplayName);
270: FromHeader fromHeader = protocolObjects.headerFactory
271: .createFromHeader(fromNameAddress, "12345");
272:
273: // create To Header
274: SipURI toAddress = protocolObjects.addressFactory
275: .createSipURI(toUser, toSipAddress);
276: Address toNameAddress = protocolObjects.addressFactory
277: .createAddress(toAddress);
278: toNameAddress.setDisplayName(toDisplayName);
279: ToHeader toHeader = protocolObjects.headerFactory
280: .createToHeader(toNameAddress, null);
281:
282: // create Request URI
283: String peerHostPort = peerHost + ":" + peerPort;
284: SipURI requestURI = protocolObjects.addressFactory
285: .createSipURI(toUser, peerHostPort);
286:
287: // Create ViaHeaders
288:
289: ArrayList viaHeaders = new ArrayList();
290: ViaHeader viaHeader = protocolObjects.headerFactory
291: .createViaHeader(host, sipProvider
292: .getListeningPoint(
293: protocolObjects.transport)
294: .getPort(), protocolObjects.transport, null);
295:
296: // add via headers
297: viaHeaders.add(viaHeader);
298:
299: SipURI sipuri = protocolObjects.addressFactory
300: .createSipURI(null, host);
301: sipuri.setPort(peerPort);
302: sipuri.setLrParam();
303:
304: RouteHeader routeHeader = protocolObjects.headerFactory
305: .createRouteHeader(protocolObjects.addressFactory
306: .createAddress(sipuri));
307:
308: // Create ContentTypeHeader
309: ContentTypeHeader contentTypeHeader = protocolObjects.headerFactory
310: .createContentTypeHeader("application", "sdp");
311:
312: // Create a new CallId header
313: CallIdHeader callIdHeader = sipProvider.getNewCallId();
314: // JvB: Make sure that the implementation matches the messagefactory
315: callIdHeader = protocolObjects.headerFactory
316: .createCallIdHeader(callIdHeader.getCallId());
317:
318: // Create a new Cseq header
319: CSeqHeader cSeqHeader = protocolObjects.headerFactory
320: .createCSeqHeader(1L, Request.INVITE);
321:
322: // Create a new MaxForwardsHeader
323: MaxForwardsHeader maxForwards = protocolObjects.headerFactory
324: .createMaxForwardsHeader(70);
325:
326: // Create the request.
327: Request request = protocolObjects.messageFactory
328: .createRequest(requestURI, Request.INVITE,
329: callIdHeader, cSeqHeader, fromHeader,
330: toHeader, viaHeaders, maxForwards);
331: // Create contact headers
332:
333: SipURI contactUrl = protocolObjects.addressFactory
334: .createSipURI(fromName, host);
335: contactUrl.setPort(listeningPoint.getPort());
336:
337: // Create the contact name address.
338: SipURI contactURI = protocolObjects.addressFactory
339: .createSipURI(fromName, host);
340: contactURI.setPort(sipProvider.getListeningPoint(
341: protocolObjects.transport).getPort());
342: contactURI.setTransportParam(protocolObjects.transport);
343:
344: Address contactAddress = protocolObjects.addressFactory
345: .createAddress(contactURI);
346:
347: // Add the contact address.
348: contactAddress.setDisplayName(fromName);
349:
350: contactHeader = protocolObjects.headerFactory
351: .createContactHeader(contactAddress);
352: request.addHeader(contactHeader);
353:
354: // Dont use the Outbound Proxy. Use Lr instead.
355: request.setHeader(routeHeader);
356:
357: // Add the extension header.
358: Header extensionHeader = protocolObjects.headerFactory
359: .createHeader("My-Header", "my header value");
360: request.addHeader(extensionHeader);
361:
362: String sdpData = "v=0\r\n"
363: + "o=4855 13760799956958020 13760799956958020"
364: + " IN IP4 129.6.55.78\r\n"
365: + "s=mysession session\r\n"
366: + "p=+46 8 52018010\r\n"
367: + "c=IN IP4 129.6.55.78\r\n" + "t=0 0\r\n"
368: + "m=audio 6022 RTP/AVP 0 4 18\r\n"
369: + "a=rtpmap:0 PCMU/8000\r\n"
370: + "a=rtpmap:4 G723/8000\r\n"
371: + "a=rtpmap:18 G729A/8000\r\n" + "a=ptime:20\r\n";
372: byte[] contents = sdpData.getBytes();
373:
374: request.setContent(contents, contentTypeHeader);
375:
376: extensionHeader = protocolObjects.headerFactory
377: .createHeader("My-Other-Header",
378: "my new header value ");
379: request.addHeader(extensionHeader);
380:
381: Header callInfoHeader = protocolObjects.headerFactory
382: .createHeader("Call-Info",
383: "<http://www.antd.nist.gov>");
384: request.addHeader(callInfoHeader);
385:
386: // Create the client transaction.
387: inviteTid = sipProvider.getNewClientTransaction(request);
388: Dialog dialog = inviteTid.getDialog();
389:
390: TestHarness.assertTrue(
391: "Initial dialog state should be null", dialog
392: .getState() == null);
393:
394: // send the request out.
395: inviteTid.sendRequest();
396:
397: this .originalDialog = dialog;
398: // This is not a valid test. There is a race condition in this test
399: // the response may have already come in and reset the state of the tx
400: // to proceeding.
401: // TestHarness.assertSame(
402: // "Initial transaction state should be CALLING", inviteTid
403: // .getState(), TransactionState.CALLING);
404:
405: } catch (Exception ex) {
406: logger.error(unexpectedException, ex);
407: TestHarness.fail(unexpectedException);
408:
409: }
410: }
411:
412: public void processIOException(IOExceptionEvent exceptionEvent) {
413: logger.info("IOException happened for "
414: + exceptionEvent.getHost() + " port = "
415: + exceptionEvent.getPort());
416:
417: }
418:
419: public void processTransactionTerminated(
420: TransactionTerminatedEvent transactionTerminatedEvent) {
421: logger.info("Transaction terminated event recieved");
422: }
423:
424: public void processDialogTerminated(
425: DialogTerminatedEvent dialogTerminatedEvent) {
426: logger.info("dialogTerminatedEvent");
427:
428: }
429: }
|