package org.mobicents.slee.sipevent.server.subscription.sip;
import gov.nist.javax.sip.Utils;
import gov.nist.javax.sip.header.HeaderFactoryExt;
import gov.nist.javax.sip.header.HeaderFactoryImpl;
import gov.nist.javax.sip.header.ims.PChargingVectorHeader;
import java.io.IOException;
import java.io.StringWriter;
import java.text.ParseException;
import javax.persistence.EntityManager;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.SipException;
import javax.sip.TransactionDoesNotExistException;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.EventHeader;
import javax.sip.header.SubscriptionStateHeader;
import javax.sip.message.Request;
import javax.slee.ActivityContextInterface;
import javax.xml.bind.JAXBElement;
import javax.xml.bind.JAXBException;
import org.apache.log4j.Logger;
import org.mobicents.slee.sipevent.server.subscription.ImplementedSubscriptionControlSbbLocalObject;
import org.mobicents.slee.sipevent.server.subscription.NotifyContent;
import org.mobicents.slee.sipevent.server.subscription.SubscriptionControlSbb;
import org.mobicents.slee.sipevent.server.subscription.pojo.Subscription;
/**
* Handles the notification of a SIP subscriber
*
* @author martins
*
*/
public class SipSubscriberNotificationHandler {
private static Logger logger = Logger
.getLogger(SubscriptionControlSbb.class);
private SipSubscriptionHandler sipSubscriptionHandler;
public SipSubscriberNotificationHandler(
SipSubscriptionHandler sipSubscriptionHandler) {
this.sipSubscriptionHandler = sipSubscriptionHandler;
}
public void notifySipSubscriber(Object content,
ContentTypeHeader contentTypeHeader, Subscription subscription,
EntityManager entityManager,
ImplementedSubscriptionControlSbbLocalObject childSbb) {
try {
// get subscription dialog
ActivityContextInterface dialogACI = sipSubscriptionHandler.sbb
.getActivityContextNamingfacility().lookup(
subscription.getKey().toString());
if (dialogACI != null) {
Dialog dialog = (Dialog) dialogACI.getActivity();
// create notify
Request notify = createNotify(dialog, subscription);
// add content
if (content != null) {
notify = setNotifyContent(subscription, notify, content,
contentTypeHeader, childSbb);
}
// ....aayush added code here (with ref issue #567)
notify.addHeader(addPChargingVectorHeader());
// send notify in dialog related with subscription
dialog.sendRequest(sipSubscriptionHandler.sbb.getSipProvider()
.getNewClientTransaction(notify));
if (logger.isDebugEnabled()) {
logger.debug("NotifySubscribers: subscription "
+ subscription.getKey() + " sent request:\n"
+ notify.toString());
}
} else {
// clean up
logger.warn("Unable to find dialog aci to notify subscription "
+ subscription.getKey()
+ ". Removing subscription data");
sipSubscriptionHandler.sbb.removeSubscriptionData(
entityManager, subscription, null, null, childSbb);
}
} catch (Exception e) {
logger.error("failed to notify subscriber", e);
}
}
public Request setNotifyContent(Subscription subscription, Request notify,
Object content, ContentTypeHeader contentTypeHeader,
ImplementedSubscriptionControlSbbLocalObject childSbb)
throws JAXBException, ParseException, IOException {
if (!subscription.getResourceList()) {
// filter content per subscriber (notifier rules)
Object filteredContent = childSbb.filterContentPerSubscriber(
subscription.getSubscriber(), subscription.getNotifier(),
subscription.getKey().getEventPackage(), content);
// filter content per notifier (subscriber rules)
// TODO
// marshall content to string
StringWriter stringWriter = new StringWriter();
childSbb.getMarshaller().marshal(filteredContent, stringWriter);
notify.setContent(stringWriter.toString(), contentTypeHeader);
stringWriter.close();
}
else {
// resource list subscription, no filtering
if (content instanceof JAXBElement) {
// marshall content to string
StringWriter stringWriter = new StringWriter();
childSbb.getMarshaller().marshal(content, stringWriter);
notify.setContent(stringWriter.toString(), contentTypeHeader);
stringWriter.close();
}
else {
notify.setContent(content, contentTypeHeader);
}
}
return notify;
}
/*
* for internal usage, creates a NOTIFY notify, asks the content to the
* concrete implementation component, and then sends the request to the
* subscriber
*/
public void createAndSendNotify(EntityManager entityManager,
Subscription subscription, Dialog dialog,
ImplementedSubscriptionControlSbbLocalObject childSbb)
throws TransactionDoesNotExistException, SipException,
ParseException {
// create notify
Request notify = createNotify(dialog, subscription);
// add content if subscription is active
if (subscription.getStatus().equals(Subscription.Status.active)) {
if (subscription.getKey().getEventPackage().endsWith(".winfo")) {
// winfo content, increment version before adding the content
subscription.incrementVersion();
entityManager.persist(subscription);
entityManager.flush();
notify.setContent(
sipSubscriptionHandler.sbb
.getWInfoSubscriptionHandler()
.getFullWatcherInfoContent(entityManager,
subscription),
sipSubscriptionHandler.sbb
.getWInfoSubscriptionHandler()
.getWatcherInfoContentHeader());
} else {
// specific event package content
NotifyContent notifyContent = childSbb
.getNotifyContent(subscription);
// add content
if (notifyContent != null) {
try {
notify = setNotifyContent(subscription, notify,
notifyContent.getContent(), notifyContent
.getContentTypeHeader(), childSbb);
} catch (Exception e) {
logger.error("failed to set notify content", e);
}
}
}
}
// ....aayush added code here (with ref issue #567)
notify.addHeader(addPChargingVectorHeader());
// send notify
ClientTransaction clientTransaction = sipSubscriptionHandler.sbb
.getSipProvider().getNewClientTransaction(notify);
dialog.sendRequest(clientTransaction);
if (logger.isDebugEnabled()) {
logger.debug("Request sent:\n" + notify.toString());
}
}
// creates a notify request and fills headers
public Request createNotify(Dialog dialog, Subscription subscription) {
Request notify = null;
try {
notify = dialog.createRequest(Request.NOTIFY);
// add event header
EventHeader eventHeader = sipSubscriptionHandler.sbb
.getHeaderFactory().createEventHeader(
subscription.getKey().getEventPackage());
if (subscription.getKey().getRealEventId() != null)
eventHeader.setEventId(subscription.getKey().getRealEventId());
notify.setHeader(eventHeader);
// add max forwards header
notify.setHeader(sipSubscriptionHandler.sbb.getHeaderFactory()
.createMaxForwardsHeader(
sipSubscriptionHandler.sbb.getConfiguration()
.getMaxForwards()));
/*
* NOTIFY requests MUST contain a "Subscription-State" header with a
* value of "active", "pending", or "terminated". The "active" value
* indicates that the subscription has been accepted and has been
* authorized (in most cases; see section 5.2.). The "pending" value
* indicates that the subscription has been received, but that
* policy information is insufficient to accept or deny the
* subscription at this time. The "terminated" value indicates that
* the subscription is not active.
*/
SubscriptionStateHeader ssh = null;
if (subscription.getStatus().equals(Subscription.Status.active)
|| subscription.getStatus().equals(
Subscription.Status.pending)) {
ssh = sipSubscriptionHandler.sbb.getHeaderFactory()
.createSubscriptionStateHeader(
subscription.getStatus().toString());
/*
* If the value of the "Subscription-State" header is "active"
* or "pending", the notifier SHOULD also include in the
* "Subscription- State" header an "expires" parameter which
* indicates the time remaining on the subscription.
*/
ssh.setExpires(subscription.getRemainingExpires());
} else if (subscription.getStatus().equals(
Subscription.Status.waiting)
|| subscription.getStatus().equals(
Subscription.Status.terminated)) {
ssh = sipSubscriptionHandler.sbb.getHeaderFactory()
.createSubscriptionStateHeader("terminated");
/*
* If the value of the "Subscription-State" header is
* "terminated", the notifier SHOULD also include a "reason"
* parameter.
*/
if(subscription.getLastEvent() != null) {
ssh.setReasonCode(subscription.getLastEvent().toString());
}
}
notify.addHeader(ssh);
// if it's a RLS notify a required header must be present
if (subscription.getResourceList()) {
notify.addHeader(sipSubscriptionHandler.sbb.getHeaderFactory().createRequireHeader("eventlist"));
}
} catch (Exception e) {
logger.error("unable to fill notify headers", e);
}
return notify;
}
/**
*
* @return the newly created P-charging-vector header
* @throws ParseException
*/
private PChargingVectorHeader addPChargingVectorHeader() throws ParseException
{
// aayush..started adding here.
/*
* (with ref to issue #567)
* Need to add a P-charging-vector header here with a unique ICID parameter
* and an orig-ioi parameter pointing to the home domain of the PS.
*/
// sbb.getHeaderFactory() does not provide the API for creating P-headers.
HeaderFactoryExt extensions = new HeaderFactoryImpl();
// Ideally,there should also be an ICID generator in Utils, that generates a unique ICID.
PChargingVectorHeader pcv = extensions.createChargingVectorHeader(Utils.getInstance().generateBranchId()+System.currentTimeMillis());
pcv.setOriginatingIOI(sipSubscriptionHandler.sbb.getConfiguration().getPChargingVectorHeaderTerminatingIOI());
return pcv;
//aayush...added code till here.
}
}