001: /*
002: * Conditions Of Use
003: *
004: * This software was developed by employees of the National Institute of
005: * Standards and Technology (NIST), and others.
006: * This software is has been contributed to the public domain.
007: * As a result, a formal license is not needed to use the software.
008: *
009: * This software is provided "AS IS."
010: * NIST MAKES NO WARRANTY OF ANY KIND, EXPRESS, IMPLIED
011: * OR STATUTORY, INCLUDING, WITHOUT LIMITATION, THE IMPLIED WARRANTY OF
012: * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, NON-INFRINGEMENT
013: * AND DATA ACCURACY. NIST does not warrant or make any representations
014: * regarding the use of the software or the results thereof, including but
015: * not limited to the correctness, accuracy, reliability or usefulness of
016: * the software.
017: *
018: *
019: */
020: package test.tck.msgflow;
021:
022: import gov.nist.javax.sip.SipStackImpl;
023:
024: import java.text.ParseException;
025: import java.util.LinkedList;
026: import java.util.List;
027:
028: import javax.sip.ListeningPoint;
029: import javax.sip.ObjectInUseException;
030: import javax.sip.SipProvider;
031: import javax.sip.SipStack;
032: import javax.sip.address.Address;
033: import javax.sip.address.AddressFactory;
034: import javax.sip.address.SipURI;
035: import javax.sip.header.CSeqHeader;
036: import javax.sip.header.CallIdHeader;
037: import javax.sip.header.ContactHeader;
038: import javax.sip.header.ContentTypeHeader;
039: import javax.sip.header.FromHeader;
040: import javax.sip.header.Header;
041: import javax.sip.header.HeaderFactory;
042: import javax.sip.header.MaxForwardsHeader;
043: import javax.sip.header.ToHeader;
044: import javax.sip.header.ViaHeader;
045: import javax.sip.message.MessageFactory;
046: import javax.sip.message.Request;
047: import javax.sip.message.Response;
048:
049: import test.tck.TckInternalError;
050: import test.tck.TestHarness;
051: import test.tck.TiUnexpectedError;
052:
053: /**
054: * <p>
055: * Title: TCK
056: * </p>
057: * <p>
058: * Description: JAIN SIP 1.1 Technology Compatibility Kit
059: * </p>
060: *
061: * @author Emil Ivov Network Research Team, Louis Pasteur University,
062: * Strasbourg, France
063: * @author Ivelin Ivanov
064: *
065: * @version 1.0
066: */
067:
068: public class MessageFlowHarness extends TestHarness {
069: protected static final String EXTENSION_HDR = "Status-Extension";
070:
071: protected static int counter;
072:
073: // timeout values depend on pc, mine is not that powerful :)
074: protected static long MESSAGES_ARRIVE_FOR = 2500;
075:
076: // it is really important to delete as a failure messes up following tests
077: // so let's try real hard - 10 is a good number
078: protected static int RETRY_OBJECT_DELETES = 10;
079:
080: protected static long RETRY_OBJECT_DELETES_AFTER = 500;
081:
082: protected static long STACKS_START_FOR = 1000;
083:
084: protected static long STACKS_SHUT_DOWN_FOR = 500;
085:
086: protected static long TRANSACTION_TIMES_OUT_FOR = 38000;
087:
088: protected ListeningPoint riListeningPoint = null;
089:
090: protected ListeningPoint tiListeningPoint = null;
091:
092: protected SipProvider riSipProvider = null;
093:
094: protected SipProvider tiSipProvider = null;
095:
096: protected SipEventCollector eventCollector = new SipEventCollector();
097:
098: protected SipStack riSipStack;
099:
100: protected SipStack tiSipStack;
101:
102: public MessageFlowHarness(String name) {
103: this (name, true);
104: }
105:
106: protected MessageFlowHarness(String name, boolean autoDialog) {
107: super (name, autoDialog);
108: System.out.println("Initializing test " + name);
109:
110: try {
111: if (riFactory != null)
112: riFactory.resetFactory();
113:
114: riSipStack = riFactory
115: .createSipStack(getRiProperties(autoDialog));
116: assertTrue("RI must be gov.nist",
117: riSipStack instanceof SipStackImpl);
118:
119: tiFactory.resetFactory();
120: tiFactory.setPathName(getImplementationPath());
121: tiSipStack = tiFactory.createSipStack(getTiProperties());
122: if (riSipStack == tiSipStack) {
123: throw new TckInternalError(
124: "riSipStack should not the same as tiSipStack");
125: }
126: } catch (TckInternalError ex) {
127: throw ex;
128: } catch (Exception ex) {
129: fail("initialization failed");
130: }
131: }
132:
133: // issue 17 on dev.java.net specify the headerFactory to use
134: // report and fix thereof larryb@dev.java.net
135: protected void addStatus(HeaderFactory headerFactory,
136: Request request) {
137: try {
138: Header extension = headerFactory.createHeader(
139: EXTENSION_HDR, new Integer(counter++).toString());
140: request.addHeader(extension);
141: } catch (ParseException ex) {
142: // do nothing
143: }
144: }
145:
146: protected void addStatus(Request request, Response response) {
147: Header extension = request.getHeader(EXTENSION_HDR);
148: if (extension != null)
149: response.addHeader(extension);
150: }
151:
152: /**
153: * Initialises both RI and TI sip stacks and stack factories.
154: *
155: * @throws java.lang.Exception
156: * All Let all exceptions that come from the underlying stack to
157: * pass through and surface at JUnit Level.
158: */
159: public void setUp() throws java.lang.Exception {
160:
161: riListeningPoint = riSipStack.createListeningPoint(
162: LOCAL_ADDRESS, RI_PORT, "udp");
163: riSipProvider = riSipStack.createSipProvider(riListeningPoint);
164:
165: tiListeningPoint = tiSipStack.createListeningPoint(
166: LOCAL_ADDRESS, TI_PORT, "udp");
167: tiSipProvider = tiSipStack.createSipProvider(tiListeningPoint);
168:
169: // If we don't wait for them to start first messages get lost and are
170: // therefore reported as test failures.
171: sleep(STACKS_START_FOR);
172: }
173:
174: /**
175: * Sets all JAIN SIP objects to null and resets the SipFactory.
176: *
177: * @throws java.lang.Exception
178: */
179: public void tearDown() throws java.lang.Exception {
180:
181: // Delete RI SipProvider
182: int tries = 0;
183: for (tries = 0; tries < RETRY_OBJECT_DELETES; tries++) {
184: try {
185: riSipStack.deleteSipProvider(riSipProvider);
186: } catch (ObjectInUseException ex) {
187: // System.err.println("Retrying delete of riSipProvider!");
188: sleep(RETRY_OBJECT_DELETES_AFTER);
189: continue;
190: }
191: break;
192: }
193: if (tries >= RETRY_OBJECT_DELETES)
194: throw new TckInternalError(
195: "Failed to delete riSipProvider!");
196:
197: // Delete RI ListeningPoint
198: for (tries = 0; tries < RETRY_OBJECT_DELETES; tries++) {
199: try {
200: riSipStack.deleteListeningPoint(riListeningPoint);
201: } catch (ObjectInUseException ex) {
202: // System.err.println("Retrying delete of riListeningPoint!");
203: sleep(RETRY_OBJECT_DELETES_AFTER);
204: continue;
205: }
206: break;
207: }
208: if (tries >= RETRY_OBJECT_DELETES)
209: throw new TckInternalError(
210: "Failed to delete riListeningPoint!");
211:
212: riSipProvider = null;
213: riListeningPoint = null;
214:
215: // Delete TI SipProvider
216: for (tries = 0; tries < RETRY_OBJECT_DELETES; tries++) {
217: try {
218: tiSipStack.deleteSipProvider(tiSipProvider);
219: } catch (ObjectInUseException ex) {
220: // System.err.println("Retrying delete of tiSipProvider!");
221: sleep(RETRY_OBJECT_DELETES_AFTER);
222: continue;
223: }
224: break;
225: }
226: if (tries >= RETRY_OBJECT_DELETES)
227: throw new TiUnexpectedError(
228: "Failed to delete tiSipProvider!");
229:
230: // Delete TI ListeningPoint
231: for (tries = 0; tries < RETRY_OBJECT_DELETES; tries++) {
232: try {
233: tiSipStack.deleteListeningPoint(tiListeningPoint);
234: } catch (ObjectInUseException ex) {
235: // System.err.println("Retrying delete of tiListeningPoint!");
236: sleep(RETRY_OBJECT_DELETES_AFTER);
237: continue;
238: }
239: break;
240: }
241:
242: if (tries >= RETRY_OBJECT_DELETES)
243: throw new TiUnexpectedError(
244: "Failed to delete tiListeningPoint!");
245: riSipStack.stop();
246:
247: tiSipStack.stop();
248:
249: tiSipProvider = null;
250: tiListeningPoint = null;
251:
252: // Wait for stack threads to release resources (e.g. port)
253: sleep(STACKS_SHUT_DOWN_FOR);
254: }
255:
256: // ========================= Utility Methods =========================
257: /**
258: * Creates a SipRequest using the specified factories. The request has the
259: * specified method and is meant to be sent from srcProvider to dstProvider.
260: * This method is prefered to manual creation of requests as it helps avoid
261: * using RI objects instead of corresponding TI objects (or vice versa).
262: *
263: * @param method
264: * the request's method
265: * @param addressFactory
266: * the address factory to use when creating addresses
267: * @param headerFactory
268: * the header factory to use when creating headers
269: * @param messageFactory
270: * the message factory to use when creating headers
271: * @param srcProvider
272: * the provider that will eventually be used to send the request
273: * @param dstProvider
274: * the provider that will eventually dispatch the request to a
275: * SipListener
276: * @param contentType
277: * if the content parameter is not null then this is its content
278: * type.
279: * @param contentSubType
280: * if the content parameter is not null then this is its sub
281: * content type.
282: * @param content
283: * the content of the request. if null this parameter is ignored
284: * @return a request generated by the specified factories and destined to go
285: * from srcProvider to dstProvider
286: * @throws Exception
287: * if anything should go wrong. further exception handling is
288: * left to calling methods (or JUnit).
289: */
290: protected Request createRequest(String method,
291: AddressFactory addressFactory, HeaderFactory headerFactory,
292: MessageFactory messageFactory, SipProvider srcProvider,
293: SipProvider dstProvider, String contentType,
294: String contentSubType, Object content) throws Exception {
295: // Source SipUri
296: ListeningPoint srclp = srcProvider.getListeningPoints()[0];
297: SipURI srcSipURI = addressFactory.createSipURI(null, srclp
298: .getIPAddress());
299: srcSipURI.setPort(srclp.getPort());
300: srcSipURI.setTransportParam(srclp.getTransport());
301:
302: // Destination SipURI
303: ListeningPoint dstlp = dstProvider.getListeningPoints()[0];
304: SipURI dstSipURI = addressFactory.createSipURI(null, dstlp
305: .getIPAddress());
306: dstSipURI.setPort(dstlp.getPort());
307: dstSipURI.setTransportParam(dstlp.getTransport());
308: // CallId
309: CallIdHeader callId = srcProvider.getNewCallId();
310: callId = headerFactory.createCallIdHeader(callId.getCallId());
311:
312: // CSeq
313: CSeqHeader cSeq = headerFactory.createCSeqHeader(1L, method);
314:
315: // From
316: Address fromAddress = addressFactory.createAddress(srcSipURI);
317:
318: FromHeader from = headerFactory.createFromHeader(fromAddress,
319: Integer.toString(srcProvider.hashCode()));
320: // To
321: Address toAddress = addressFactory.createAddress(dstSipURI);
322: ToHeader to = headerFactory.createToHeader(toAddress, null);
323: // Contact
324: ContactHeader contact = headerFactory
325: .createContactHeader(fromAddress);
326:
327: List via = new LinkedList();
328: ViaHeader viaHeader = headerFactory.createViaHeader(srclp
329: .getIPAddress(), srclp.getPort(), srclp.getTransport(),
330: // BUG: Use proper RFC3261 branch ID
331: "z9hG4bK" + Long.toString(System.currentTimeMillis())
332: // branch id
333: );
334: via.add(viaHeader);
335: MaxForwardsHeader maxForwards = headerFactory
336: .createMaxForwardsHeader(3);
337:
338: Request request = messageFactory.createRequest(dstSipURI,
339: method, callId, cSeq, from, to, via, maxForwards);
340: request.addHeader(contact);
341: if (contentType != null && contentSubType != null
342: && content != null) {
343: ContentTypeHeader contentTypeHdr = headerFactory
344: .createContentTypeHeader(contentType,
345: contentSubType);
346: request.setContent(content, contentTypeHdr);
347: }
348: // pass the headerFactory - issue17 by larryb@dev.java.net
349: addStatus(headerFactory, request);
350: return request;
351: }
352:
353: /**
354: * Creates an invite request object using the RI. This invite request is
355: * meant to be sent to the TI
356: *
357: * @param contentType
358: * if the content parameter is not null then this is its content
359: * type.
360: * @param contentSubType
361: * if the content parameter is not null then this is its content
362: * sub type.
363: * @param content
364: * if the request is to have any content then this parameter is
365: * used to set it. Th content parameter is to be left to null if
366: * the request won't have any content.
367: * @return an RI->TI invite request
368: * @throws TckInternalError
369: * if anything should gou wrong.
370: */
371: protected Request createRiInviteRequest(String contentType,
372: String contentSubType, Object content)
373: throws TckInternalError {
374: try {
375: return createRequest(Request.INVITE, riAddressFactory,
376: riHeaderFactory, riMessageFactory, riSipProvider,
377: tiSipProvider, contentType, contentSubType, content);
378: } catch (Throwable exc) {
379: throw new TckInternalError(
380: "Failed to create an RI->TI invite request", exc);
381: }
382: }
383:
384: /**
385: * Creates an invite request object using the TI. This invite request is
386: * meant to be sent to the RI
387: *
388: * @param contentType
389: * if the content parameter is not null then this is its content
390: * type.
391: * @param contentSubType
392: * if the content parameter is not null then this is its content
393: * sub type.
394: * @param content
395: * if the request is to have any content then this parameter is
396: * used to set it. Th content parameter is to be left to null if
397: * the request won't have any content.
398: * @return an TI->RI invite request
399: * @throws TiUnexpectedError
400: * if anything should gou wrong.
401: */
402: protected Request createTiInviteRequest(String contentType,
403: String contentSubType, Object content)
404: throws TiUnexpectedError {
405: try {
406: return createRequest(Request.INVITE, tiAddressFactory,
407: tiHeaderFactory, tiMessageFactory, tiSipProvider,
408: riSipProvider, contentType, contentSubType, content);
409: } catch (Throwable exc) {
410: throw new TiUnexpectedError(
411: "Failed to create a TI->RI invite request", exc);
412: }
413: }
414:
415: /**
416: * Creates a register request object using the RI. This register request is
417: * meant to be sent to the TI
418: *
419: * @return an RI->TI register request
420: * @throws TckInternalError
421: * if anything should gou wrong.
422: */
423: protected Request createRiRegisterRequest() throws TckInternalError {
424: try {
425: return createRequest(Request.REGISTER, riAddressFactory,
426: riHeaderFactory, riMessageFactory, riSipProvider,
427: tiSipProvider, null, null, null);
428: } catch (Throwable exc) {
429: throw new TckInternalError(
430: "Failed to create an RI->TI register request", exc);
431: }
432: }
433:
434: /**
435: * Creates a register request object using the TI. This register request is
436: * meant to be sent to the RI
437: *
438: * @return a TI->RI register request
439: * @throws TiUnexpectedError
440: * if anything should gou wrong.
441: */
442: protected Request createTiRegisterRequest()
443: throws TiUnexpectedError {
444: try {
445: return createRequest(Request.REGISTER, tiAddressFactory,
446: tiHeaderFactory, tiMessageFactory, tiSipProvider,
447: riSipProvider, null, null, null);
448:
449: } catch (Throwable exc) {
450: throw new TiUnexpectedError(
451: "Failed to create a TI->RI register request", exc);
452: }
453: }
454:
455: /**
456: * Waits during LISTEN_TIMEOUT milliseconds. This method is called after a
457: * message has been sent so that it has the time to propagate though the
458: * sending and receiving stack
459: */
460: public static void waitForMessage() {
461: sleep(MESSAGES_ARRIVE_FOR);
462: }
463:
464: /**
465: * Wait till a transaction times out.
466: *
467: */
468: protected static void waitForTimeout() {
469: sleep(TRANSACTION_TIMES_OUT_FOR);
470: }
471:
472: /**
473: * waits a good long time for messages.
474: */
475: protected static void waitShortForMessage() {
476: sleep(MESSAGES_ARRIVE_FOR / 2);
477: }
478:
479: /**
480: * Waits during _no_less_ than sleepFor milliseconds. Had to implement it on
481: * top of Thread.sleep() to guarantee minimum sleep time.
482: *
483: * @param sleepFor
484: * the number of miliseconds to wait
485: */
486: protected static void sleep(long sleepFor) {
487: long startTime = System.currentTimeMillis();
488: long haveBeenSleeping = 0;
489: while (haveBeenSleeping < sleepFor) {
490: try {
491: //Thread.sleep(sleepFor - haveBeenSleeping);
492: if (sleepFor - haveBeenSleeping < 500) {
493: Thread.sleep(sleepFor - haveBeenSleeping);
494: } else {
495: Thread.sleep(500);
496: System.out.print(".");
497: }
498: } catch (InterruptedException ex) {
499: // we-ll have to wait again!
500: }
501: haveBeenSleeping = (System.currentTimeMillis() - startTime);
502: }
503:
504: }
505:
506: /**
507: * Add a contact for the TI.
508: */
509: public ContactHeader createTiContact() throws Exception {
510: try {
511: ContactHeader contact = tiHeaderFactory
512: .createContactHeader();
513:
514: // JvB: getIPAddress may return null!
515: String ip = tiSipProvider.getSipStack().getIPAddress();
516: if (ip == null) {
517: ListeningPoint lp = (ListeningPoint) tiSipProvider
518: .getSipStack().getListeningPoints().next();
519: ip = lp.getIPAddress();
520: }
521:
522: SipURI srcSipURI = tiAddressFactory.createSipURI(null, ip);
523: srcSipURI.setPort(tiSipProvider.getListeningPoint("udp")
524: .getPort());
525: srcSipURI.setTransportParam("udp");
526: Address address = tiAddressFactory.createAddress(srcSipURI);
527: address.setDisplayName("TI Contact");
528: contact.setAddress(address);
529: return contact;
530: } catch (Exception ex) {
531: ex.printStackTrace();
532: assertTrue(false);
533: throw ex;
534: }
535: }
536:
537: /**
538: * Add a contact for the TI.
539: */
540: public ContactHeader createRiContact() throws TckInternalError {
541: try {
542: ContactHeader contact = riHeaderFactory
543: .createContactHeader();
544: // BUG reported by Ben Evans (Open Cloud):
545: // Should be using RI's address factory here, not TI's.
546:
547: ListeningPoint lp = riSipProvider.getListeningPoints()[0];
548: SipURI srcSipURI = riAddressFactory.createSipURI(null, lp
549: .getIPAddress());
550: srcSipURI.setPort(lp.getPort());
551: srcSipURI.setTransportParam(lp.getTransport());
552: Address address = riAddressFactory.createAddress(srcSipURI);
553: address.setDisplayName("RI Contact");
554: contact.setAddress(address);
555: return contact;
556: } catch (Exception ex) {
557: throw new TckInternalError(ex.getMessage());
558: }
559: }
560:
561: }
|