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