package org.mobicents.slee.resource.sip11;
import gov.nist.javax.sip.header.CSeq;
import gov.nist.javax.sip.header.CallID;
import gov.nist.javax.sip.header.Via;
import java.net.DatagramSocket;
import java.net.InetSocketAddress;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.DialogTerminatedEvent;
import javax.sip.IOExceptionEvent;
import javax.sip.ListeningPoint;
import javax.sip.ObjectInUseException;
import javax.sip.RequestEvent;
import javax.sip.ResponseEvent;
import javax.sip.ServerTransaction;
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.Transaction;
import javax.sip.TransactionAlreadyExistsException;
import javax.sip.TransactionState;
import javax.sip.TransactionTerminatedEvent;
import javax.sip.address.AddressFactory;
import javax.sip.header.CSeqHeader;
import javax.sip.header.CallIdHeader;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.HeaderFactory;
import javax.sip.header.ToHeader;
import javax.sip.message.MessageFactory;
import javax.sip.message.Request;
import javax.sip.message.Response;
import javax.slee.Address;
import javax.slee.AddressPlan;
import javax.slee.facilities.Tracer;
import javax.slee.resource.ActivityFlags;
import javax.slee.resource.ActivityHandle;
import javax.slee.resource.ConfigProperties;
import javax.slee.resource.EventFlags;
import javax.slee.resource.FailureReason;
import javax.slee.resource.FireableEventType;
import javax.slee.resource.InvalidConfigurationException;
import javax.slee.resource.Marshaler;
import javax.slee.resource.ReceivableService;
import javax.slee.resource.ResourceAdaptor;
import javax.slee.resource.ResourceAdaptorContext;
import javax.slee.transaction.SleeTransactionManager;
import net.java.slee.resource.sip.CancelRequestEvent;
import net.java.slee.resource.sip.DialogActivity;
import org.mobicents.slee.resource.sip11.wrappers.ACKDummyTransaction;
import org.mobicents.slee.resource.sip11.wrappers.ClientTransactionWrapper;
import org.mobicents.slee.resource.sip11.wrappers.DialogWrapper;
import org.mobicents.slee.resource.sip11.wrappers.RequestEventWrapper;
import org.mobicents.slee.resource.sip11.wrappers.ResponseEventWrapper;
import org.mobicents.slee.resource.sip11.wrappers.ServerTransactionWrapper;
import org.mobicents.slee.resource.sip11.wrappers.TimeoutEventWrapper;
import org.mobicents.slee.resource.sip11.wrappers.TransactionTerminatedEventWrapper;
import org.mobicents.slee.resource.sip11.wrappers.WrapperSuperInterface;
public class SipResourceAdaptor implements SipListener, ResourceAdaptor {
// Config Properties Names -------------------------------------------
private static final String SIP_BIND_ADDRESS = "javax.sip.IP_ADDRESS";
private static final String SIP_PORT_BIND = "javax.sip.PORT";
private static final String TRANSPORTS_BIND = "javax.sip.TRANSPORT";
private static final String STACK_NAME_BIND = "javax.sip.STACK_NAME";
// Config Properties Values -------------------------------------------
private int port;
private Set<String> transports = new HashSet<String>();
private String transportsProperty;
private String stackName;
private String stackAddress;
/**
* allowed transports
*/
private Set<String> allowedTransports = new HashSet<String>();
private SipProvider provider;
private SleeSipProviderImpl providerProxy;
// Activity related ====================
/**
* Holds mapping between fromTag+CallId to activity handle This amppping is
* arbitrary since from/to tags are exchangable between requests - in
* meaning, depending on which side makes request it puts its local tag into
* from header. However this isnt big pain as this is used to swallow late
* 2xx for UAC dialog forking and possiblt retransmissions
*/
private Map<String, SipActivityHandle> fromTagCallId2Handle = null;
private Map<SipActivityHandle,WrapperSuperInterface> activities = null;
/**
* caches the eventIDs, avoiding lookup in container
*/
public final EventIDCache eventIdCache = new EventIDCache();
/**
* tells the RA if an event with a specified ID should be filtered or not
*/
private final EventIDFilter eventIDFilter = new EventIDFilter();
/**
*
*/
private ResourceAdaptorContext raContext;
/**
*
*/
private Tracer tracer;
/**
*
*/
private SipStack sipStack = null;
/**
*
*/
private SipFactory sipFactory = null;
/**
* for all events we are interested in knowing when the event failed to be processed
*/
private static final int EVENT_FLAGS = getEventFlags();
private static int getEventFlags() {
int eventFlags = EventFlags.REQUEST_EVENT_UNREFERENCED_CALLBACK;
EventFlags.setRequestProcessingFailedCallback(eventFlags);
return eventFlags;
}
private static final int ACTIVITY_FLAGS = ActivityFlags.REQUEST_ENDED_CALLBACK;
public SipResourceAdaptor() {
// Those values are defualt
this.port = 5060;
// this.transport = "udp";
allowedTransports.add("udp");
allowedTransports.add("tcp");
transports.add("udp");
// this.stackAddress = "127.0.0.1";
// this.stackPrefix = "gov.nist";
}
// XXX -- SipListenerMethods - here we process incoming data
public void processIOException(IOExceptionEvent arg0) {
// TODO Auto-generated method stub
}
public void processRequest(RequestEvent req) {
if (tracer.isInfoEnabled()) {
tracer.info("Received Request:\n"+req.getRequest());
}
ServerTransaction st = req.getServerTransaction();
ServerTransactionWrapper stw = null;
// CANCEL, ACK and BYE have Transaction created for them
if (st == null || st.getApplicationData() == null) {
try {
if (req.getDialog() == null && req.getRequest().getMethod().equals(Request.ACK)) {
// create fake ack server transaction
st = new ACKDummyTransaction(req.getRequest());
}
stw = (ServerTransactionWrapper) this.providerProxy
.getNewServerTransaction(req.getRequest(), st, false);
st = (ServerTransaction) stw.getWrappedTransaction();
if (tracer.isFineEnabled()) {
tracer.fine("\n----------------- CREATED NEW STx ---------------------\nBRANCH: "
+ st.getBranchId()
+ "\n-------------------------------------------------------");
}
}
catch (TransactionAlreadyExistsException e) {
if (tracer.isFineEnabled()) {
tracer.fine(
"Request where the server tx already exists, should be a retransmission and will be dropped. Request: \n"
+ req.getRequest()
+ "\n-------------------------", e);
}
return;
}
catch (Exception e) {
tracer.severe(
"\n-------------------------\nREQUEST:\n-------------------------\n"
+ req.getRequest()
+ "\n-------------------------", e);
sendErrorResponse(st, req.getRequest(),
Response.SERVER_INTERNAL_ERROR, e.getMessage());
return;
}
} else {
// TODO:add error send response on type cast exctption
stw = (ServerTransactionWrapper) st.getApplicationData();
}
if (req.getRequest().getMethod().equals(Request.CANCEL)) {
processCancelRequest(st, stw, req);
} else {
processNotCancelRequest(st, stw, req);
}
}
private void processCancelRequest(ServerTransaction st,
ServerTransactionWrapper STW, RequestEvent req) {
SipActivityHandle inviteHandle = STW.getInviteHandle();
ServerTransactionWrapper inviteSTW = (ServerTransactionWrapper) getActivity(inviteHandle);
CancelRequestEvent REW = null;
boolean inDialog = false;
SipActivityHandle SAH = null;
// FIXME: There is no mentioen about state in specs...
if (inviteSTW != null) {
if (tracer.isFineEnabled()) {
tracer.fine("Found INVITE transaction CANCEL[" + STW + "] \nINVITE["
+ inviteSTW + "]");
}
if ((inviteSTW.getState() == TransactionState.TERMINATED)
|| (inviteSTW.getState() == TransactionState.COMPLETED)
|| (inviteSTW.getState() == TransactionState.CONFIRMED)) {
tracer.severe("Invite transaction has been found in state other than proceeding("+inviteSTW.getState()+"), final response sent, sending BAD_REQUEST");
// FINAL
// FINAL RESPONSE
// HAS
// BEEN
// SENT?
Response response;
try {
response = this.providerProxy.getMessageFactory()
.createResponse(Response.BAD_REQUEST,
req.getRequest());
STW.sendResponse(response);
} catch (Throwable e) {
tracer.severe(e.getMessage(),e);
}
return;
}
if (inviteSTW.getDialog() != null) {
if (tracer.isFineEnabled()) {
tracer.fine("Found DIALOG transaction CANCEL["
+ STW
+ "]\nINVITE["
+ inviteSTW
+ "]\nDialog["
+ inviteSTW.getDialog()
+ "]\nSEQUENCE:Send200ToCANCEL,FireEventOnDialog,Send487ToInvite");
}
SAH = ((DialogWrapper) inviteSTW.getDialog())
.getActivityHandle();
inDialog = true;
REW = new CancelRequestEvent(this.providerProxy, STW,
inviteSTW, inviteSTW != null ? inviteSTW.getDialog()
: null, req.getRequest());
// RequestEventWrapper REW = new RequestEventWrapper(
// this.providerProxy, STW, inviteSTW != null ? inviteSTW
// .getDialog() : null, req.getRequest());
} else {
if (tracer.isFineEnabled()) {
tracer.fine("DIALOG not found transaction CANCEL[" + STW
+ "]\nINVITE[" + inviteSTW + "]\nDialog["
+ inviteSTW.getDialog()
+ "]\nSEQUENCE:FireEventOnInvite");
}
SAH = inviteSTW.getActivityHandle();
inDialog = false;
REW = new CancelRequestEvent(this.providerProxy, STW,
inviteSTW, inviteSTW != null ? inviteSTW.getDialog()
: null, req.getRequest());
}
} else {
if (tracer.isFineEnabled()) {
tracer.fine("INVITE not found transaction CANCEL[" + STW
+ "]\nSEQUENCE:FireEventOnCancel");
}
SAH = STW.getActivityHandle();
inDialog = false;
REW = new CancelRequestEvent(this.providerProxy, STW, inviteSTW,
inviteSTW != null ? inviteSTW.getDialog() : null, req
.getRequest());
}
FireableEventType eventID = eventIdCache.getEventId(raContext.getEventLookupFacility(), REW.getRequest(),
inDialog);
fireEvent(REW, SAH, eventID, new Address(AddressPlan.SIP,
((ToHeader) req.getRequest().getHeader(ToHeader.NAME))
.getAddress().toString()),false);
}
private void processNotCancelRequest(ServerTransaction st,
ServerTransactionWrapper STW, RequestEvent req) {
// WE HAVE SET UP ALL WHAT WE NEED, NOW DO SOMETHING
SipActivityHandle SAH = null;
DialogWrapper DW = null;
boolean inDialog = false;
final SleeTransactionManager txManager = raContext.getSleeTransactionManager();
boolean terminateTx = false;
try {
txManager.begin();
terminateTx = true;
if (st.getDialog() != null) {
// TODO: add check for fork?
Dialog d = st.getDialog();
if (d.getApplicationData() != null
&& d.getApplicationData() instanceof DialogActivity) {
DW = (DialogWrapper) d.getApplicationData();
inDialog = true;
SAH = DW.getActivityHandle();
DW.addOngoingTransaction(STW);
} else {
if (tracer.isFineEnabled()) {
tracer
.fine("Dialog ["
+ d
+ "] exists, but no wrapper is present. Delivering event on TX");
}
inDialog = false;
SAH = STW.getActivityHandle();
}
} else {
// This means that ST is activity
SAH = STW.getActivityHandle();
if (activities.get(SAH) == null) {
if (!addActivity(SAH, STW)) {
sendErrorResponse(req.getServerTransaction(), req.getRequest(),
Response.SERVER_INTERNAL_ERROR,
"Failed to deliver request event to JAIN SLEE container");
terminateTx = false;
txManager.rollback();
return;
}
}
}
RequestEventWrapper REW = new RequestEventWrapper(this.providerProxy,
STW, DW, req.getRequest());
if (!fireEvent(REW, SAH, eventIdCache.getEventId(raContext.getEventLookupFacility(), REW
.getRequest(), inDialog), new Address(AddressPlan.SIP,
((ToHeader) req.getRequest().getHeader(ToHeader.NAME))
.getAddress().toString()),true)) {
if (!inDialog) {
// since the activity would be created only in slee with a sucessfull
// event firing then remove it from the map (was added by provider) here
activities.remove(STW.getActivityHandle());
STW.cleanup();
}
sendErrorResponse(req.getServerTransaction(), req.getRequest(),
Response.SERVER_INTERNAL_ERROR,
"Failed to deliver request event to JAIN SLEE container");
terminateTx = false;
txManager.rollback();
return;
}
else if (!inDialog && STW.getWrappedTransaction() instanceof ACKDummyTransaction) {
processTransactionTerminated(new TransactionTerminatedEventWrapper(this.providerProxy,
(ServerTransaction) STW.getWrappedTransaction()));
}
terminateTx = false;
txManager.commit();
}
catch (Throwable e) {
tracer.severe(e.getMessage(),e);
if (terminateTx) {
try {
txManager.rollback();
}
catch (Throwable f) {
tracer.severe(f.getMessage(),f);
}
}
}
}
public void processResponse(ResponseEvent resp) {
try{
if (tracer.isInfoEnabled()) {
tracer.info("Received Response:\n"+resp.getResponse());
}
ClientTransaction ct = resp.getClientTransaction();
if (ct == null) {
processLateResponse(resp);
return;
}
final int statusCode = resp.getResponse().getStatusCode();
final String method = ((CSeqHeader) resp.getResponse().getHeader(
CSeqHeader.NAME)).getMethod();
if (ct.getApplicationData() == null
|| !(ct.getApplicationData() instanceof ClientTransactionWrapper)) {
// ERROR?
tracer.severe("Received app data[" + ct.getApplicationData()
+ "] - should be instance of wrapper class!!");
// TODO: Send SERVER_ERROR ?
return;
}
// WE HAVE SET UP ALL WHAT WE NEED, NOW DO SOMETHING
// ComponentKey eventKey = null;
SipActivityHandle SAH = null;
ClientTransactionWrapper CTW = (ClientTransactionWrapper) ct.getApplicationData();
DialogWrapper DW = null;
boolean inDialog = false;
if (ct.getDialog() != null) {
Dialog d = ct.getDialog();
if (d.getApplicationData() != null && d.getApplicationData() instanceof DialogActivity) {
DW = (DialogWrapper) d.getApplicationData();
SAH = DW.getActivityHandle();
inDialog = true;
} else {
if (tracer.isFineEnabled()) {
tracer.fine("Dialog [" + d + "] exists, but no wrapper is present. Delivering event on TX");
}
inDialog = false;
SAH = CTW.getActivityHandle();
}
} else {
SAH = CTW.getActivityHandle();
}
//DW.processIncomingResponse - it fires messages in some special casses.
if (inDialog && DW.processIncomingResponse(resp)) {
} else {
ResponseEventWrapper REW = new ResponseEventWrapper(this.providerProxy, CTW, DW, resp.getResponse());
FireableEventType eventID = eventIdCache.getEventId(raContext.getEventLookupFacility(), REW.getResponse());
fireEvent(REW, SAH, eventID, new Address(AddressPlan.SIP, ((FromHeader) resp.getResponse().getHeader(FromHeader.NAME)).getAddress().toString()),false);
}
if ((statusCode == 481 || statusCode == 408) && inDialog && (!method.equals(Request.INVITE) || !method.equals(Request.SUBSCRIBE))) {
try {
Request bye = DW.createRequest(Request.BYE);
this.provider.sendRequest(bye);
} catch (SipException e) {
e.printStackTrace();
}
}
} catch (Exception e) {
e.printStackTrace();
}
//if (statusCode > 299 && DW.getState() != DialogState.CONFIRMED) {
// try {
//
// DW.delete();
// } catch (Exception e) {
// e.printStackTrace();
// }
//}
}
private void processLateResponse(ResponseEvent resp)
{
int _statusCode = resp.getResponse().getStatusCode();
String callId, _method, branchId;
callId = ((CallID) resp.getResponse().getHeader(CallID.NAME)).getCallId();
_method = ((CSeq) resp.getResponse().getHeader(CSeq.NAME)).getMethod();
branchId = ((Via) resp.getResponse().getHeaders(Via.NAME).next()).getBranch();
String toTag = ((ToHeader) resp.getResponse().getHeader(ToHeader.NAME)).getTag();
if (tracer.isInfoEnabled()) {
tracer.info("ClientTransaction is null posible late 2xx. ToTag[" + toTag + "] Dialog[" + resp.getDialog() + "] CALLID[" + callId + "] BRANCH[" + branchId
+ "] METHOD[" + _method + "] CODE[" + _statusCode + "]");
}
if ((DialogWrapper.dialogCreatingMethods.contains(_method))) {
SipActivityHandle forkMasterDialogHandle = getMasterHandleForCall(resp.getResponse());
if (forkMasterDialogHandle != null) {
DialogWrapper dw = (DialogWrapper) getActivity(forkMasterDialogHandle);
if (dw != null) {
dw.processIncomingResponse(resp);
if (tracer.isFineEnabled()) {
tracer.fine("Message was late 2xx, dialog wrapper processed it. CALLID[" + callId + "] BRANCH[" + branchId + "] METHOD[" + _method + "] CODE["
+ _statusCode + "]");
}
} else {
if (tracer.isFineEnabled()) {
tracer.fine("No master dialog wrapper, using default. CALLID[" + callId + "] BRANCH[" + branchId + "] METHOD[" + _method + "] CODE[" + _statusCode
+ "]");
}
new DialogWrapper(providerProxy, this).doTerminateOnLate2xx(resp);
}
} else {
if (tracer.isFineEnabled()) {
tracer.fine("No Handle for dialog with such from and callId, using default. CALLID[" + callId + "] BRANCH[" + branchId + "] METHOD[" + _method
+ "] CODE[" + _statusCode + "]");
}
new DialogWrapper(providerProxy, this).doTerminateOnLate2xx(resp);
}
} else {
if (tracer.isFineEnabled()) {
tracer.fine("===> ClientTransaction is NULL, along with dialog - RTR ? CALLID[" + callId + "] BRANCH[" + branchId + "] METHOD[" + _method + "] CODE["
+ _statusCode + "]");
}
// FIXME:, add default termiantion of late 2xx??
}
return;
}
public void processTimeout(TimeoutEvent arg0) {
Transaction t = null;
boolean dialogPresent = false;
SipActivityHandle handleToFire = null;
TimeoutEventWrapper tew = null;
//int eventID = -1;
String address = null;
boolean isServerTransaction = false;
DialogWrapper dw = null;
String method = null;
try {
if (tracer.isInfoEnabled()) {
if (arg0.isServerTransaction()) {
tracer.info("Server transaction "
+ arg0.getServerTransaction().getBranchId()
+ " timer expired");
} else {
tracer.info("Client transaction "
+ arg0.getClientTransaction().getBranchId()
+ " timer expired");
}
}
if (!arg0.isServerTransaction()) {
t = arg0.getClientTransaction();
if (t.getApplicationData() == null
|| !(t.getApplicationData() instanceof ClientTransactionWrapper)) {
tracer
.severe("FAILURE on processTimeout - CTX. Wrong app data["
+ t.getApplicationData() + "]");
return;
} else {
tew = new TimeoutEventWrapper(this.providerProxy,
(ClientTransaction) t.getApplicationData(), arg0
.getTimeout());
ClientTransactionWrapper ctw = (ClientTransactionWrapper) t
.getApplicationData();
// method = ctw.getRequest().getMethod();
if (ctw.getDialog() != null
&& ctw.getDialog() instanceof DialogWrapper) {
dialogPresent = true;
// handleToFire = ((DialogWrapper)
// ctw.getDialog()).getActivityHandle();
dw = (DialogWrapper) ctw.getDialog();
} else {
handleToFire = ctw.getActivityHandle();
}
}
} else {
isServerTransaction = true;
t = arg0.getServerTransaction();
ServerTransactionWrapper stw = (ServerTransactionWrapper) t
.getApplicationData();
if (stw == null) {
tracer
.severe("FAILURE on processTimeout - STX. Wrong app data["
+ t.getApplicationData() + "]");
return;
}
// method = stw.getRequest().getMethod();
if ((stw.getDialog()) != null
&& stw.getDialog() instanceof DialogWrapper) {
dialogPresent = true;
// handleToFire = ((DialogWrapper)
// stw.getDialog()).getActivityHandle();
dw = (DialogWrapper) stw.getDialog();
}
}
if (dialogPresent) {
if (!isServerTransaction) {
handleToFire = dw.getActivityHandle();
}
}
method = t.getRequest().getMethod();
address = ((FromHeader) t.getRequest().getHeader(FromHeader.NAME))
.getAddress().toString();
} catch(RuntimeException re){
re.printStackTrace();
throw re;
}finally {
// NOTE: Dialog gets terminated if transaction is dialog creating
if (handleToFire != null)
fireEvent(tew, handleToFire, eventIdCache.getTransactionTimeoutEventId(
raContext.getEventLookupFacility(), dialogPresent), new Address(
AddressPlan.SIP, address),false);
//Stack will fire TXTerminated
//if (wsi != null)
// sendActivityEndEvent(wsi.getActivityHandle());
if (method!=null && method.equals(Request.BYE) && dw != null) {
dw.delete();
}
}
}
public void processTransactionTerminated(
TransactionTerminatedEvent txTerminatedEvent) {
Transaction t = null;
if (!txTerminatedEvent.isServerTransaction()) {
t = txTerminatedEvent.getClientTransaction();
} else {
t = txTerminatedEvent.getServerTransaction();
}
if (tracer.isInfoEnabled()) {
tracer.info("SIP Transaction "+t.getBranchId()+" terminated");
}
//HACK FOR ACK.......
if(t.getApplicationData()==null && t.getRequest().getMethod().equals(Request.ACK))
{
SipActivityHandle _sah=new SipActivityHandle(t.getBranchId()+"_"+Request.ACK);
t.setApplicationData(activities.get(_sah));
}
if (t.getApplicationData() != null) {
WrapperSuperInterface wsi = (WrapperSuperInterface) t
.getApplicationData();
DialogWrapper dw = null;
if (t.getDialog() != null
&& t.getDialog().getApplicationData() instanceof DialogWrapper) {
dw = (DialogWrapper) t.getDialog().getApplicationData();
if (wsi instanceof ServerTransactionWrapper) {
dw.removeOngoingTransaction((ServerTransactionWrapper) wsi);
} else if (wsi instanceof ClientTransactionWrapper) {
dw.removeOngoingTransaction((ClientTransactionWrapper) wsi);
} else {
tracer
.severe("Unknown type "
+ wsi.getClass()
+ " of SIP Transaction, can't remove from dialog wrapper");
}
}
if (!this.sendActivityEndEvent(wsi.getActivityHandle())) {
// not a tx activity, cleanup references now
wsi.cleanup();
}
} else {
if (tracer.isInfoEnabled()) {
tracer
.info("TransactionTerminatedEvent dropped. There is no activity for transaction = "
+ t.getBranchId()
+ " , request method = "
+ t.getRequest().getMethod());
}
}
}
public void processDialogTerminated(DialogTerminatedEvent dte) {
DialogWrapper dw = null;
if (dte.getDialog() instanceof DialogWrapper) {
dw = (DialogWrapper) dte.getDialog();
} else if (dte.getDialog().getApplicationData() != null) {
dw = (DialogWrapper) dte.getDialog().getApplicationData();
}
if (tracer.isInfoEnabled()) {
if (dw != null)
tracer.info("SIP Dialog " + dw.getActivityHandle() + " terminated");
}
if (dw != null) {
if (dw.getActivityHandle() == null) {
tracer.severe(" FAILED: CLEANED DIALOG:" + dw);
}
this.sendActivityEndEvent(dw.getActivityHandle());
} else {
if (tracer.isFineEnabled()) {
tracer.fine("DialogTerminatedEvent droping due to null app data.");
}
}
}
// *************** Event Life cycle
/**
*
*/
public boolean sendActivityEndEvent(SipActivityHandle ah) {
try {
if (this.activities.containsKey(ah)) {
this.raContext.getSleeEndpoint().endActivity(ah);
return true;
}
} catch (Exception e) {
tracer.severe(e.getMessage(),e);
//This could be called when TX times out, we could call this twice, but we dont
}
return false;
}
public boolean addActivity(SipActivityHandle sah,
WrapperSuperInterface wrapperActivity) {
if (tracer.isFineEnabled()) {
tracer.fine("Adding sip activity handle " + sah);
}
try {
raContext.getSleeEndpoint().startActivityTransacted(sah,wrapperActivity,ACTIVITY_FLAGS);
}
catch (Throwable e) {
tracer.severe(e.getMessage(),e);
return false;
}
activities.put(sah, wrapperActivity);
return true;
}
public void removeActivity(SipActivityHandle sah) {
if (tracer.isFineEnabled()) {
tracer.fine("Removing sip activity handle " + sah);
}
this.activities.remove(sah);
}
// ------- END OF PROVISIONING
// *********************** DEBUG PART]
/*
private Timer debugTimer = new Timer();
private org.apache.log4j.Logger debugLogger = org.apache.log4j.Logger.getLogger("org.mobicents.slee.resource.sip.DEBUG");
private HashMap receivedEvents = new HashMap();
private ArrayList orderOfEvent = new ArrayList();
private HashMap timeStamps = new HashMap();
private class EventTimerTask extends TimerTask {
int runCount = 0;
@Override
public void run() {
debugLogger.info("-------------------- DEUMP RUN[" + runCount + "]-----------------");
debugLogger.info("[" + runCount + "] ACTIVITIES DUMP");
TreeMap ac = new TreeMap(activities);
int count = 0;
for (Object key : ac.keySet()) {
debugLogger.info("[" + runCount + "] AC[" + count++
+ "] KEY[" + key + "] A[" + ac.get(key) + "]");
}
debugLogger.info("[" + runCount + "] --- EVENTS RECEVIED");
ArrayList orderCopy = new ArrayList(orderOfEvent);
count = 0;
for (Object event : orderCopy) {
debugLogger.info("[" + runCount + "] EVENT[" + count++ + "] E[" + event + "] STAMP[" + timeStamps.get(event) + "] A[" + receivedEvents.get(event) + "]");
}
debugLogger.info("----- "+Arrays.toString(fromTagCallId2Handle.keySet().toArray())+" -----");
debugLogger.info("[" + runCount + "] ================================================");
runCount++;
}
}
private void initDebug() {
debugTimer.scheduleAtFixedRate(new EventTimerTask(), 5000, 5000);
}
private void tearDownDebug() {
debugTimer.cancel();
}
*/
public boolean fireEvent(Object event, ActivityHandle handle, FireableEventType eventID, Address address, boolean useFiltering, boolean transacted) {
if (useFiltering && eventIDFilter.filterEvent(eventID)) {
if (tracer.isFineEnabled()) {
tracer.fine("Event " + eventID + " filtered");
}
} else if (eventID == null) {
tracer.severe("Event id for " + eventID + " is unknown, cant fire!!!");
} else {
if (tracer.isFineEnabled()) {
tracer.fine("Firing event " + event + " on handle " + handle);
}
try {
if (transacted){
this.raContext.getSleeEndpoint().fireEventTransacted(handle, eventID, event, address, null, EVENT_FLAGS);
}
else {
this.raContext.getSleeEndpoint().fireEvent(handle, eventID, event, address, null, EVENT_FLAGS);
}
return true;
} catch (Exception e) {
tracer.severe("Error firing event.", e);
}
}
return false;
}
public boolean fireEvent(Object event, ActivityHandle handle, FireableEventType eventId, Address address, boolean transacted) {
return this.fireEvent(event, handle, eventId, address, true, transacted);
}
// --- XXX - error responses to be a good citizen
private void sendErrorResponse(ServerTransaction serverTransaction,
Request request, int code, String msg) {
if (!request.getMethod().equals(Request.ACK)) {
try {
ContentTypeHeader contentType = this.providerProxy
.getHeaderFactory().createContentTypeHeader("text",
"plain");
Response response = providerProxy.getMessageFactory()
.createResponse(code, request, contentType,
msg.getBytes());
if (serverTransaction != null) {
serverTransaction.sendResponse(response);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void addClientDialogMaping(String key, SipActivityHandle handle) {
this.fromTagCallId2Handle.put(key, handle);
}
public void removeClientDialogMapping(String key) {
this.fromTagCallId2Handle.remove(key);
}
public SipActivityHandle getMasterHandleForCall(Response msg) {
String key = ((FromHeader) msg.getHeader(FromHeader.NAME)).getTag() + "_" + ((CallIdHeader) msg.getHeader(CallIdHeader.NAME)).getCallId();
SipActivityHandle forkMasterDialogHandle = fromTagCallId2Handle.get(key);
return forkMasterDialogHandle;
}
// LIFECYLE
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#raActive()
*/
public void raActive() {
try {
// this.mux = this.registerMultiplexer();
Properties properties = new Properties();
// load properties for the stack
properties.load(getClass().getResourceAsStream("sipra.properties"));
// now load config properties
properties.setProperty(SIP_BIND_ADDRESS, this.stackAddress);
properties.setProperty(STACK_NAME_BIND, this.stackName);
properties.setProperty(TRANSPORTS_BIND, transportsProperty);
properties.setProperty(SIP_PORT_BIND, Integer.toString(this.port));
this.sipFactory = SipFactory.getInstance();
this.sipFactory.setPathName("gov.nist"); // hmmm
this.sipStack = this.sipFactory.createSipStack(properties);
this.sipStack.start();
activities = new ConcurrentHashMap<SipActivityHandle, WrapperSuperInterface>();
fromTagCallId2Handle = new ConcurrentHashMap<String, SipActivityHandle>();
boolean created = false;
if (tracer.isFineEnabled()) {
tracer
.fine("---> START "
+ Arrays.toString(transports.toArray()));
}
for (String trans : transports) {
ListeningPoint lp = this.sipStack.createListeningPoint(
this.stackAddress, this.port, trans);
if (!created) {
this.provider = this.sipStack.createSipProvider(lp);
// this.provider
// .setAutomaticDialogSupportEnabled(automaticDialogSupport);
created = true;
} else
this.provider.addListeningPoint(lp);
this.provider.addSipListener(this);
}
// LETS CREATE FP
// SipFactory sipFactory = SipFactory.getInstance();
AddressFactory addressFactory = sipFactory.createAddressFactory();
HeaderFactory headerFactory = sipFactory.createHeaderFactory();
MessageFactory messageFactory = sipFactory.createMessageFactory();
this.providerProxy = new SleeSipProviderImpl(addressFactory,
headerFactory, messageFactory, sipStack, this, provider);
} catch (Throwable ex) {
String msg = "error in initializing resource adaptor";
tracer.severe(msg, ex);
throw new RuntimeException(msg,ex);
}
if (tracer.isFineEnabled()) {
tracer.fine("Sip Resource Adaptor entity active.");
}
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#raInactive()
*/
public void raInactive() {
this.provider.removeSipListener(this);
ListeningPoint[] listeningPoints = this.provider.getListeningPoints();
for (int i = 0; i < listeningPoints.length; i++) {
ListeningPoint lp = listeningPoints[i];
for (int k = 0; k < 10; k++) {
try {
this.sipStack.deleteListeningPoint(lp);
this.sipStack.deleteSipProvider(this.provider);
break;
} catch (ObjectInUseException ex) {
tracer
.severe(
"Object in use -- retrying to delete listening point",
ex);
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
}
}
if (tracer.isFineEnabled()) {
tracer.fine("Sip Resource Adaptor entity inactive.");
}
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#raStopping()
*/
public void raStopping() {
// TODO Auto-generated method stub
}
// EVENT PROCESSING CALLBACKS
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#eventProcessingFailed(javax.slee.resource.ActivityHandle, javax.slee.resource.FireableEventType, java.lang.Object, javax.slee.Address, javax.slee.resource.ReceivableService, int, javax.slee.resource.FailureReason)
*/
public void eventProcessingFailed(ActivityHandle ah,
FireableEventType arg1, Object event, Address arg3,
ReceivableService arg4, int arg5, FailureReason arg6) {
String id = ((SipActivityHandle) ah).getID();
if (!id.endsWith(Request.CANCEL)
|| !(event instanceof CancelRequestEvent))
return;
// PROCESSING FAILED, WE HAVE TO SEND 481 response to CANCEL
try {
Response txDoesNotExistsResponse = this.providerProxy
.getMessageFactory().createResponse(
Response.CALL_OR_TRANSACTION_DOES_NOT_EXIST,
((CancelRequestEvent) event).getRequest());
ServerTransactionWrapper stw = (ServerTransactionWrapper) getActivity(ah);
// provider.sendResponse(txDoesNotExistsResponse);
stw.sendResponse(txDoesNotExistsResponse);
} catch (Exception e) {
e.printStackTrace();
}
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#eventProcessingSuccessful(javax.slee.resource.ActivityHandle, javax.slee.resource.FireableEventType, java.lang.Object, javax.slee.Address, javax.slee.resource.ReceivableService, int)
*/
public void eventProcessingSuccessful(ActivityHandle arg0,
FireableEventType arg1, Object arg2, Address arg3,
ReceivableService arg4, int arg5) {
// not used
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#eventUnreferenced(javax.slee.resource.ActivityHandle, javax.slee.resource.FireableEventType, java.lang.Object, javax.slee.Address, javax.slee.resource.ReceivableService, int)
*/
public void eventUnreferenced(ActivityHandle arg0, FireableEventType arg1,
Object arg2, Address arg3, ReceivableService arg4, int arg5) {
// TODO
}
// RA CONFIG
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#raConfigurationUpdate(javax.slee.resource.ConfigProperties)
*/
public void raConfigurationUpdate(ConfigProperties arg0) {
// this ra does not support config update while entity is active
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#raConfigure(javax.slee.resource.ConfigProperties)
*/
public void raConfigure(ConfigProperties properties) {
if (tracer.isFineEnabled()) {
tracer.fine("Configuring RA.");
}
this.stackName = "SipResourceAdaptorStack_" + (String) properties.getProperty(STACK_NAME_BIND).getValue();
this.port = (Integer) properties.getProperty(SIP_PORT_BIND).getValue();
this.stackAddress = (String) properties.getProperty(SIP_BIND_ADDRESS).getValue();
if (this.stackAddress.equals("null")) {
this.stackAddress = System.getProperty("jboss.bind.address");
}
this.transportsProperty = (String) properties.getProperty(TRANSPORTS_BIND).getValue();
for (String transport : this.transportsProperty.split(",")) {
this.transports.add(transport);
}
tracer.info("RA bound to " + this.stackName + ":" + this.port);
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#raUnconfigure()
*/
public void raUnconfigure() {
this.port = -1;
this.stackName = null;
this.stackAddress = null;
this.transports.clear();
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#raVerifyConfiguration(javax.slee.resource.ConfigProperties)
*/
public void raVerifyConfiguration(ConfigProperties properties)
throws InvalidConfigurationException {
try {
// get port
Integer port = (Integer) properties.getProperty(SIP_PORT_BIND).getValue();
// get host
String stackAddress = (String) properties.getProperty(SIP_BIND_ADDRESS).getValue();
if (stackAddress.equals("null")) {
stackAddress = System.getProperty("jboss.bind.address");
}
// try to open socket
InetSocketAddress sockAddress = new InetSocketAddress(stackAddress,
port);
new DatagramSocket(sockAddress).close();
// check transports
String transports = (String) properties.getProperty(TRANSPORTS_BIND).getValue();
String[] transportsArray = transports.split(",");
boolean validTransports = true;
if (transportsArray.length > 0) {
for (String transport : transportsArray) {
if (!allowedTransports.contains(transport.toLowerCase()))
validTransports = false;
break;
}
}
else {
validTransports = false;
}
if (!validTransports) {
throw new IllegalArgumentException(TRANSPORTS_BIND+" config property with invalid value: "+transports);
}
}
catch (Throwable e) {
throw new InvalidConfigurationException(e.getMessage(),e);
}
}
// EVENT FILTERING
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#serviceActive(javax.slee.resource.ReceivableService)
*/
public void serviceActive(ReceivableService receivableService) {
eventIDFilter.serviceActive(receivableService);
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#serviceInactive(javax.slee.resource.ReceivableService)
*/
public void serviceInactive(ReceivableService receivableService) {
eventIDFilter.serviceInactive(receivableService);
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#serviceStopping(javax.slee.resource.ReceivableService)
*/
public void serviceStopping(ReceivableService receivableService) {
eventIDFilter.serviceStopping(receivableService);
}
// RA CONTEXT
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#setResourceAdaptorContext(javax.slee.resource.ResourceAdaptorContext)
*/
public void setResourceAdaptorContext(ResourceAdaptorContext raContext) {
this.raContext = raContext;
this.tracer = raContext.getTracer("SipResourceAdaptor");
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#unsetResourceAdaptorContext()
*/
public void unsetResourceAdaptorContext() {
this.raContext = null;
}
/**
* Retrieves the RA context
*/
public ResourceAdaptorContext getRaContext() {
return raContext;
}
// ACTIVITY MANAGEMENT
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#activityEnded(javax.slee.resource.ActivityHandle)
*/
public void activityEnded(ActivityHandle handle) {
if (tracer.isFineEnabled()) {
tracer.fine("Removing activity for handle[" + handle + "] activity["
+ this.activities.get(handle) + "].");
}
WrapperSuperInterface activity = (WrapperSuperInterface) this.activities
.remove(handle);
if (activity != null) {
activity.cleanup();
}
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#administrativeRemove(javax.slee.resource.ActivityHandle)
*/
public void administrativeRemove(ActivityHandle activityHandle) {
// TODO
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#activityUnreferenced(javax.slee.resource.ActivityHandle)
*/
public void activityUnreferenced(ActivityHandle arg0) {
// not used
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#getActivity(javax.slee.resource.ActivityHandle)
*/
public Object getActivity(ActivityHandle arg0) {
if (arg0 instanceof SipActivityHandle) {
return this.activities.get(arg0);
} else {
return null;
}
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#getActivityHandle(java.lang.Object)
*/
public ActivityHandle getActivityHandle(Object activity) {
if (activity instanceof WrapperSuperInterface) {
WrapperSuperInterface wrapperSuperInterface = (WrapperSuperInterface) activity;
if (activities.containsKey(wrapperSuperInterface.getActivityHandle())) {
return wrapperSuperInterface.getActivityHandle();
}
else {
return null;
}
}
else {
return null;
}
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#queryLiveness(javax.slee.resource.ActivityHandle)
*/
public void queryLiveness(ActivityHandle arg0) {
// TODO Auto-generated method stub
}
// OTHER GETTERS
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#getResourceAdaptorInterface(java.lang.String)
*/
public Object getResourceAdaptorInterface(String raTypeSbbInterfaceclassName) {
// this ra implements a single ra type
return providerProxy;
}
/*
* (non-Javadoc)
* @see javax.slee.resource.ResourceAdaptor#getMarshaler()
*/
public Marshaler getMarshaler() {
// TODO Auto-generated method stub
return null;
}
}