001: package examples.refer;
002:
003: import javax.sip.*;
004: import javax.sip.address.*;
005: import javax.sip.header.*;
006: import javax.sip.message.*;
007:
008: import java.text.ParseException;
009: import java.util.*;
010:
011: import org.apache.log4j.*;
012:
013: /**
014: * This example shows an out-of-dialog REFER scenario:
015: *
016: * referer sends REFER to referee, with Refer-To set to Shootme
017: * referee sends INVITE to Shootme, and NOTIFYs to referer about call progress
018: *
019: * This is the referee
020: *
021: * @see RFC3515 http://www.ietf.org/rfc/rfc3515.txt
022: *
023: * @author Jeroen van Bemmel
024: */
025: public class Referee implements SipListener {
026:
027: private static AddressFactory addressFactory;
028:
029: private static MessageFactory messageFactory;
030:
031: private static HeaderFactory headerFactory;
032:
033: private static SipStack sipStack;
034:
035: private int port;
036:
037: protected SipProvider udpProvider;
038:
039: protected Dialog dialog;
040:
041: private static Logger logger = Logger.getLogger(Referee.class);
042:
043: private EventHeader referEvent;
044:
045: protected static final String usageString = "java "
046: + "examples.shootist.Shootist \n"
047: + ">>>> is your class path set to the root?";
048:
049: private static void usage() {
050: logger.info(usageString);
051: System.exit(0);
052:
053: }
054:
055: public void processRequest(RequestEvent requestEvent) {
056: Request request = requestEvent.getRequest();
057: ServerTransaction serverTransactionId = requestEvent
058: .getServerTransaction();
059:
060: logger.info("\n\nRequest " + request.getMethod()
061: + " received at " + sipStack.getStackName()
062: + " with server transaction id " + serverTransactionId
063: + " and dialog id " + requestEvent.getDialog());
064: logger.info(request.toString());
065: if (request.getMethod().equals(Request.REFER)) {
066: try {
067: processRefer(requestEvent, serverTransactionId);
068: } catch (Exception e) {
069: e.printStackTrace();
070: }
071: }
072:
073: }
074:
075: /**
076: * Process the REFER request.
077: * @throws ParseException
078: * @throws SipException
079: * @throws InvalidArgumentException
080: */
081: public void processRefer(RequestEvent requestEvent,
082: ServerTransaction serverTransaction) throws ParseException,
083: SipException, InvalidArgumentException {
084: SipProvider sipProvider = (SipProvider) requestEvent
085: .getSource();
086: Request refer = requestEvent.getRequest();
087:
088: System.out.println("referee: got an REFER sending Accepted");
089: System.out.println("referee: " + refer.getMethod());
090: System.out.println("referee : dialog = "
091: + requestEvent.getDialog());
092:
093: // Check that it has a Refer-To, if not bad request
094: ReferToHeader refTo = (ReferToHeader) refer
095: .getHeader(ReferToHeader.NAME);
096: if (refTo == null) {
097: Response bad = messageFactory.createResponse(
098: Response.BAD_REQUEST, refer);
099: bad.setReasonPhrase("Missing Refer-To");
100: sipProvider.sendResponse(bad);
101: return;
102: }
103:
104: // Always create a ServerTransaction, best as early as possible in the code
105: Response response = null;
106: ServerTransaction st = requestEvent.getServerTransaction();
107: if (st == null) {
108: st = sipProvider.getNewServerTransaction(refer);
109: }
110:
111: // Check if it is an initial SUBSCRIBE or a refresh / unsubscribe
112: String toTag = Integer
113: .toHexString((int) (Math.random() * Integer.MAX_VALUE));
114: response = messageFactory.createResponse(202, refer);
115: ToHeader toHeader = (ToHeader) response
116: .getHeader(ToHeader.NAME);
117:
118: // Sanity check: to header should not have a tag. Else the dialog
119: // should have matched
120: if (toHeader.getTag() != null) {
121: System.err
122: .println("####ERROR: To-tag!=null but no dialog match! My dialog="
123: + dialog.getState());
124: }
125: toHeader.setTag(toTag); // Application is supposed to set.
126:
127: this .dialog = st.getDialog();
128: // REFER dialogs do not terminate on bye.
129: this .dialog.terminateOnBye(false);
130: if (dialog != null) {
131: System.out.println("Dialog " + dialog);
132: System.out.println("Dialog state " + dialog.getState());
133: System.out.println("local tag=" + dialog.getLocalTag());
134: System.out.println("remote tag=" + dialog.getRemoteTag());
135: }
136:
137: // Both 2xx response to SUBSCRIBE and NOTIFY need a Contact
138: Address address = addressFactory
139: .createAddress("Referee <sip:127.0.0.1>");
140: ((SipURI) address.getURI()).setPort(udpProvider
141: .getListeningPoint("udp").getPort());
142: ContactHeader contactHeader = headerFactory
143: .createContactHeader(address);
144: response.addHeader(contactHeader);
145:
146: // Expires header is mandatory in 2xx responses to REFER
147: ExpiresHeader expires = (ExpiresHeader) refer
148: .getHeader(ExpiresHeader.NAME);
149: if (expires == null) {
150: expires = headerFactory.createExpiresHeader(30); // rather short
151: }
152: response.addHeader(expires);
153:
154: /*
155: * JvB: The REFER MUST be answered first.
156: */
157: st.sendResponse(response);
158:
159: // NOTIFY MUST have "refer" event, possibly with id
160: referEvent = headerFactory.createEventHeader("refer");
161:
162: // Not necessary, but allowed: id == cseq of REFER
163: long id = ((CSeqHeader) refer.getHeader("CSeq")).getSeqNumber();
164: referEvent.setEventId(Long.toString(id));
165:
166: sendNotify(Response.TRYING, "Trying");
167:
168: // Then call the refer-to
169: sendInvite(refTo);
170: }
171:
172: private void sendNotify(int code, String reason)
173: throws SipException, ParseException {
174: /*
175: * NOTIFY requests MUST contain a "Subscription-State" header with a
176: * value of "active", "pending", or "terminated". The "active" value
177: * indicates that the subscription has been accepted and has been
178: * authorized (in most cases; see section 5.2.). The "pending" value
179: * indicates that the subscription has been received, but that
180: * policy information is insufficient to accept or deny the
181: * subscription at this time. The "terminated" value indicates that
182: * the subscription is not active.
183: */
184:
185: Request notifyRequest = dialog.createRequest("NOTIFY");
186:
187: // Initial state is pending, second time we assume terminated (Expires==0)
188: String state = SubscriptionStateHeader.PENDING;
189: if (code > 100 && code < 200) {
190: state = SubscriptionStateHeader.ACTIVE;
191: } else if (code >= 200) {
192: state = SubscriptionStateHeader.TERMINATED;
193: }
194:
195: SubscriptionStateHeader sstate = headerFactory
196: .createSubscriptionStateHeader(state);
197: if (state == SubscriptionStateHeader.TERMINATED) {
198: sstate.setReasonCode("noresource");
199: }
200: notifyRequest.addHeader(sstate);
201: notifyRequest.setHeader(referEvent);
202:
203: Address address = addressFactory
204: .createAddress("Referee <sip:127.0.0.1>");
205: ((SipURI) address.getURI()).setPort(udpProvider
206: .getListeningPoint("udp").getPort());
207: ContactHeader contactHeader = headerFactory
208: .createContactHeader(address);
209: notifyRequest.setHeader(contactHeader);
210: // notifyRequest.setHeader(routeHeader);
211: ClientTransaction ct2 = udpProvider
212: .getNewClientTransaction(notifyRequest);
213:
214: ContentTypeHeader ct = headerFactory.createContentTypeHeader(
215: "message", "sipfrag");
216: ct.setParameter("version", "2.0");
217:
218: notifyRequest.setContent("SIP/2.0 " + code + ' ' + reason, ct);
219:
220: // Let the other side know that the tx is pending acceptance
221: //
222: dialog.sendRequest(ct2);
223: logger.info("NOTIFY Branch ID "
224: + ((ViaHeader) notifyRequest.getHeader(ViaHeader.NAME))
225: .getParameter("branch"));
226: logger.info("Dialog " + dialog);
227: logger.info("Dialog state after NOTIFY: " + dialog.getState());
228: }
229:
230: public void processResponse(ResponseEvent responseReceivedEvent) {
231: logger.info("Got a response");
232: Response response = (Response) responseReceivedEvent
233: .getResponse();
234: Transaction tid = responseReceivedEvent.getClientTransaction();
235:
236: System.out
237: .println("Response received with client transaction id "
238: + tid + ":\n" + response);
239:
240: CSeqHeader cseq = (CSeqHeader) response
241: .getHeader(CSeqHeader.NAME);
242: if (cseq.getMethod().equals(Request.INVITE)) {
243:
244: try {
245: sendNotify(response.getStatusCode(), response
246: .getReasonPhrase());
247: } catch (Exception e1) {
248: e1.printStackTrace();
249: }
250:
251: if (response.getStatusCode() >= 200
252: && response.getStatusCode() < 300) {
253: try {
254: Request ack = tid.getDialog().createAck(
255: cseq.getSeqNumber());
256: tid.getDialog().sendAck(ack);
257:
258: // kill it right away
259: Request bye = tid.getDialog().createRequest(
260: Request.BYE);
261: tid.getDialog().sendRequest(
262: udpProvider.getNewClientTransaction(bye));
263: } catch (Exception e) {
264: e.printStackTrace();
265: }
266: }
267: }
268:
269: }
270:
271: public void processTimeout(javax.sip.TimeoutEvent timeoutEvent) {
272: Transaction transaction;
273: if (timeoutEvent.isServerTransaction()) {
274: transaction = timeoutEvent.getServerTransaction();
275: } else {
276: transaction = timeoutEvent.getClientTransaction();
277: }
278: logger.info("state = " + transaction.getState());
279: logger.info("dialog = " + transaction.getDialog());
280: logger.info("dialogState = "
281: + transaction.getDialog().getState());
282: logger.info("Transaction Time out");
283: }
284:
285: public void sendInvite(ReferToHeader to) {
286:
287: try {
288:
289: String fromName = "Referee";
290: String fromSipAddress = "here.com";
291: String fromDisplayName = "The Master Blaster";
292:
293: // create >From Header
294: SipURI fromAddress = addressFactory.createSipURI(fromName,
295: fromSipAddress);
296:
297: Address fromNameAddress = addressFactory
298: .createAddress(fromAddress);
299: fromNameAddress.setDisplayName(fromDisplayName);
300: FromHeader fromHeader = headerFactory.createFromHeader(
301: fromNameAddress, "12345");
302:
303: // create To Header
304: ToHeader toHeader = headerFactory.createToHeader(to
305: .getAddress(), null);
306:
307: // get Request URI
308: SipURI requestURI = (SipURI) to.getAddress().getURI();
309:
310: // Get transport
311: String transport = requestURI.getTransportParam();
312: if (transport == null)
313: transport = "udp";
314:
315: ListeningPoint lp = udpProvider
316: .getListeningPoint(transport);
317:
318: // Create ViaHeaders
319:
320: ArrayList viaHeaders = new ArrayList();
321: ViaHeader viaHeader = headerFactory.createViaHeader(
322: "127.0.0.1", lp.getPort(), transport, null);
323:
324: // add via headers
325: viaHeaders.add(viaHeader);
326:
327: // Create a new CallId header
328: CallIdHeader callIdHeader = udpProvider.getNewCallId();
329:
330: // Create a new Cseq header
331: CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L,
332: Request.INVITE);
333:
334: // Create a new MaxForwardsHeader
335: MaxForwardsHeader maxForwards = headerFactory
336: .createMaxForwardsHeader(70);
337:
338: // Create the request. (TODO should read request type from Refer-To)
339: Request request = messageFactory.createRequest(requestURI,
340: Request.INVITE, callIdHeader, cSeqHeader,
341: fromHeader, toHeader, viaHeaders, maxForwards);
342: // Create contact headers
343: String host = lp.getIPAddress();
344:
345: SipURI contactURI = addressFactory.createSipURI(fromName,
346: host);
347: contactURI.setPort(lp.getPort());
348: contactURI.setTransportParam(transport);
349:
350: Address contactAddress = addressFactory
351: .createAddress(contactURI);
352:
353: // Add the contact address.
354: contactAddress.setDisplayName(fromName);
355:
356: ContactHeader contactHeader = headerFactory
357: .createContactHeader(contactAddress);
358: request.addHeader(contactHeader);
359:
360: // Create the client transaction.
361: ClientTransaction inviteTid = udpProvider
362: .getNewClientTransaction(request);
363:
364: System.out.println("Invite Dialog = "
365: + inviteTid.getDialog());
366:
367: // send the request out.
368: inviteTid.sendRequest();
369:
370: } catch (Throwable ex) {
371: logger.info(ex.getMessage());
372: ex.printStackTrace();
373: usage();
374: }
375: }
376:
377: private static void initFactories() throws Exception {
378:
379: SipFactory sipFactory = SipFactory.getInstance();
380: sipFactory.setPathName("gov.nist");
381: Properties properties = new Properties();
382:
383: logger.addAppender(new FileAppender(new SimpleLayout(),
384: "refereeoutputlog.txt"));
385:
386: properties.setProperty("javax.sip.STACK_NAME", "referee");
387: // You need 16 for logging traces. 32 for debug + traces.
388: // Your code will limp at 32 but it is best for debugging.
389: // JvB note: debug level may impact order of messages!
390: properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "32");
391: properties.setProperty("gov.nist.javax.sip.DEBUG_LOG",
392: "refereedebug.txt");
393: properties.setProperty("gov.nist.javax.sip.SERVER_LOG",
394: "refereelog.txt");
395:
396: try {
397: // Create SipStack object
398: sipStack = sipFactory.createSipStack(properties);
399: logger.info("sipStack = " + sipStack);
400: } catch (PeerUnavailableException e) {
401: // could not find
402: // gov.nist.jain.protocol.ip.sip.SipStackImpl
403: // in the classpath
404: e.printStackTrace();
405: System.err.println(e.getMessage());
406: if (e.getCause() != null)
407: e.getCause().printStackTrace();
408: System.exit(0);
409: }
410:
411: try {
412: headerFactory = sipFactory.createHeaderFactory();
413: addressFactory = sipFactory.createAddressFactory();
414: messageFactory = sipFactory.createMessageFactory();
415: } catch (Exception ex) {
416: ex.printStackTrace();
417: System.exit(0);
418: }
419: }
420:
421: public void createProvider() {
422:
423: try {
424:
425: ListeningPoint lp = sipStack.createListeningPoint(
426: "127.0.0.1", this .port, "udp");
427:
428: this .udpProvider = sipStack.createSipProvider(lp);
429: logger.info("udp provider " + udpProvider);
430:
431: } catch (Exception ex) {
432: logger.info(ex.getMessage());
433: ex.printStackTrace();
434: usage();
435: }
436:
437: }
438:
439: public Referee(int port) {
440: this .port = port;
441: }
442:
443: public static void main(String args[]) throws Exception {
444: initFactories();
445: Referee notifier = new Referee(5065);
446: notifier.createProvider();
447: notifier.udpProvider.addSipListener(notifier);
448: }
449:
450: public void processIOException(IOExceptionEvent exceptionEvent) {
451: System.out.println("processIOEx:" + exceptionEvent);
452: }
453:
454: public void processTransactionTerminated(
455: TransactionTerminatedEvent tte) {
456:
457: logger.info("transaction terminated:" + tte);
458: }
459:
460: public void processDialogTerminated(
461: DialogTerminatedEvent dialogTerminatedEvent) {
462:
463: logger.info("dialog terminated:" + dialogTerminatedEvent);
464: }
465:
466: }
|