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.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.SIPETagHeader;
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 Publisher implements SipListener {
private final String eventPackage = "presence";
private final String contentType = "application";
private final String contentSubType = "pidf+xml";
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 int expires = 60;
private String etag;
private final String tupleId = Utils.getInstance().generateTag();
private final String publisher;
private final int listeningPort;
private final ResourceListServerSipTest test;
private enum StateMachine {
starting, started, stopping
}
private StateMachine stateMachine;
public Publisher(String publisher, int listeningPort, ResourceListServerSipTest test) {
this.publisher = publisher;
this.listeningPort = listeningPort;
this.test = test;
}
public void publish() throws InvalidArgumentException,
TooManyListenersException, ParseException, SipException {
this.stateMachine = StateMachine.starting;
initSipStack();
sendPublishRequest(getAvailableRequestContent(), this.expires);
}
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", publisher);
properties.setProperty("gov.nist.javax.sip.DEBUG_LOG", "publisher_"
+ listeningPort + "_debug.txt");
properties.setProperty("gov.nist.javax.sip.SERVER_LOG", "publisher_"
+ 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 sendPublishRequest(String content, int expires) throws ParseException,
InvalidArgumentException, SipException {
// create From Header
Address address = addressFactory.createAddress(publisher);
FromHeader fromHeader = headerFactory.createFromHeader(address, Utils.getInstance()
.generateTag());
// create To Header
ToHeader toHeader = headerFactory.createToHeader(address, 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.PUBLISH);
// Create a new MaxForwardsHeader
MaxForwardsHeader maxForwards = headerFactory
.createMaxForwardsHeader(70);
// Create the request.
Request request = messageFactory.createRequest(address.getURI(),
Request.PUBLISH, callIdHeader, cSeqHeader, fromHeader,
toHeader, viaHeaders, maxForwards);
// Create contact headers
SipURI contactURI = (SipURI) addressFactory.createURI(publisher + ":"
+ 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(expires));
if (content != null) {
// add content
request.setContent(content, headerFactory.createContentTypeHeader(
this.contentType, this.contentSubType));
}
if (etag != null) {
// add etag header
request.addHeader(headerFactory.createSIPIfMatchHeader(etag));
}
// create the client transaction.
ClientTransaction clientTransaction = sipProvider
.getNewClientTransaction(request);
// send the request out.
clientTransaction.sendRequest();
}
private String getAvailableRequestContent() {
return "<?xml version='1.0' encoding='UTF-8'?>"
+ "<presence xmlns='urn:ietf:params:xml:ns:pidf' entity='"
+ publisher
+ "'>"
+ "<tuple id='"+tupleId+"'><status><basic>open</basic></status></tuple>"
+ "</presence>";
}
private String getBusyRequestContent() {
return "<?xml version='1.0' encoding='UTF-8'?>"
+ "<presence xmlns='urn:ietf:params:xml:ns:pidf' xmlns:dm='urn:ietf:params:xml:ns:pidf:data-model' xmlns:rpid='urn:ietf:params:xml:ns:pidf:rpid' xmlns:c='urn:ietf:params:xml:ns:pidf:cipid' entity='"
+ publisher
+ "'>"
+ "<tuple id='"+tupleId+"'><status><basic>open</basic></status></tuple>"
+ "<dm:person id='"+tupleId+"'>"
+ "<rpid:activities><rpid:busy/></rpid:activities>"
+ "<dm:note>Busy</dm:note>" + "</dm:person>" + "</presence>";
}
private String getUnavailableRequestContent() {
return "<?xml version='1.0' encoding='UTF-8'?>"
+ "<presence xmlns='urn:ietf:params:xml:ns:pidf' entity='"
+ publisher
+ "'>"
+ "<tuple id='"+tupleId+"'><status><basic>closed</basic></status></tuple>"
+ "</presence>";
}
private boolean busyState = false;
private String getRefreshRequestContent() {
if (busyState) {
busyState = false;
return getAvailableRequestContent();
}
else {
busyState = true;
return getBusyRequestContent();
}
}
private void refresh() throws SipException, InvalidArgumentException,
ParseException {
sendPublishRequest(getRefreshRequestContent(), this.expires);
}
public void unpublish() throws SipException, InvalidArgumentException,
ParseException {
sendPublishRequest(null,0);
}
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) {
test.failTest("processRequest: arg0 = " + arg0);
}
public void processResponse(ResponseEvent arg0) {
Response response = arg0.getResponse();
System.out.println("Publisher " + publisher + " rcvd response:\n"
+ response);
switch (this.stateMachine) {
case starting:
// initial request response
if (response.getStatusCode() > 299) {
test.failTest("unexpected response when " + this.stateMachine
+ " publisher " + publisher + ", can't proceeed");
} else if (response.getStatusCode() > 199) {
SIPETagHeader sIPETagHeader = (SIPETagHeader) response
.getHeader(SIPETagHeader.NAME);
ExpiresHeader expiresHeader = response.getExpires();
if (sIPETagHeader != null && expiresHeader != null) {
this.expires = expiresHeader.getExpires();
setRefreshTimer(this.expires);
etag = sIPETagHeader.getETag();
}
} else {
test.failTest("unexpected response when " + this.stateMachine
+ " publisher " + publisher + ", can't proceeed");
}
break;
case started:
// refresh request response
if (response.getStatusCode() > 299) {
test.failTest("unexpected response when " + this.stateMachine
+ " publisher " + publisher + ", can't proceeed");
} else if (response.getStatusCode() > 199) {
SIPETagHeader sIPETagHeader = (SIPETagHeader) response
.getHeader(SIPETagHeader.NAME);
ExpiresHeader expiresHeader = response.getExpires();
if (sIPETagHeader != null && expiresHeader != null) {
this.expires = expiresHeader.getExpires();
setRefreshTimer(this.expires);
etag = sIPETagHeader.getETag();
}
} else {
test.failTest("unexpected response when " + this.stateMachine
+ " publisher " + publisher + ", can't proceeed");
}
break;
case stopping:
// refresh request response
if (response.getStatusCode() > 299) {
test.failTest("unexpected response when " + this.stateMachine
+ " publisher " + publisher + ", can't proceeed");
} else if (response.getStatusCode() > 199) {
// TODO finished
} else {
test.failTest("unexpected response when " + this.stateMachine
+ " publisher " + publisher + ", can't proceeed");
}
break;
default:
test.failTest("invalid state " + this.stateMachine
+ " on publisher " + publisher);
}
}
public void processTimeout(TimeoutEvent arg0) {
test.failTest("processTimeout: arg0 = " + arg0);
}
public void processTransactionTerminated(TransactionTerminatedEvent arg0) {
}
}