package org.mobicents.servlet.sip.seam.media.framework;
import static org.jboss.seam.annotations.Install.FRAMEWORK;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.sip.SipSession;
import org.jboss.seam.ScopeType;
import org.jboss.seam.annotations.AutoCreate;
import org.jboss.seam.annotations.In;
import org.jboss.seam.annotations.Install;
import org.jboss.seam.annotations.Logger;
import org.jboss.seam.annotations.Name;
import org.jboss.seam.annotations.Observer;
import org.jboss.seam.annotations.Scope;
import org.jboss.seam.core.Events;
import org.jboss.seam.log.Log;
import org.mobicents.mscontrol.MsConnectionEvent;
import org.mobicents.mscontrol.MsEndpoint;
import org.mobicents.mscontrol.MsLinkEvent;
import org.mobicents.mscontrol.events.MsEventIdentifier;
import org.mobicents.mscontrol.events.dtmf.MsDtmfNotifyEvent;
import org.mobicents.mscontrol.events.pkg.DTMF;
import org.mobicents.mscontrol.events.pkg.MsAnnouncement;
import org.mobicents.mscontrol.events.pkg.MsAudio;
import org.mobicents.servlet.sip.seam.entrypoint.media.MediaEvent;
/**
* This class keeps track of some Media events and produces other events for convenience.
* It also keeps track on the DTMF numbers entered in the context of every SipSession.
*
* IMPORTANT NOTE FOR REVIEW!
* The reason this class cannot be Application scoped (and avoid the static dtmfBuffer collection)
* is that when an event is raised in the context of one SipSession Seam flags the sipSession member
* in this class as "injected" in Bijection interceptor and it stays like that or the whole
* application without ever being updated. This seems wrong as we may want to keep state in finer-grained
* contexts.
*
* @author vralev
*
*/
@Name("mediaEventDispatcher")
@Scope(ScopeType.STATELESS)
@Install(precedence=FRAMEWORK)
@AutoCreate
public class MediaEventDispatcher {
@In(required=false) MediaSessionStore mediaSessionStore;
@Logger Log log;
@In(required=false) SipSession sipSession;
private static ConcurrentHashMap<Object, StringBuffer> dtmfBuffer =
new ConcurrentHashMap<Object, StringBuffer>();
private void addNumber(Object object, String number) {
if(dtmfBuffer.get(object) == null) {
dtmfBuffer.put(object, new StringBuffer());
}
dtmfBuffer.get(object).append(number);
}
/**
* Reset the DTMF buffer for give object (SipSession, MsLink or MsConnection)
*
* @param object
*/
public void reset(Object object) {
dtmfBuffer.put(object, new StringBuffer());
}
/**
* Get the DTMF buffer for a given object (SipSession, MsLink or MsConnection)
*
* @param object
* @return Returns null for unknown objects.
*/
public String getDtmfArchive(Object object) {
return dtmfBuffer.get(object).toString();
}
public String getDtmfArchive(Object object, int digits) {
String stringDigits = getDtmfArchive(object);
int numDigits = stringDigits.length();
return stringDigits.substring(numDigits - digits, numDigits);
}
/**
* You can use this method to simulate some event, BUT THIS IS NOT RECOMMENDED!
* @param mediaEvent
*/
@Observer("mediaEvent")
public void doMediaEvent(MediaEvent mediaEvent) {
MsEventIdentifier identifier = mediaEvent.getMsNotifyEvent().getEventID();
if (identifier.equals(DTMF.TONE)) {
MsDtmfNotifyEvent event = (MsDtmfNotifyEvent) mediaEvent.getMsNotifyEvent();
String signal = event.getSequence();
addNumber(mediaEvent.getLink(), signal);
addNumber(mediaEvent.getEndpoint(), signal);
addNumber(mediaEvent.getSipSession(), signal);
Events.instance().raiseEvent("DTMF", signal);
} else if(identifier.equals(MsAnnouncement.COMPLETED)) {
Events.instance().raiseEvent("announcementComplete");
} else if(identifier.equals(MsAnnouncement.FAILED)) {
Events.instance().raiseEvent("announcementFailed");
} else if(identifier.equals(MsAudio.FAILED)) {
Events.instance().raiseEvent("audioFailed");
}
}
/**
* You can use this method to simulate some event, BUT THIS IS NOT RECOMMENDED!
*/
@Observer("preLinkConnected")
public void doLinkConnected(MsLinkEvent linkEvent) {
mediaSessionStore.setMsLink(linkEvent.getSource());
MsEndpoint from = linkEvent.getSource().getEndpoints()[0];
MsEndpoint to = linkEvent.getSource().getEndpoints()[1];
MsEndpoint prEndpoint = (MsEndpoint)
sipSession.getAttribute("org.mobicents.servlet.sip.seam.media.prEndpoint");
MsEndpoint terminatingEndpoint = from;
if(from.getLocalName().equals(prEndpoint.getLocalName())) {
terminatingEndpoint = to;
}
mediaSessionStore.setMsEndpoint(terminatingEndpoint);
Events.instance().raiseEvent("linkConnected", linkEvent);
Events.instance().raiseEvent("storeLinkConnected", linkEvent);
}
/**
* You can use this method to simulate some event, BUT THIS IS NOT RECOMMENDED!
*/
@Observer("connectionOpen")
public void doConnectionOpen(MsConnectionEvent connectionEvent) {
mediaSessionStore.setMsConnection(connectionEvent.getConnection());
mediaSessionStore.setMsEndpoint(connectionEvent.getConnection().getEndpoint());
Events.instance().raiseEvent("storeConnectionOpen", connectionEvent);
}
@Observer("preConnectionHalfOpen")
public void doConnectionHalfOpen(MsConnectionEvent connectionEvent) {
log.info("PRECONNECTION for " + sipSession.toString());
mediaSessionStore.setMsConnection(connectionEvent.getConnection());
mediaSessionStore.setMsEndpoint(connectionEvent.getConnection().getEndpoint());
sipSession.setAttribute("org.mobicents.servlet.sip.seam.media.prEndpoint",
connectionEvent.getConnection().getEndpoint());
Events.instance().raiseEvent("connectionHalfOpen", connectionEvent);
}
/**
* You can use this method to simulate some event, BUT THIS IS NOT RECOMMENDED!
*/
@Observer("sipSessionDestroyed")
public void doSipSessionDestroyed(SipSession sipSession) {
dtmfBuffer.remove(sipSession);
}
/**
* You can use this method to simulate some event, BUT THIS IS NOT RECOMMENDED!
*/
@Observer("linkDisconnected")
public void doLinkDisconnected(MsLinkEvent linkEvent) {
dtmfBuffer.remove(linkEvent.getSource());
}
/**
* You can use this method to simulate some event, BUT THIS IS NOT RECOMMENDED!
*/
@Observer("connectionDisconnected")
public void doConnectionDisconnected(MsConnectionEvent connectionEvent) {
dtmfBuffer.remove(connectionEvent.getConnection());
}
}