package org.mobicents.slee.sipevent.server.subscription.eventlist; import gov.nist.javax.sip.Utils; import java.text.ParseException; import java.util.ArrayList; import java.util.Properties; import java.util.Timer; import java.util.TimerTask; import java.util.TooManyListenersException; import javax.sip.ClientTransaction; import javax.sip.Dialog; import javax.sip.DialogTerminatedEvent; import javax.sip.IOExceptionEvent; import javax.sip.InvalidArgumentException; import javax.sip.ListeningPoint; import javax.sip.ObjectInUseException; import javax.sip.PeerUnavailableException; import javax.sip.RequestEvent; import javax.sip.ResponseEvent; import javax.sip.SipException; import javax.sip.SipFactory; import javax.sip.SipListener; import javax.sip.SipProvider; import javax.sip.SipStack; import javax.sip.TimeoutEvent; import javax.sip.TransactionTerminatedEvent; import javax.sip.TransportNotSupportedException; import javax.sip.address.Address; import javax.sip.address.AddressFactory; import javax.sip.address.SipURI; import javax.sip.header.CSeqHeader; import javax.sip.header.CallIdHeader; import javax.sip.header.ContactHeader; import javax.sip.header.ExpiresHeader; import javax.sip.header.FromHeader; import javax.sip.header.HeaderFactory; import javax.sip.header.MaxForwardsHeader; import javax.sip.header.ToHeader; import javax.sip.header.ViaHeader; import javax.sip.message.MessageFactory; import javax.sip.message.Request; import javax.sip.message.Response; /** * * @author Eduardo Martins * */ public class Subscriber implements SipListener { private final String eventPackage = "presence"; private final static Timer timer = new Timer(); private SipProvider sipProvider; private AddressFactory addressFactory; private MessageFactory messageFactory; private HeaderFactory headerFactory; private SipStack sipStack; private ContactHeader contactHeader; private String notifierPort; private String transport; private ListeningPoint listeningPoint; private Dialog dialog; private int expires = 300; private final String subscriber; private final String notifier; private final int listeningPort; private final ResourceListServerSipTest test; private enum StateMachine { starting, started, stopping } private StateMachine stateMachine; public Subscriber(String subscriber, String notifier, int listeningPort, ResourceListServerSipTest test) { this.subscriber = subscriber; this.notifier = notifier; this.listeningPort = listeningPort; this.test = test; } public void subscribe() { this.stateMachine = StateMachine.starting; try { initSipStack(); sendInitialRequest(); } catch (Exception e) { e.printStackTrace(); test.failTest(e.getMessage()); } } private void initSipStack() throws PeerUnavailableException, TransportNotSupportedException, InvalidArgumentException, ObjectInUseException, TooManyListenersException { // init sip stack notifierPort = "5060"; transport = "udp"; SipFactory sipFactory = SipFactory.getInstance(); sipFactory.setPathName("gov.nist"); Properties properties = new Properties(); properties.setProperty("javax.sip.USE_ROUTER_FOR_ALL_URIS", "false"); properties.setProperty("javax.sip.STACK_NAME", subscriber); properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "subscriber_"+listeningPort+"_debug.txt"); properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "subscriber_"+listeningPort+"_log.txt"); properties.setProperty("javax.sip.FORKABLE_EVENTS", "foo"); // Set to 0 in your production code for max speed. // You need 16 for logging traces. 32 for debug + traces. // Your code will limp at 32 but it is best for debugging. properties.setProperty("gov.nist.javax.sip.TRACE_LEVEL", "0"); sipStack = sipFactory.createSipStack(properties); headerFactory = sipFactory.createHeaderFactory(); addressFactory = sipFactory.createAddressFactory(); messageFactory = sipFactory.createMessageFactory(); this.listeningPoint = sipStack.createListeningPoint(ServerConfiguration.SERVER_HOST, listeningPort, transport); sipProvider = sipStack.createSipProvider(listeningPoint); sipProvider.addSipListener(this); } private void sendInitialRequest() throws ParseException, InvalidArgumentException, SipException { // create From Header Address fromAddress = addressFactory.createAddress(subscriber); FromHeader fromHeader = headerFactory.createFromHeader( fromAddress, Utils.getInstance().generateTag()); // create To Header Address toAddress = addressFactory.createAddress(notifier); ToHeader toHeader = headerFactory.createToHeader(toAddress, null); // Create ViaHeaders ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>(); int port = sipProvider.getListeningPoint(transport).getPort(); ViaHeader viaHeader = headerFactory.createViaHeader(ServerConfiguration.SERVER_HOST, port, transport, null); viaHeaders.add(viaHeader); // Create a new CallId header CallIdHeader callIdHeader = sipProvider.getNewCallId(); // Create a new Cseq header CSeqHeader cSeqHeader = headerFactory.createCSeqHeader(1L, Request.SUBSCRIBE); // Create a new MaxForwardsHeader MaxForwardsHeader maxForwards = headerFactory .createMaxForwardsHeader(70); // Create the request. Request request = messageFactory.createRequest(toAddress.getURI(), Request.SUBSCRIBE, callIdHeader, cSeqHeader, fromHeader, toHeader, viaHeaders, maxForwards); // Create contact headers SipURI contactURI = (SipURI) addressFactory.createURI(subscriber + ":" + listeningPoint.getIPAddress()); contactURI.setTransportParam(transport); contactURI.setPort(port); // add the contact address. Address contactAddress = addressFactory.createAddress(contactURI); // create and save contact header contactHeader = headerFactory.createContactHeader(contactAddress); request.addHeader(contactHeader); // add route request.addHeader(headerFactory.createRouteHeader(addressFactory .createAddress("<sip:"+ServerConfiguration.SERVER_HOST+":" + notifierPort + ";transport=" + transport + ";lr>"))); // Create an event header for the subscription. request.addHeader(headerFactory.createEventHeader(this.eventPackage)); // add expires request.addHeader(headerFactory.createExpiresHeader(this.expires)); // add accept headers request.addHeader(headerFactory.createAcceptHeader("application", "pidf+xml")); request.addHeader(headerFactory.createAcceptHeader("application", "rlmi+xml")); request.addHeader(headerFactory.createAcceptHeader("multipart", "related")); // add supported header request.addHeader(headerFactory.createSupportedHeader("eventlist")); // create the client transaction. ClientTransaction clientTransaction = sipProvider.getNewClientTransaction(request); // save the dialog this.dialog = clientTransaction.getDialog(); // send the request out. clientTransaction.sendRequest(); } private void refresh() throws SipException, InvalidArgumentException, ParseException { Request request = dialog.createRequest(Request.SUBSCRIBE); // Create a new MaxForwardsHeader request.addHeader(headerFactory .createMaxForwardsHeader(70)); // Create an event header for the subscription. request.addHeader(headerFactory.createEventHeader(this.eventPackage)); // add expires request.addHeader(headerFactory.createExpiresHeader(this.expires)); // add accept headers request.addHeader(headerFactory.createAcceptHeader("application", "pidf+xml")); request.addHeader(headerFactory.createAcceptHeader("application", "rlmi+xml")); request.addHeader(headerFactory.createAcceptHeader("multipart", "related")); // add supported header request.addHeader(headerFactory.createSupportedHeader("eventlist")); // re-set request uri request.setRequestURI(addressFactory.createURI(notifier)); // create client tx and send request sipProvider.getNewClientTransaction(request).sendRequest(); } public void unsubscribe() throws SipException, InvalidArgumentException, ParseException { Request request = dialog.createRequest(Request.SUBSCRIBE); // Create a new MaxForwardsHeader request.addHeader(headerFactory .createMaxForwardsHeader(70)); // Create an event header for the subscription. request.addHeader(headerFactory.createEventHeader(this.eventPackage)); // add expires request.addHeader(headerFactory.createExpiresHeader(0)); // add accept headers request.addHeader(headerFactory.createAcceptHeader("application", "pidf+xml")); request.addHeader(headerFactory.createAcceptHeader("application", "rlmi+xml")); request.addHeader(headerFactory.createAcceptHeader("multipart", "related")); // add supported header request.addHeader(headerFactory.createSupportedHeader("eventlist")); // re-set request uri request.setRequestURI(addressFactory.createURI(notifier)); // create client tx and send request sipProvider.getNewClientTransaction(request).sendRequest(); } private void setRefreshTimer(int expires) { TimerTask task = new TimerTask() { @Override public void run() { try { this.cancel(); refresh(); } catch (Exception e) { e.printStackTrace(); test.failTest(e.getMessage()); } } }; timer.schedule(task, (expires-1)*1000); } // JAIN SIP LISTENER public void processDialogTerminated(DialogTerminatedEvent arg0) { // TODO Auto-generated method stub } public void processIOException(IOExceptionEvent arg0) { test.failTest("processIOException: arg0 = "+arg0); } public void processRequest(RequestEvent arg0) { Request request = arg0.getRequest(); System.out.println("processRequest: request = "+request); try { arg0.getServerTransaction().sendResponse(messageFactory.createResponse(Response.OK, request)); } catch (Exception e) { e.printStackTrace(); test.failTest(e.getMessage()); } } public void processResponse(ResponseEvent arg0) { Response response = arg0.getResponse(); System.out.println("Subscriber "+subscriber+" rcvd response:\n"+response); switch (this.stateMachine) { case starting: // initial request response if (response.getStatusCode() > 299) { test.failTest("unexpected response when "+this.stateMachine+" subscriber "+subscriber+", can't proceeed"); } else if (response.getStatusCode() > 199) { if (this.dialog == null) { this.dialog = arg0.getDialog(); } ExpiresHeader expiresHeader = response.getExpires(); if (expiresHeader != null) { this.expires = expiresHeader.getExpires(); setRefreshTimer(this.expires); } } break; case started: // refresh request response if (response.getStatusCode() > 299) { test.failTest("unexpected response when "+this.stateMachine+" subscriber "+subscriber+", can't proceeed"); } else if (response.getStatusCode() > 199) { ExpiresHeader expiresHeader = response.getExpires(); if (expiresHeader != null) { this.expires = expiresHeader.getExpires(); setRefreshTimer(this.expires); } } break; case stopping: // refresh request response if (response.getStatusCode() > 299) { test.failTest("unexpected response when "+this.stateMachine+" subscriber "+subscriber+", can't proceeed"); } else if (response.getStatusCode() > 199) { // TODO finished } else { test.failTest("unexpected response when "+this.stateMachine+" subscriber "+subscriber+", can't proceeed"); } break; default: test.failTest("invalid state "+this.stateMachine+" on subscriber "+subscriber); } } public void processTimeout(TimeoutEvent arg0) { test.failTest("processTimeout: arg0 = "+arg0); } public void processTransactionTerminated(TransactionTerminatedEvent arg0) { } }