Skip to main content

Too much memory and SLEE slow down

10 replies [Last post]
robertofrancesc...
Offline
Joined: 2008-08-28

Hello, I'm trying to test the performance of a simple SIP service based on a B2B architecture and I'm getting memory issues with the server when it is under Sipp generated load. The fact is that I see the free memory continously decreasing until it gets very low and server stops responding to the INVITEs. Then the cpu load jumps at the maximum and I even got hibernate errors about no new connection available.

I have one SBB receiving the INVITE request and searching for the right service to start by looking into a DB using Hibernate and a MySQL separate database. The service found by query is then started as a child SBB of the selector SBB and it manages the call until its ending.

Sipp is configured using a custom scenario and sends INVITE at a fixed rate.

If you need more details just ask me and I will post as soon as I can. Thank you!

Reply viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.
robertofrancesc...
Offline
Joined: 2008-08-28

Last night I left the test going on with the -Xloggc option for JBOSS to analyze the Garbage Collector behaviour and I found something interesting. Using GCViewer I have this output from the log file: [b]http://conan.diei.unipg.it/JSLEE/gc.jpg[/b]

After 1h40m I have the server stopping to react to incoming INVITEs and in the log I see that all the Heap memory (in this case 2GB) is used and the Full GC is started over and over again.

I have a memory leak somewhere but I'm wondering where it is because the code is almost the same as the sip-load-test-with-dialogs included in my mobicents version.... I'm going to try another version today and see if there's something to do with it.

I'll keep you updated, thanks for your support.

robertofrancesc...
Offline
Joined: 2008-08-28

I've changed the code to the one posted below and solved the memory leaks that I was experiencing before but now I'm getting errors during the load test as you can see in this post: http://forums.java.net/jive/thread.jspa?threadID=47379&tstart=0

CODE:

public abstract class CallControlSbb implements javax.slee.Sbb {

private SbbContext sbbContext = null; // This SBB's context
private SipActivityContextInterfaceFactory sipActivityContextInterfaceFactory;
private SipProvider provider;
private SipResourceAdaptorSbbInterface sipFactoryProvider;
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
private TimerFacility timerFacility;

//Utilities class used by different SBBs
private SipUtils sipUtils;

// -- EVENT HANDLERS
public void onInvite(javax.sip.RequestEvent event,
ActivityContextInterface aci) {
if (logger.isDebugEnabled())
logger.info("CallControl service: INVITE received");

//detach from server transaction
aci.detach(this.getSbbContext().getSbbLocalObject());
//first leg not created yet, this is the first INVITE
if (getFirstLegDialog() == null)
{
//get the ServerTransaction from the event
ServerTransaction serverTransaction = event.getServerTransaction();
//save the first leg dialog in a CMP field
Dialog dialog = serverTransaction.getDialog();
setFirstLegDialog(dialog);
//save the server transaction in a CMP field
setLastServerTransaction(serverTransaction);
//start the creation of the second leg
createSecondLeg(serverTransaction.getRequest());
}//else {
//TODO gestione RE-Invite
//}
}

//ACK event fired on a dialog activity
public void onAck(javax.sip.RequestEvent event,
ActivityContextInterface aci) {
Dialog dialog = (Dialog) aci.getActivity();
//forward ACK on the other leg
forwardAck(event.getRequest(), dialog);
//TIMER
if (dialog.equals(getFirstLegDialog()) && getTimerId() == null) {
//B2BUA established the call, let's start the timer
try {
//set up the timer
TimerOptions timerOptions = new TimerOptions();
timerOptions.setPreserveMissed(TimerPreserveMissed.ALL);

//get the duration of the timer from the mbean
CallControlConfigurator config = (CallControlConfigurator) ConfigurationProvider
.getCopy(CallControlConfiguratorMBean.MBEAN_NAME_PREFIX,
"configuration");
long duration = config.getDuration();

//start the timer
TimerID timerId = timerFacility.setTimer(aci, null, System.currentTimeMillis() + duration * 1000, timerOptions);
setTimerId(timerId);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

//BYE events are not routed by the Dialog AC and hence are processed below
public void onDialogTerminationRequest(javax.sip.RequestEvent event,
ActivityContextInterface aci) {
if (logger.isDebugEnabled())
logger.info("CallControl service: DialogTerminationRequest");

Dialog dialog = (Dialog) aci.getActivity();
//detach from this dialog AC
aci.detach(this.getSbbContext().getSbbLocalObject());

//stop the timer
TimerID timer = getTimerId();
if (timer != null)
timerFacility.cancelTimer(timer);

try {
//send a 200 OK response to the BYE request
ServerTransaction serverTransaction = event.getServerTransaction();
serverTransaction.sendResponse(createResponse(serverTransaction.getRequest(),200));
//send a BYE request to the other leg
Dialog peer = getPeerDialog(dialog);
Request secondLegBye = peer.createRequest(Request.BYE);
ClientTransaction secondLegCTX = provider.getNewClientTransaction(secondLegBye);
peer.sendRequest(secondLegCTX);
//detach from 2nd leg dialog
sipActivityContextInterfaceFactory.getActivityContextInterface(peer).detach(this.getSbbContext().getSbbLocalObject());
} catch (Exception ex){
ex.printStackTrace();
}
}

//Setup early dialog event
//fired when we receive or send the first provisional response on a dialog
public void onSetupEarly(javax.sip.ResponseEvent event,
ActivityContextInterface aci) {
if (logger.isDebugEnabled())
logger.info("CallControl service: SetupEarly");
Dialog dialog = (Dialog) aci.getActivity();
//forward second leg response on the first leg dialog
if (dialog.equals(getSecondLegDialog()))
forwardResponse(event.getResponse());
}

//Setup confirmed dialog event
//fired when we receive or send a 200 OK response to an INVITE on a dialog
public void onSetupConfirmed(javax.sip.ResponseEvent event,
ActivityContextInterface aci) {
if (logger.isDebugEnabled())
logger.info("CallControl service: SetupConfirmed");
Dialog dialog = (Dialog) aci.getActivity();
//forward second leg response on the first leg dialog
if (dialog.equals(getSecondLegDialog()))
forwardResponse(event.getResponse());
}

//Setup failed dialog event
public void onSetupFailed(javax.sip.ResponseEvent event,
ActivityContextInterface aci) {
if (logger.isDebugEnabled())
logger.info("CallControl service: SetupFailed ");
Dialog dialog = (Dialog) aci.getActivity();
//manage the setup failure
manageSetupFailure(event, dialog);
}

//Setup failed due to a timeout
public void onSetupTimedOut(javax.sip.TimeoutEvent event,
ActivityContextInterface aci) {
if (logger.isDebugEnabled())
logger.info("CallControl service: SetupTimedOut");
Dialog dialog = (Dialog) aci.getActivity();
//manage the setup timeout
manageSetupTimeout(dialog);
}

//TODO introdurre mbean per gestione servizio
public void onServiceStartedEvent(
javax.slee.serviceactivity.ServiceStartedEvent event,
ActivityContextInterface aci) {

if(logger.isDebugEnabled()) {
logger.debug("onServiceStartedEvent(event=" + event.toString() + ",aci="
+ aci.toString() + ")");
}

try {
Context myEnv = (Context) new InitialContext().lookup("java:comp/env");
ServiceActivity sa = ((ServiceActivityFactory) myEnv
.lookup("slee/serviceactivity/factory")).getActivity();
if (sa.equals(aci.getActivity())) {
//print that the service is ready
if (logger.isDebugEnabled())
logger.info("<<< CallControl Service started! >>>");

//start the mbean associated with this SBB
startMBeanConfigurator();
}
// don't want to receive further events on this activity
aci.detach(this.sbbContext.getSbbLocalObject());

} catch (Exception e) {
logger.error("Can't handle service started event.", e);
}
}

//Time expired event
public void onTimerEvent(TimerEvent event, ActivityContextInterface aci) {
try {
//get the two legs
Dialog firstLegDialog = getFirstLegDialog();
Dialog secondLegDialog = getSecondLegDialog();
//create two new clientTX
ClientTransaction firstLegCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(firstLegDialog.createRequest(Request.BYE));
ClientTransaction secondLegCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(secondLegDialog.createRequest(Request.BYE));
//send BYE on the first leg
firstLegDialog.sendRequest(firstLegCTX);
//send BYE on the second leg
secondLegDialog.sendRequest(secondLegCTX);
//detach from 1st leg dialog
sipActivityContextInterfaceFactory.getActivityContextInterface(firstLegDialog).detach(this.getSbbContext().getSbbLocalObject());
//detach from 2nd leg dialog
sipActivityContextInterfaceFactory.getActivityContextInterface(secondLegDialog).detach(this.getSbbContext().getSbbLocalObject());
} catch (Exception ex) {
ex.printStackTrace();
}
}

//--- END OF EVENT HANDLERS

//Create a SIP response to the given request
private Response createResponse(final Request request, int responseType) throws ParseException {
final SipURI sipUri = (SipURI) request.getRequestURI();
final SipURI sipAddress = addressFactory.createSipURI(sipUri.getUser(),
sipUri.getHost());

//create a response with the same content (SDP) as the request
Response response;
if (request.getContent() != null) {
response = messageFactory.createResponse(responseType, request, (ContentTypeHeader) request.getHeader(ContentTypeHeader.NAME), request.getContent());
} else {
response = messageFactory.createResponse(responseType, request);
}

//add our contact header to the response
response.addHeader(headerFactory.createContactHeader(addressFactory.createAddress(sipAddress)));

return response;
}

//Create the second leg from the first INVITE
private void createSecondLeg(Request firstInvite) {
//RequestURI
String code = "ABC"; //TODO parametrizzare!!
//String pgwIp = "10.10.45.31"; //TODO parametrizzare!!
String pgwIp = "192.168.1.168"; //TODO parametrizzare!!
try {
//RequestURI
SipURI originalURI = (SipURI) firstInvite.getRequestURI();
SipURI requestUri = addressFactory.createSipURI(code + originalURI.getUser(), pgwIp);

//From
FromHeader originalFrom = (FromHeader) firstInvite.getHeader(FromHeader.NAME);
FromHeader from = headerFactory.createFromHeader(originalFrom.getAddress(), sipUtils.generateTag());

//To
ToHeader originalTo = (ToHeader) firstInvite.getHeader(ToHeader.NAME);
SipURI originalToAddress = sipUtils.convertAddressToSipURI(originalTo.getAddress());
SipURI toAddress = addressFactory.createSipURI(code + originalToAddress.getUser(), pgwIp);
ToHeader to = headerFactory.createToHeader(addressFactory.createAddress(toAddress), null);

//CallID
CallIdHeader callId = provider.getNewCallId();

//Via
ArrayList viaHeadersList = new ArrayList();
ViaHeader via = sipUtils.createLocalViaHeader();
via.setBranch(sipUtils.generateBranch());
viaHeadersList.add(via);

//Cseq
long cseq = 1;
CSeqHeader cseqHeader = headerFactory.createCSeqHeader(cseq, Request.INVITE);

//MaxForwards
MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);

//ContentType and Content
ContentTypeHeader originalContentType = (ContentTypeHeader) firstInvite.getHeader(ContentTypeHeader.NAME);
ContentTypeHeader contentType = headerFactory.createContentTypeHeader(originalContentType.getContentType(), originalContentType.getContentSubType());

//INVITE request for second leg creation
Request invite = messageFactory.createRequest(requestUri, Request.INVITE, callId, cseqHeader, from, to, viaHeadersList, maxForwards, contentType, firstInvite.getContent());

//Contact
invite.addHeader(sipUtils.createLocalContactHeader());

//create the ClientTX and second leg dialog
ClientTransaction secondLegCTX = provider.getNewClientTransaction(invite);
//store the new INVITE ClientTX in a CMP field
setLastClientTransaction(secondLegCTX);
Dialog leg = (Dialog) provider.getNewDialog(secondLegCTX);
leg.terminateOnBye(true);
//attach to the dialog AC
ActivityContextInterface secondLegACI = sipActivityContextInterfaceFactory.getActivityContextInterface(leg);
secondLegACI.attach(this.getSbbContext().getSbbLocalObject());
//attach to the clientTX AC
//ActivityContextInterface secondLegCTXACI = sipActivityContextInterfaceFactory.getActivityContextInterface(secondLegCTX);
//secondLegCTXACI.attach(this.getSbbContext().getSbbLocalObject());
//store the second dialog in a CMP field
setSecondLegDialog(leg);

//send the INVITE on the second leg
secondLegCTX.sendRequest();

} catch (Exception ex) {
ex.printStackTrace();
}
}

//Forward an ACK request to the peer leg
private void forwardAck(final Request request, final Dialog dialog) {
Dialog peer = getPeerDialog(dialog);
CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
try {
peer.sendAck(peer.createAck(cseq.getSeqNumber()));
} catch (Exception ex) {
ex.printStackTrace();
}
}

//Forward a request to the peer leg
private void forwardRequest(final Request request, final Dialog peerDialog) {
try {
Request newRequest = peerDialog.createRequest(request.getMethod());

//TODO aggiungere eventuali header
//newRequest.addHeader(headerFactory.createMaxForwardsHeader(69));

//set the right request content
if (request.getContent() != null) {
newRequest.removeContent();
newRequest.removeHeader(ContentTypeHeader.NAME);
ContentTypeHeader contentType = (ContentTypeHeader) request.getHeader(ContentTypeHeader.NAME);
newRequest.setContent(request.getContent(), headerFactory.createContentTypeHeader(contentType.getContentType(), contentType.getContentSubType()));
}
//create the ClientTX
ClientTransaction peerCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(newRequest);
//send the request on the peer leg
peerDialog.sendRequest(peerCTX);
} catch(Exception ex) {
ex.printStackTrace();
}
}

//Forward a response to the peer leg
private void forwardResponse(final Response originalResponse) {
ServerTransaction stx = getLastServerTransaction();
try {
Response response = createResponse(stx.getRequest(), originalResponse.getStatusCode());
if (originalResponse.getContent() != null) {
response.removeContent();
response.removeHeader(ContentTypeHeader.NAME);
ContentTypeHeader contentType = (ContentTypeHeader) originalResponse.getHeader(ContentTypeHeader.NAME);
response.setContent(originalResponse.getContent(), headerFactory.createContentTypeHeader(contentType.getContentType(), contentType.getContentSubType()));
}
Dialog dialog = stx.getDialog();
if (dialog.getState() == null && getPeerDialog(dialog).getState() == DialogState.TERMINATED)
{
// Sending an error final response on a NULL state dialog throws an exception
// 180 response to change first dialog state to EARLY before closing it
Response fooResponse = createResponse(stx.getRequest(), 180);
stx.sendResponse(fooResponse);
}
stx.sendResponse(response);
} catch (Exception ex) {
ex.printStackTrace();
}
}

//get the peer dialog (other leg)
private Dialog getPeerDialog(Dialog dialog) {
Dialog peer;
if (getFirstLegDialog().equals(dialog))
peer = getSecondLegDialog();
else
peer = getFirstLegDialog();
return peer;
}

//get the SBB context
protected SbbContext getSbbContext() {
return sbbContext;
}

//manage the failure of a call setup
private void manageSetupFailure(final javax.sip.ResponseEvent event, final Dialog dialog) {
//stop the timer
if (getTimerId() != null)
timerFacility.cancelTimer(getTimerId());

Dialog peerDialog = getPeerDialog(dialog);
try {
//check if this is the right dialog
if (peerDialog.getState() == null || peerDialog.getState() == DialogState.EARLY) {
if (getFirstLegDialog().equals(dialog)) {
//send a new CANCEL request on 2nd leg
//create a CANCEL request
Request cancelRequest = getLastClientTransaction().createCancel();
//create the ClientTX
ClientTransaction peerCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(cancelRequest);
//send the CANCEL on the peer leg
peerCTX.sendRequest();
} else {
//forward final error response on 1st leg
forwardResponse(event.getResponse());
}
}
//detach from 1st leg dialog
sipActivityContextInterfaceFactory.getActivityContextInterface(dialog).detach(this.getSbbContext().getSbbLocalObject());
//detach from 2nd leg dialog
sipActivityContextInterfaceFactory.getActivityContextInterface(peerDialog).detach(this.getSbbContext().getSbbLocalObject());
} catch (Exception ex) {
ex.printStackTrace();
}
}

//manage the timeout of a call setup
private void manageSetupTimeout(final Dialog dialog) {
//stop the timer
if (getTimerId() != null)
timerFacility.cancelTimer(getTimerId());

Dialog peerDialog = getPeerDialog(dialog);
try {
//check if this is the right dialog
if (peerDialog.getState() == null || peerDialog.getState() == DialogState.EARLY) {
if (getFirstLegDialog().equals(dialog)) {
//send a new CANCEL request on 2nd leg
//create a CANCEL request
Request cancelRequest = getLastClientTransaction().createCancel();
//create the ClientTX
ClientTransaction peerCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(cancelRequest);
//send the CANCEL on the peer leg
peerCTX.sendRequest();
} else {
ServerTransaction currentServerTX = getLastServerTransaction();
//to avoid exceptions is necessary to delete the dialog
currentServerTX.getDialog().delete();
//send a timeout response to the 1st leg
Response response = createResponse(currentServerTX.getRequest(), 408);
currentServerTX.sendResponse(response);
}
}
//detach from 1st leg dialog
sipActivityContextInterfaceFactory.getActivityContextInterface(dialog).detach(this.getSbbContext().getSbbLocalObject());
//detach from 2nd leg dialog
sipActivityContextInterfaceFactory.getActivityContextInterface(peerDialog).detach(this.getSbbContext().getSbbLocalObject());
} catch (Exception ex) {
ex.printStackTrace();
}
}

//DEBUG method: print all the attached activity contexts
private void printAttachedActivities() {
ActivityContextInterface[] lala = getSbbContext().getActivities();
logger.info("Attached to " + lala.length + " activities!\n");
for (int i = 0; i < lala.length; i++) {
logger.info("activity " + (i+1) + ":\n");
logger.info(lala[i].getActivity().toString());
}
}

protected void startMBeanConfigurator() {
int confValue;
Context myEnv = null;

CallControlConfigurator config = new CallControlConfigurator();

try {
myEnv = (Context) new InitialContext().lookup("java:comp/env");
} catch (NamingException ne) {
logger.warn("Could not set SBB context:" + ne.getMessage());
return;
}

confValue = 0;

//TODO inserire file XML di configurazione se serve

try {
confValue = (Integer) myEnv.lookup("CallControl_duration");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

if (confValue <= 0) {
// WE DONT KNOW WHAT TO DO.... kernel panic
// LETS ALLOW DEFAULT VALUE
config.setDuration(20);
} else {
config.setDuration(confValue);
}

config.startService();
}

// -- CMP METHODS

public abstract Dialog getFirstLegDialog();

public abstract void setFirstLegDialog(Dialog dialog);

public abstract Dialog getSecondLegDialog();

public abstract void setSecondLegDialog(Dialog dialog);

public abstract ClientTransaction getLastClientTransaction();

public abstract void setLastClientTransaction(ClientTransaction ct);

public abstract ServerTransaction getLastServerTransaction();

public abstract void setLastServerTransaction(ServerTransaction st);

public abstract TimerID getTimerId();

public abstract void setTimerId(TimerID timer);

// -- END OF CMP METHODS

// -- SBB OBJECT LIFECYLE METHODS

/**
* Called when an sbb object is instantied and enters the pooled state.
*/
public void setSbbContext(SbbContext context) {
if (logger.isDebugEnabled()) {
logger.debug("setSbbContext(...)");
}
this.sbbContext = context;
try {
Context ctx = (Context)new InitialContext().lookup("java:comp/env");
sipActivityContextInterfaceFactory = (SipActivityContextInterfaceFactory)ctx.lookup("slee/resources/jainsip/1.2/acifactory");
sipFactoryProvider = (SipResourceAdaptorSbbInterface)ctx.lookup("slee/resources/jainsip/1.2/provider");
provider = sipFactoryProvider.getSipProvider();
addressFactory = sipFactoryProvider.getAddressFactory();
headerFactory = sipFactoryProvider.getHeaderFactory();
messageFactory = sipFactoryProvider.getMessageFactory();
timerFacility = (TimerFacility) ctx.lookup("slee/facilities/timer");
sipUtils = SipUtilsFactorySingleton.getInstance().getSipUtils();
} catch (NamingException e) {
e.printStackTrace();
}
}

public void unsetSbbContext() {
if (logger.isDebugEnabled()) {
logger.debug("unsetSbbContext()");
}
this.sbbContext = null;
this.sipUtils = null;
}

public void sbbCreate() throws javax.slee.CreateException {
if (logger.isDebugEnabled()) {
logger.debug("sbbCreate()");
}
}

public void sbbPostCreate() throws javax.slee.CreateException {
if (logger.isDebugEnabled()) {
logger.debug("sbbPostCreate()");
}
}

public void sbbActivate() {
if (logger.isDebugEnabled()) {
logger.debug("sbbActivate()");
}
}

public void sbbPassivate() {
if (logger.isDebugEnabled()) {
logger.debug("sbbPassivate()");
}
}

public void sbbRemove() {
if (logger.isDebugEnabled()) {
logger.debug("sbbRemove()");
}
}

public void sbbLoad() {
if (logger.isDebugEnabled()) {
logger.debug("sbbLoad()");
}
}

public void sbbStore() {
if (logger.isDebugEnabled()) {
logger.debug("sbbStore()");
}
}

public void sbbExceptionThrown(Exception exception, Object event,
ActivityContextInterface activity) {
if (logger.isDebugEnabled()) {
logger.debug("sbbExceptionThrown(exception=" + exception.toString()
+ ",event=" + event.toString() + ",activity="
+ activity.toString() + ")");
}
}

public void sbbRolledBack(RolledBackContext sbbRolledBack) {
if (logger.isDebugEnabled()) {
logger.debug("sbbRolledBack(sbbRolledBack=" + sbbRolledBack.toString()
+ ")");
}
}

private static Logger logger = Logger.getLogger(CallControlSbb.class);

}

Thank you

vola
Offline
Joined: 2008-05-20

Hi,

I encountered similar memory leak problem when doing load tests on "Call controller 2". Could you please tell me what modification you did to solve the problem?

Thanks

robertofrancesc...
Offline
Joined: 2008-08-28

Hello vola,

basically I explicitily detached all the ServerTransaction and ClientTransaction ACs from my SBB and made sure that at the end of the call the Dialogs and the Timer are detached and disabled too. But I'm experiencing different problems using a B2BUA architecture like the one I posted here: http://forums.java.net/jive/thread.jspa?threadID=47379&tstart=0

Let me know if you find something else that improves the speed and stability because I really need it now.

Thank you,
Roberto.

baranowb
Offline
Joined: 2006-01-09

which mobicents version?

robertofrancesc...
Offline
Joined: 2008-08-28

Hello, I,m using Mobicents 1.2.0.BETA3.

baranowb
Offline
Joined: 2006-01-09

Could YOu provide us with test service or JProfiler data?

robertofrancesc...
Offline
Joined: 2008-08-28

I'm not using JProfiler because I never used it before, this is the source code of the service started by the SelectorSBB after looking into a DB:

public abstract class CallControlSbb implements javax.slee.Sbb {

private SbbContext sbbContext = null; // This SBB's context
private SipActivityContextInterfaceFactory sipActivityContextInterfaceFactory;
private SipProvider provider;
private SipResourceAdaptorSbbInterface sipFactoryProvider;
private AddressFactory addressFactory;
private HeaderFactory headerFactory;
private MessageFactory messageFactory;
private TimerFacility timerFacility;

//Utilities class used by different SBBs
private SipUtils sipUtils;

// -- EVENT HANDLERS
public void onInvite(javax.sip.RequestEvent event,
ActivityContextInterface aci) {
logger.info("CallControl service: INVITE received");

//first leg not created yet, this is the first INVITE
if (getFirstLegDialog() == null)
{
//get the ServerTransaction from the event
ServerTransaction serverTransaction = event.getServerTransaction();
//save the first leg dialog in a CMP field
Dialog dialog = serverTransaction.getDialog();
setFirstLegDialog(dialog);
//save the server transaction in a CMP field
setLastServerTransaction(serverTransaction);
//start the creation of the second leg
createSecondLeg(serverTransaction.getRequest());
}//else {
//TODO gestione RE-Invite se serve
//}
}

//ACK event fired on a dialog activity
public void onAck(javax.sip.RequestEvent event,
ActivityContextInterface aci) {
Dialog dialog = (Dialog) aci.getActivity();
//forward ACK on the other leg
forwardAck(event.getRequest(), dialog);
if (dialog.equals(getFirstLegDialog())) {
//B2BUA established the call, let's start the timer
try {
//set up the timer
TimerOptions timerOptions = new TimerOptions();
timerOptions.setPreserveMissed(TimerPreserveMissed.ALL);

//get the duration of the timer from the mbean
CallControlConfigurator config = (CallControlConfigurator) ConfigurationProvider
.getCopy(CallControlConfiguratorMBean.MBEAN_NAME_PREFIX,
"configuration");
long duration = config.getDuration();

//start the timer
TimerID timerId = timerFacility.setTimer(aci, null, System.currentTimeMillis() + duration * 1000, timerOptions);
setTimerId(timerId);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

//BYE events are not routed by the Dialog AC and hence are processed below
public void onDialogTerminationRequest(javax.sip.RequestEvent event,
ActivityContextInterface aci) {
logger.info("CallControl service: DialogTerminationRequest");
Dialog dialog = (Dialog) aci.getActivity();
if (dialog.equals(getFirstLegDialog())) {
logger.info("First leg BYE request");
} else {
logger.info("Second leg BYE request");
}
//detach from this dialog AC
aci.detach(this.getSbbContext().getSbbLocalObject());

//stop the timer
timerFacility.cancelTimer(getTimerId());

try {
//send a 200 OK response to the BYE request
ServerTransaction serverTransaction = event.getServerTransaction();
serverTransaction.sendResponse(createResponse(serverTransaction.getRequest(),200));
//send a BYE request to the other leg
Dialog peer = getPeerDialog(dialog);
Request secondLegBye = peer.createRequest(Request.BYE);
ClientTransaction secondLegCTX = provider.getNewClientTransaction(secondLegBye);
peer.sendRequest(secondLegCTX);
} catch (Exception ex){
ex.printStackTrace();
}
}

//Setup early dialog event
//fired when we receive or send the first provisional response on a dialog
public void onSetupEarly(javax.sip.ResponseEvent event,
ActivityContextInterface aci) {
logger.info("CallControl service: SetupEarly");
Dialog dialog = (Dialog) aci.getActivity();
//forward second leg response on the first leg dialog
if (dialog.equals(getSecondLegDialog()))
forwardResponse(event.getResponse());
}

//Setup confirmed dialog event
//fired when we receive or send a 200 OK response to an INVITE on a dialog
public void onSetupConfirmed(javax.sip.ResponseEvent event,
ActivityContextInterface aci) {
logger.info("CallControl service: SetupConfirmed");
Dialog dialog = (Dialog) aci.getActivity();
//forward second leg response on the first leg dialog
if (dialog.equals(getSecondLegDialog()))
forwardResponse(event.getResponse());
}

//Setup failed dialog event
public void onSetupFailed(javax.sip.ResponseEvent event,
ActivityContextInterface aci) {
logger.info("CallControl service: SetupFailed ");
Dialog dialog = (Dialog) aci.getActivity();
//manage the setup failure
manageSetupFailure(event, dialog);
}

//Setup failed due to a timeout
public void onSetupTimedOut(javax.sip.TimeoutEvent event,
ActivityContextInterface aci) {
logger.info("CallControl service: SetupTimedOut");
Dialog dialog = (Dialog) aci.getActivity();
//manage the setup timeout
manageSetupTimeout(dialog);
}

//TODO introdurre mbean per gestione servizio
public void onServiceStartedEvent(
javax.slee.serviceactivity.ServiceStartedEvent event,
ActivityContextInterface aci) {

if(logger.isDebugEnabled()) {
logger.debug("onServiceStartedEvent(event=" + event.toString() + ",aci="
+ aci.toString() + ")");
}

try {
Context myEnv = (Context) new InitialContext().lookup("java:comp/env");
ServiceActivity sa = ((ServiceActivityFactory) myEnv
.lookup("slee/serviceactivity/factory")).getActivity();
if (sa.equals(aci.getActivity())) {
//print that the service is ready
logger.info("<<< CallControl Service started! >>>");

//start the mbean associated with this SBB
startMBeanConfigurator();
}
// don't want to receive further events on this activity
aci.detach(this.sbbContext.getSbbLocalObject());

} catch (Exception e) {
logger.error("Can't handle service started event.", e);
}
}

//Time expired event
public void onTimerEvent(TimerEvent event, ActivityContextInterface aci) {
try {
//get the two legs
Dialog firstLegDialog = getFirstLegDialog();
Dialog secondLegDialog = getSecondLegDialog();
//create two new clientTX
ClientTransaction firstLegCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(firstLegDialog.createRequest(Request.BYE));
ClientTransaction secondLegCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(secondLegDialog.createRequest(Request.BYE));
//send BYE on the first leg
firstLegDialog.sendRequest(firstLegCTX);
//send BYE on the second leg
secondLegDialog.sendRequest(secondLegCTX);
} catch (Exception ex) {
ex.printStackTrace();
}
}

//--- END OF EVENT HANDLERS

//Create a SIP response to the given request
private Response createResponse(final Request request, int responseType) throws ParseException {
final SipURI sipUri = (SipURI) request.getRequestURI();
final SipURI sipAddress = addressFactory.createSipURI(sipUri.getUser(),
sipUri.getHost());

//create a response with the same content (SDP) as the request
Response response;
if (request.getContent() != null) {
response = messageFactory.createResponse(responseType, request, (ContentTypeHeader) request.getHeader(ContentTypeHeader.NAME), request.getContent());
} else {
response = messageFactory.createResponse(responseType, request);
}

//add our contact header to the response
response.addHeader(headerFactory.createContactHeader(addressFactory.createAddress(sipAddress)));

return response;
}

//Create the second leg from the first INVITE
private void createSecondLeg(Request firstInvite) {
//RequestURI
String code = "ABC"; //TODO parametrizzare!!
//String pgwIp = "10.10.45.31"; //TODO parametrizzare!!
String pgwIp = "192.168.1.165"; //TODO parametrizzare!!
try {
//RequestURI
SipURI originalURI = (SipURI) firstInvite.getRequestURI();
SipURI requestUri = addressFactory.createSipURI(code + originalURI.getUser(), pgwIp);

//From
FromHeader originalFrom = (FromHeader) firstInvite.getHeader(FromHeader.NAME);
FromHeader from = headerFactory.createFromHeader(originalFrom.getAddress(), sipUtils.generateTag());

//To
ToHeader originalTo = (ToHeader) firstInvite.getHeader(ToHeader.NAME);
SipURI originalToAddress = sipUtils.convertAddressToSipURI(originalTo.getAddress());
SipURI toAddress = addressFactory.createSipURI(code + originalToAddress.getUser(), pgwIp);
ToHeader to = headerFactory.createToHeader(addressFactory.createAddress(toAddress), null);

//CallID
CallIdHeader callId = provider.getNewCallId();

//Via
ArrayList viaHeadersList = new ArrayList();
ViaHeader via = sipUtils.createLocalViaHeader();
via.setBranch(sipUtils.generateBranch());
viaHeadersList.add(via);

//Cseq
long cseq = 1;
CSeqHeader cseqHeader = headerFactory.createCSeqHeader(cseq, Request.INVITE);

//MaxForwards
MaxForwardsHeader maxForwards = headerFactory.createMaxForwardsHeader(70);

//ContentType and Content
ContentTypeHeader originalContentType = (ContentTypeHeader) firstInvite.getHeader(ContentTypeHeader.NAME);
ContentTypeHeader contentType = headerFactory.createContentTypeHeader(originalContentType.getContentType(), originalContentType.getContentSubType());

//INVITE request for second leg creation
Request invite = messageFactory.createRequest(requestUri, Request.INVITE, callId, cseqHeader, from, to, viaHeadersList, maxForwards, contentType, firstInvite.getContent());

//Contact
invite.addHeader(sipUtils.createLocalContactHeader());

//create the ClientTX and second leg dialog
ClientTransaction secondLegCTX = provider.getNewClientTransaction(invite);
//store the new INVITE ClientTX in a CMP field
setLastClientTransaction(secondLegCTX);
Dialog leg = (Dialog) provider.getNewDialog(secondLegCTX);
leg.terminateOnBye(true);
//attach to the dialog AC
ActivityContextInterface secondLegACI = sipActivityContextInterfaceFactory.getActivityContextInterface(leg);
secondLegACI.attach(this.getSbbContext().getSbbLocalObject());
//attach to the clientTX AC
ActivityContextInterface secondLegCTXACI = sipActivityContextInterfaceFactory.getActivityContextInterface(secondLegCTX);
secondLegCTXACI.attach(this.getSbbContext().getSbbLocalObject());
//store the second dialog in a CMP field
setSecondLegDialog(leg);

//send the INVITE on the second leg
secondLegCTX.sendRequest();

} catch (Exception ex) {
ex.printStackTrace();
}
}

//Forward an ACK request to the peer leg
private void forwardAck(final Request request, final Dialog dialog) {
Dialog peer = getPeerDialog(dialog);
CSeqHeader cseq = (CSeqHeader) request.getHeader(CSeqHeader.NAME);
try {
peer.sendAck(peer.createAck(cseq.getSeqNumber()));
} catch (Exception ex) {
ex.printStackTrace();
}
}

//Forward a request to the peer leg
private void forwardRequest(final Request request, final Dialog peerDialog) {
try {
Request newRequest = peerDialog.createRequest(request.getMethod());

//TODO aggiungere eventuali header
//newRequest.addHeader(headerFactory.createMaxForwardsHeader(69));

//set the right request content
if (request.getContent() != null) {
newRequest.removeContent();
newRequest.removeHeader(ContentTypeHeader.NAME);
ContentTypeHeader contentType = (ContentTypeHeader) request.getHeader(ContentTypeHeader.NAME);
newRequest.setContent(request.getContent(), headerFactory.createContentTypeHeader(contentType.getContentType(), contentType.getContentSubType()));
}
//create the ClientTX
ClientTransaction peerCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(newRequest);
//send the request on the peer leg
peerDialog.sendRequest(peerCTX);
} catch(Exception ex) {
ex.printStackTrace();
}
}

//Forward a response to the peer leg
private void forwardResponse(final Response originalResponse) {
ServerTransaction stx = getLastServerTransaction();
try {
Response response = createResponse(stx.getRequest(), originalResponse.getStatusCode());
if (originalResponse.getContent() != null) {
response.removeContent();
response.removeHeader(ContentTypeHeader.NAME);
ContentTypeHeader contentType = (ContentTypeHeader) originalResponse.getHeader(ContentTypeHeader.NAME);
response.setContent(originalResponse.getContent(), headerFactory.createContentTypeHeader(contentType.getContentType(), contentType.getContentSubType()));
}
Dialog dialog = stx.getDialog();
if (dialog.getState() == null && getPeerDialog(dialog).getState() == DialogState.TERMINATED)
{
// Sending an error final response on a NULL state dialog throws an exception
// 180 response to change first dialog state to EARLY before closing it
Response fooResponse = createResponse(stx.getRequest(), 180);
stx.sendResponse(fooResponse);
}
stx.sendResponse(response);
} catch (Exception ex) {
ex.printStackTrace();
}
}

//get the peer dialog (other leg)
private Dialog getPeerDialog(Dialog dialog) {
Dialog peer;
if (getFirstLegDialog().equals(dialog))
peer = getSecondLegDialog();
else
peer = getFirstLegDialog();
return peer;
}

//get the SBB context
protected SbbContext getSbbContext() {
return sbbContext;
}

//manage the failure of a call setup
private void manageSetupFailure(final javax.sip.ResponseEvent event, final Dialog dialog) {
Dialog peerDialog = getPeerDialog(dialog);
try {
//check if this is the right dialog
if (peerDialog.getState() == null || peerDialog.getState() == DialogState.EARLY) {
if (getFirstLegDialog().equals(dialog)) {
//send a new CANCEL request on 2nd leg
//create a CANCEL request
Request cancelRequest = getLastClientTransaction().createCancel();
//create the ClientTX
ClientTransaction peerCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(cancelRequest);
//send the CANCEL on the peer leg
peerCTX.sendRequest();
} else {
//forward final error response on 1st leg
forwardResponse(event.getResponse());
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}

//manage the timeout of a call setup
private void manageSetupTimeout(final Dialog dialog) {
Dialog peerDialog = getPeerDialog(dialog);
try {
//check if this is the right dialog
if (peerDialog.getState() == null || peerDialog.getState() == DialogState.EARLY) {
if (getFirstLegDialog().equals(dialog)) {
//send a new CANCEL request on 2nd leg
//create a CANCEL request
Request cancelRequest = getLastClientTransaction().createCancel();
//create the ClientTX
ClientTransaction peerCTX = sipFactoryProvider.getSipProvider().getNewClientTransaction(cancelRequest);
//send the CANCEL on the peer leg
peerCTX.sendRequest();
} else {
ServerTransaction currentServerTX = getLastServerTransaction();
//to avoid exceptions is necessary to delete the dialog
currentServerTX.getDialog().delete();
//send a timeout response to the 1st leg
Response response = createResponse(currentServerTX.getRequest(), 408);
currentServerTX.sendResponse(response);
}
}
} catch (Exception ex) {
ex.printStackTrace();
}
}

//DEBUG method: print all the attached activity contexts
private void printAttachedActivities() {
ActivityContextInterface[] lala = getSbbContext().getActivities();
logger.info("Attached to " + lala.length + " activities!\n");
for (int i = 0; i < lala.length; i++) {
logger.info("activity " + (i+1) + ":\n");
logger.info(lala[i].getActivity().toString());
}
}

protected void startMBeanConfigurator() {
int confValue;
Context myEnv = null;

CallControlConfigurator config = new CallControlConfigurator();

try {
myEnv = (Context) new InitialContext().lookup("java:comp/env");
} catch (NamingException ne) {
logger.warn("Could not set SBB context:" + ne.getMessage());
return;
}

confValue = 0;

//TODO inserire file XML di configurazione se serve

try {
confValue = (Integer) myEnv.lookup("CallControl_duration");
} catch (NamingException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}

if (confValue <= 0) {
// WE DONT KNOW WHAT TO DO.... kernel panic
// LETS ALLOW DEFAULT VALUE
config.setDuration(20);
} else {
config.setDuration(confValue);
}

config.startService();
}

// -- CMP METHODS

public abstract Dialog getFirstLegDialog();

public abstract void setFirstLegDialog(Dialog dialog);

public abstract Dialog getSecondLegDialog();

public abstract void setSecondLegDialog(Dialog dialog);

public abstract ClientTransaction getLastClientTransaction();

public abstract void setLastClientTransaction(ClientTransaction ct);

public abstract ServerTransaction getLastServerTransaction();

public abstract void setLastServerTransaction(ServerTransaction st);

public abstract TimerID getTimerId();

public abstract void setTimerId(TimerID timer);

// -- END OF CMP METHODS

// -- SBB OBJECT LIFECYLE METHODS

/**
* Called when an sbb object is instantied and enters the pooled state.
*/
public void setSbbContext(SbbContext context) {
if (logger.isDebugEnabled()) {
logger.debug("setSbbContext(...)");
}
this.sbbContext = context;
try {
Context ctx = (Context)new InitialContext().lookup("java:comp/env");
sipActivityContextInterfaceFactory = (SipActivityContextInterfaceFactory)ctx.lookup("slee/resources/jainsip/1.2/acifactory");
sipFactoryProvider = (SipResourceAdaptorSbbInterface)ctx.lookup("slee/resources/jainsip/1.2/provider");
provider = sipFactoryProvider.getSipProvider();
addressFactory = sipFactoryProvider.getAddressFactory();
headerFactory = sipFactoryProvider.getHeaderFactory();
messageFactory = sipFactoryProvider.getMessageFactory();
timerFacility = (TimerFacility) ctx.lookup("slee/facilities/timer");
sipUtils = SipUtilsFactorySingleton.getInstance().getSipUtils();
} catch (NamingException e) {
e.printStackTrace();
}
}

public void unsetSbbContext() {
if (logger.isDebugEnabled()) {
logger.debug("unsetSbbContext()");
}
this.sbbContext = null;
}

public void sbbCreate() throws javax.slee.CreateException {
if (logger.isDebugEnabled()) {
logger.debug("sbbCreate()");
}
}

public void sbbPostCreate() throws javax.slee.CreateException {
if (logger.isDebugEnabled()) {
logger.debug("sbbPostCreate()");
}
}

public void sbbActivate() {
if (logger.isDebugEnabled()) {
logger.debug("sbbActivate()");
}
}

public void sbbPassivate() {
if (logger.isDebugEnabled()) {
logger.debug("sbbPassivate()");
}
}

public void sbbRemove() {
if (logger.isDebugEnabled()) {
logger.debug("sbbRemove()");
}
}

public void sbbLoad() {
if (logger.isDebugEnabled()) {
logger.debug("sbbLoad()");
}
}

public void sbbStore() {
if (logger.isDebugEnabled()) {
logger.debug("sbbStore()");
}
}

public void sbbExceptionThrown(Exception exception, Object event,
ActivityContextInterface activity) {
if (logger.isDebugEnabled()) {
logger.debug("sbbExceptionThrown(exception=" + exception.toString()
+ ",event=" + event.toString() + ",activity="
+ activity.toString() + ")");
}
}

public void sbbRolledBack(RolledBackContext sbbRolledBack) {
if (logger.isDebugEnabled()) {
logger.debug("sbbRolledBack(sbbRolledBack=" + sbbRolledBack.toString()
+ ")");
}
}

private static Logger logger = Logger.getLogger(CallControlSbb.class);

eduardomartins
Offline
Joined: 2005-10-10

JAIN SLEE or SIP Servlets? If it's SLEE make sure you're not leaking activity contexts such as null activities, it's a common mistake in user load tests.

You can find our SIP load tests in /trunk/servers/jain-slee/examples/sip-load-test-* (for custom and old SIP RA) and sip11-load-test-* (for the new SIP1.1 RA). Documentation: http://groups.google.com/group/mobicents-public/web/mobicents-load-testing

robertofrancesc...
Offline
Joined: 2008-08-28

Thanks for the answer, I'm using JAIN SLEE and I start the timer under the onAck event handler as you can see below:

//ACK event fired on a dialog activity
public void onAck(javax.sip.RequestEvent event,
ActivityContextInterface aci) {
Dialog dialog = (Dialog) aci.getActivity();
//forward ACK on the other leg
forwardAck(event.getRequest(), dialog);
if (dialog.equals(getFirstLegDialog())) {
//B2BUA established the call, let's start the timer
try {
//set up the timer
TimerOptions timerOptions = new TimerOptions();
timerOptions.setPreserveMissed(TimerPreserveMissed.ALL);

//get the duration of the timer from the mbean
CallControlConfigurator config = (CallControlConfigurator) ConfigurationProvider
.getCopy(CallControlConfiguratorMBean.MBEAN_NAME_PREFIX,
"configuration");
long duration = config.getDuration();

//start the timer
TimerID timerId = timerFacility.setTimer(aci, null, System.currentTimeMillis() + duration * 1000, timerOptions);
setTimerId(timerId);
} catch (Exception ex) {
ex.printStackTrace();
}
}
}

Do you know if there are any problems regarding the Dialog activity usage for the timer event? It seemed reasonable to me to not create another activity to send timer events.

I'm looking at the load test code now to see what is wrong in mine. I'll post the results as soon as I have them (at least I hope to discover something).