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.callflows.subsnotify;
021:
022: import javax.sip.ClientTransaction;
023: import javax.sip.Dialog;
024: import javax.sip.DialogState;
025: import javax.sip.DialogTerminatedEvent;
026: import javax.sip.IOExceptionEvent;
027: import javax.sip.ListeningPoint;
028: import javax.sip.RequestEvent;
029: import javax.sip.ResponseEvent;
030: import javax.sip.ServerTransaction;
031: import javax.sip.SipListener;
032: import javax.sip.SipProvider;
033: import javax.sip.SipStack;
034: import javax.sip.Transaction;
035: import javax.sip.TransactionTerminatedEvent;
036: import javax.sip.address.Address;
037: import javax.sip.address.AddressFactory;
038: import javax.sip.address.SipURI;
039: import javax.sip.header.CSeqHeader;
040: import javax.sip.header.ContactHeader;
041: import javax.sip.header.EventHeader;
042: import javax.sip.header.ExpiresHeader;
043: import javax.sip.header.HeaderFactory;
044: import javax.sip.header.SubscriptionStateHeader;
045: import javax.sip.header.ToHeader;
046: import javax.sip.header.ViaHeader;
047: import javax.sip.message.MessageFactory;
048: import javax.sip.message.Request;
049: import javax.sip.message.Response;
050:
051: import org.apache.log4j.FileAppender;
052: import org.apache.log4j.Level;
053: import org.apache.log4j.Logger;
054: import org.apache.log4j.SimpleLayout;
055:
056: import test.tck.TestHarness;
057: import test.tck.msgflow.callflows.ProtocolObjects;
058:
059: /**
060: * This is the side that sends out the notify.
061: *
062: * This code is released to domain.
063: *
064: * @author M. Ranganathan
065: */
066:
067: public class Notifier implements SipListener {
068:
069: private static AddressFactory addressFactory;
070:
071: private static MessageFactory messageFactory;
072:
073: private static HeaderFactory headerFactory;
074:
075: private static SipStack sipStack;
076:
077: private int port;
078:
079: protected SipProvider sipProvider;
080:
081: protected Dialog dialog;
082:
083: private String transport;
084:
085: private static Logger logger = Logger.getLogger(Notifier.class);
086:
087: private boolean gotSubscribeRequest;
088:
089: static {
090: try {
091: logger.setLevel(Level.INFO);
092: logger.addAppender(new FileAppender(new SimpleLayout(),
093: "logs/notifieroutputlog.txt"));
094: } catch (Exception ex) {
095: logger.info(ex.getMessage(), ex);
096: TestHarness
097: .fail("Failed to initialize Subscriber, because of "
098: + ex.getMessage());
099: }
100: }
101:
102: class MyEventSource implements Runnable {
103: private Notifier notifier;
104: private EventHeader eventHeader;
105:
106: public MyEventSource(Notifier notifier, EventHeader eventHeader) {
107: this .notifier = notifier;
108: this .eventHeader = eventHeader;
109: }
110:
111: public void run() {
112: try {
113: for (int i = 0; i < 1; i++) {
114:
115: Thread.sleep(1000);
116: Request request = this .notifier.dialog
117: .createRequest(Request.NOTIFY);
118: SubscriptionStateHeader subscriptionState = headerFactory
119: .createSubscriptionStateHeader(SubscriptionStateHeader.ACTIVE);
120: request.addHeader(subscriptionState);
121: request.addHeader(eventHeader);
122:
123: // Lets mark our Contact
124: ((SipURI) dialog.getLocalParty().getURI())
125: .setParameter("id", "not2");
126:
127: ClientTransaction ct = sipProvider
128: .getNewClientTransaction(request);
129: logger.info("NOTIFY Branch ID "
130: + ((ViaHeader) request
131: .getHeader(ViaHeader.NAME))
132: .getParameter("branch"));
133: this .notifier.dialog.sendRequest(ct);
134: logger.info("Dialog " + dialog);
135: logger.info("Dialog state after active NOTIFY: "
136: + dialog.getState());
137: }
138:
139: } catch (Throwable ex) {
140: logger.info(ex.getMessage(), ex);
141: TestHarness
142: .fail("Failed MyEventSource.run(), because of "
143: + ex.getMessage());
144: }
145: }
146: }
147:
148: public void processRequest(RequestEvent requestEvent) {
149: Request request = requestEvent.getRequest();
150: ServerTransaction serverTransactionId = requestEvent
151: .getServerTransaction();
152:
153: logger.info("\n\nRequest " + request.getMethod()
154: + " received at " + sipStack.getStackName()
155: + " with server transaction id " + serverTransactionId
156: + " and dialog id " + requestEvent.getDialog());
157:
158: if (request.getMethod().equals(Request.SUBSCRIBE)) {
159: processSubscribe(requestEvent, serverTransactionId);
160: }
161:
162: }
163:
164: /**
165: * Process the invite request.
166: */
167: public void processSubscribe(RequestEvent requestEvent,
168: ServerTransaction serverTransaction) {
169: SipProvider sipProvider = (SipProvider) requestEvent
170: .getSource();
171: Request request = requestEvent.getRequest();
172: try {
173: logger.info("notifier: got an Subscribe sending OK");
174: logger.info("notifier: " + request);
175: logger.info("notifier : dialog = "
176: + requestEvent.getDialog());
177: EventHeader eventHeader = (EventHeader) request
178: .getHeader(EventHeader.NAME);
179: this .gotSubscribeRequest = true;
180:
181: AbstractSubsnotifyTestCase.assertTrue(
182: "Event header is null ", eventHeader != null);
183:
184: // Always create a ServerTransaction, best as early as possible in the code
185: Response response = null;
186: ServerTransaction st = requestEvent.getServerTransaction();
187: if (st == null) {
188: st = sipProvider.getNewServerTransaction(request);
189: }
190:
191: // Check if it is an initial SUBSCRIBE or a refresh / unsubscribe
192: boolean isInitial = requestEvent.getDialog() == null;
193: if (isInitial) {
194: // JvB: need random tags to test forking
195: String toTag = Integer
196: .toHexString((int) (Math.random() * Integer.MAX_VALUE));
197: response = messageFactory.createResponse(202, request);
198: ToHeader toHeader = (ToHeader) response
199: .getHeader(ToHeader.NAME);
200:
201: // Sanity check: to header should not ahve a tag. Else the dialog
202: // should have matched
203: AbstractSubsnotifyTestCase.assertTrue(
204: "To tag should be null ",
205: toHeader.getTag() == null);
206: toHeader.setTag(toTag); // Application is supposed to set.
207:
208: this .dialog = st.getDialog();
209: // subscribe dialogs do not terminate on bye.
210: this .dialog.terminateOnBye(false);
211:
212: AbstractSubsnotifyTestCase
213: .assertTrue(
214: "initial -- dialog assigned to the transaction not null ",
215: dialog != null);
216: AbstractSubsnotifyTestCase.assertTrue(
217: "Dialog state should be null ", dialog
218: .getState() == null);
219:
220: } else {
221: response = messageFactory.createResponse(200, request);
222: }
223:
224: // Both 2xx response to SUBSCRIBE and NOTIFY need a Contact
225: Address address = addressFactory
226: .createAddress("Notifier <sip:127.0.0.1>");
227: ((SipURI) address.getURI()).setPort(sipProvider
228: .getListeningPoint(transport).getPort());
229: ContactHeader contactHeader = headerFactory
230: .createContactHeader(address);
231: response.addHeader(contactHeader);
232:
233: // Expires header is mandatory in 2xx responses to SUBSCRIBE
234: ExpiresHeader expires = (ExpiresHeader) request
235: .getHeader(ExpiresHeader.NAME);
236: if (expires == null) {
237: expires = headerFactory.createExpiresHeader(30); // rather short
238: }
239: response.addHeader(expires);
240:
241: /*
242: * JvB: The SUBSCRIBE MUST be answered first. See RFC3265 3.1.6.2:
243: * "[...] a NOTIFY message is always sent immediately after any 200-
244: * class response to a SUBSCRIBE request"
245: *
246: * Do this before creating the NOTIFY request below
247: */
248: st.sendResponse(response);
249: //Thread.sleep(1000); // Be kind to implementations
250:
251: /*
252: * NOTIFY requests MUST contain a "Subscription-State" header with a
253: * value of "active", "pending", or "terminated". The "active" value
254: * indicates that the subscription has been accepted and has been
255: * authorized (in most cases; see section 5.2.). The "pending" value
256: * indicates that the subscription has been received, but that
257: * policy information is insufficient to accept or deny the
258: * subscription at this time. The "terminated" value indicates that
259: * the subscription is not active.
260: */
261:
262: Request notifyRequest = dialog.createRequest("NOTIFY");
263:
264: // Mark the contact header, to check that the remote contact is updated
265: ((SipURI) contactHeader.getAddress().getURI())
266: .setParameter("id", "not");
267:
268: // Initial state is pending, second time we assume terminated (Expires==0)
269: SubscriptionStateHeader sstate = headerFactory
270: .createSubscriptionStateHeader(isInitial ? SubscriptionStateHeader.PENDING
271: : SubscriptionStateHeader.TERMINATED);
272:
273: // Need a reason for terminated
274: if (sstate.getState().equalsIgnoreCase("terminated")) {
275: sstate.setReasonCode("deactivated");
276: }
277:
278: notifyRequest.addHeader(sstate);
279: notifyRequest.setHeader(eventHeader);
280: notifyRequest.setHeader(contactHeader);
281: // notifyRequest.setHeader(routeHeader);
282: ClientTransaction ct = sipProvider
283: .getNewClientTransaction(notifyRequest);
284:
285: // Let the other side know that the tx is pending acceptance
286: //
287: dialog.sendRequest(ct);
288: logger.info("NOTIFY Branch ID "
289: + ((ViaHeader) request.getHeader(ViaHeader.NAME))
290: .getParameter("branch"));
291: logger.info("Dialog " + dialog);
292: logger.info("Dialog state after pending NOTIFY: "
293: + dialog.getState());
294: AbstractSubsnotifyTestCase.assertTrue(
295: "Dialog state after pending NOTIFY ", dialog
296: .getState() == DialogState.CONFIRMED);
297:
298: if (isInitial) {
299: Thread myEventSource = new Thread(new MyEventSource(
300: this , eventHeader));
301: myEventSource.start();
302: }
303: } catch (Throwable ex) {
304: logger.info(ex.getMessage(), ex);
305: TestHarness
306: .fail("Failed to processs Subscriber, because of "
307: + ex.getMessage());
308: }
309: }
310:
311: public void processResponse(ResponseEvent responseReceivedEvent) {
312: Response response = (Response) responseReceivedEvent
313: .getResponse();
314: Transaction tid = responseReceivedEvent.getClientTransaction();
315:
316: logger.info("Response received with client transaction id "
317: + tid + " CSeq = "
318: + response.getHeader(CSeqHeader.NAME)
319: + " status code = " + response.getStatusCode());
320:
321: }
322:
323: public void processTimeout(javax.sip.TimeoutEvent timeoutEvent) {
324: Transaction transaction;
325: if (timeoutEvent.isServerTransaction()) {
326: transaction = timeoutEvent.getServerTransaction();
327: } else {
328: transaction = timeoutEvent.getClientTransaction();
329: }
330: logger.info("state = " + transaction.getState());
331: logger.info("dialog = " + transaction.getDialog());
332: logger.info("dialogState = "
333: + transaction.getDialog().getState());
334: logger.info("Transaction Time out");
335:
336: AbstractSubsnotifyTestCase.fail("Unexpected timeout event");
337: }
338:
339: public SipProvider createProvider(int newPort) {
340:
341: try {
342:
343: port = newPort;
344:
345: ListeningPoint lp = sipStack.createListeningPoint(
346: "127.0.0.1", this .port, transport);
347:
348: this .sipProvider = sipStack.createSipProvider(lp);
349: logger.info("udp provider " + sipProvider);
350:
351: } catch (Exception ex) {
352: logger.info(ex.getMessage(), ex);
353: sipProvider = null;
354: TestHarness.fail("Failed to create SIP Provider on port "
355: + newPort + ", because of " + ex.getMessage());
356: }
357:
358: return sipProvider;
359: }
360:
361: public Notifier(ProtocolObjects protObjects) {
362: addressFactory = protObjects.addressFactory;
363: messageFactory = protObjects.messageFactory;
364: headerFactory = protObjects.headerFactory;
365: sipStack = protObjects.sipStack;
366: transport = protObjects.transport;
367: }
368:
369: public void processIOException(IOExceptionEvent exceptionEvent) {
370: // TODO Auto-generated method stub
371:
372: }
373:
374: public void processTransactionTerminated(
375: TransactionTerminatedEvent transactionTerminatedEvent) {
376: // TODO Auto-generated method stub
377:
378: }
379:
380: public void processDialogTerminated(
381: DialogTerminatedEvent dialogTerminatedEvent) {
382: // TODO Auto-generated method stub
383:
384: }
385:
386: public void checkState() {
387: TestHarness.assertTrue("Did not see subscribe",
388: this.gotSubscribeRequest);
389: }
390:
391: }
|