This section provides application development guidelines for the JSR 309 Connector.
Consider the following guidelines when designing and implementing your application:
Note that the UNSOLICITED_OFFER_GENERATED event type handling is no longer needed when moving a leg from conference to IVR with the JSR 309 Connector.
The application must handle the UNSOLICITED_OFFER_GENERATED event type. This event type is provided in the SdpPortManagerEvent object indicating that the media server has requested a change in the SDP; therefore, the application must send re-INVITE to its user agent.
If your application does not release connector resources, you will likely lose resources on your PowerMedia XMS.
Early media is supported by the JSR 289 and is enabled in the application.
To use early media, a media stream is established by the application while the SIP call leg is in 1xx state. Therefore, media will start flowing to the SIP endpoint before the call goes to the 2xx (connected) state.
A high-level early media call flow is as follows:
From the JSR 289 (SIP side), the application waits for the INVITE with SDP offer.
The application creates a Player using JSR 309 and passes in the SDP.
The application sends the JSR 309 SDP answer back to the caller using a 183 response and using PRACK.
The application instructs the PowerMedia XMS to play an announcement when the ACK (for the PRACK) is received.
When the PowerMedia XMS has finished the announcement, it sends a BYE to the connector which signals the application.
The application sends a 487 Call Terminated response to the caller rather than a 200 OK.
The JSR 309 Connector supports redundant (active/alternate) media servers.
If the primary (active) media server becomes non-responsive, the connector software automatically routes a new invite request to the next available redundant (alternate) media server. If no media servers are available, all new calls will fail until one of the configured media servers becomes available.
If the media server fails, the application will be notified via the NETWORK_STREAM error.
You may configure one or more media servers. The first media server is considered the primary (active) one and all others are used as redundant (alternate) media servers. The mediaserver.redundancy parameter in the properties file controls this feature. For more information, see Installation and Configuration.
In order for the SIP protocol stack to function properly, you must accurately synchronize system clocks on all engine and SIP data tier servers to a common time source, to within one or two milliseconds. Differences in system clock settings can cause a number of severe issues such as:
SIP timers firing prematurely on servers with the fast clock settings.
Poor distribution of timer processing in the engine tier. For example, one engine tier server may process all expired timers, whereas other engine tier servers process no timers.
You should use a Network Time Protocol (NTP) client or daemon on each OCCAS instance and synchronize to a common NTP server.
Because the initial T1 timer value of 500 milliseconds controls the retransmission interval for INVITE request and responses, and also sets the initial values of other timers, even small differences in system clock settings can cause improper SIP protocol behavior. For example, an engine tier server with a system clock 250 milliseconds faster than other servers will process more expired timers than other engine tier servers, will cause retransmits to begin in half the allotted time, and may force messages to timeout prematurely.
Java object serialization is used to support replication of Java objects to another process. Serialization enables an application in a distributed or clustered environment to support application replication. By default serialization is turned on in the PowerMedia MSC.
To use serialization in your application, follow these instructions:
Add the transient keyword
to the JSR 309 MSControlFactory, Driver, and Listener instance attributes.
See Example A.
Note that the JSR 309 specification defines MSControlFactory, Driver,
and Listener as non-serializable; therefore, the application is required
to use the transient keyword. The application servlet that contains
these attributes must implement the Java serializable interface.
All JSR 309 Listener classes must implement the Serializable interface. See Example B.
See additional guidelines in Guidelines for Using Serialization.
Turning off serialization can improve performance. If your application does not need to participate in replication, you can disable local serialization by the container. To do so, add the following parameter to the OCCAS startup script:
-Dwlss.local.serialization=false
Following is the relevant portion of a startup script that has been modified to disable local serialization by the container.
if "%WLS_REDIRECT_LOG%"=="" (
echo Starting WLS with line:
echo %JAVA_HOME%\bin\java %JAVA_VM% %MEM_ARGS% %JAVA_OPTIONS%
-Dwlss.local.serialization=false -Dweblogic.Name=%SERVER_NAME%
-Djava.security.policy=%WL_HOME%\server\lib\weblogic.policy %PROXY_SETTINGS% %SERVER_CLASS%
%JAVA_HOME%\bin\java %JAVA_VM% %MEM_ARGS% %JAVA_OPTIONS% -Dwlss.local.serialization=false
-Dweblogic.Name=%SERVER_NAME% -Djava.security.policy=%WL_HOME%\server\lib\weblogic.policy
%PROXY_SETTINGS% %SERVER_CLASS%
) else (
echo Redirecting output from WLS window to %WLS_REDIRECT_LOG%
%JAVA_HOME%\bin\java %JAVA_VM% %MEM_ARGS% %JAVA_OPTIONS% -Dwlss.local.serialization=false
-Dweblogic.Name=%SERVER_NAME% -Djava.security.policy=%WL_HOME%\server\lib\weblogic.policy
%PROXY_SETTINGS% %SERVER_CLASS% >"%WLS_REDIRECT_LOG%" 2>&1
)
Follow these rules and guidelines for using serialization in your user application:
Avoid using instance and static variables in read and write mode because different instances may exist on different JVMs. In other words, static and transient attributes are not replicated.
All non-transient fields must be serializable (or primitive).
Always explicitly define the following in your serialized classes.
For example:
private static
final long serialVersionUID = <long integer>
You can use eclipse to generate a unique UID.
All classes in a hierarchy must be serializable. If the subclass implements serialization and the superclass does not, the subclass must take care of the serialization of the superclass attributes.
readObject implementations always start by calling the defaultReadObject() method.
The de-serialization process typically calls the readObject method. If you overwrite the readObject method, you must first call defaultReadObject() before adding any other instantiation to your object.
There is a performance cost to storing large object graphs in a Sip session or HTTP session. Large applications require using persistent sessions. Sessions must be read by the servlet whenever the object graph is used and rewritten whenever the object graph is updated.
Avoid using java.io.* because the files may not exist on all application servers. Instead use getResourceAsStream.
Avoid storing values in a ServletContext. A ServletContext is not serializable and also the different instances may exist in different JVMs.
Use SipSession setAttribute method to store the session state and use SipSession getAttribute method to restore it. Note that the setAttribute and getAttribute methods respectively lock/unlock the call state. Remember that setAttribute and getAttribute are expensive operations so use these methods judiciously.
In OCCAS the SAS.doAction() method also locks/unlocks the call state.
When using Maps to store serializable information, both keys and data must be serializable. Note the following rule:
Do not use an object reference as a key, because the object hashed ID changes between the serialization and de-serialization operations. This means that searching the Map may fail.
If your application needs to run in a distributed environment, you must add the <distributable/> tag in your application’s sip.xml and make sure that serialization is set to true (that is, it is not set to false) in the OCCAS startup script.
This example illustrates serialization best practices. The text in bold illustrates various rules and guidelines discussed in Using Serialization and Guidelines for Using Serialization.
public abstract class DlgcTest extends SipServlet implements Serializable
{
private static final long serialVersionUID = -6161452986725649032L;
//Since this value is a constant no need to serialize it; thus static is used
protected static String dlgcDriverName = "com.dialogic.dlg309";
//Note use of transient keyword: Factory, Listener, and Driver must be transient
transient protected MsControlFactory mscFactory;
transient protected DlgcSdpPortEventListener speListener;
transient protected Driver dlgcDriver =null;
private static Logger log = Logger.getLogger(DlgcTest.class);
@Override
public void init(ServletConfig cfg)
throws ServletException
{
super.init(cfg);
try
{
dlgcDriver = DriverManager.getDriver(dlgcDriverName);
mscFactory = dlgcDriver.getFactory(null);
speListener = new DlgcSdpPortEventListener();
}
catch (Exception e)
{
throw new ServletException(e);
}
}
@Override
public void doInvite(final SipServletRequest req)
throws ServletException, IOException
{
log.info("doInvite");
NetworkConnection networkConnection = null;
SipSession sipSession = req.getSession();
if (req.isInitial())
{
// We have a new call.
try
{
MediaSession mediaSession = mscFactory.createMediaSession();
networkConnection = mediaSession.createNetworkConnection(NetworkConnection.BASIC);
sipSession.setAttribute("MEDIA_SESSION", mediaSession);
sipSession.setAttribute("NETWORK_CONNECTION", networkConnection);
//Even though this is not part of the best practices section
//typically the application must store the UA SIP Session in the Media Session.
//This is done because the UA SIP Session may be required inside a given Listener.
//For example, the Listener may be required to send a Bye message to the UA.
mediaSession.setAttribute("SIP_SESSION", sipSession);
networkConnection.getSdpPortManager().addListener(speListener);
}
catch (MsControlException e)
{
req.createResponse(SipServletResponse.SC_SERVICE_UNAVAILABLE).send();
}
}
.
.
.
}
}
This example illustrates serialization best practices. The text in bold demonstrates the rule that all Listener classes must implement the Serializable interface, as discussed in Using Serialization.
private class PlayerEventListener implements MediaEventListener<PlayerEvent>, Serializable
{
private static final long serialVersionUID = -50247033407045994L;
@Override
public void onEvent(PlayerEvent event)
{
log.info("PlayerEventListener::onEvent()");
log.info(" EVENT TYPE : " + event.getEventType());
…
//This shows how the UA sip session is used in the Listener to send the bye to the phone UA
MediaSession mediaSession = event.getSource().getMediaSession();
SipSession session = (SipSession) mediaSession.getAttribute("SIP_SESSION");
log.debug("Calling BYE..Hanging Phone");
SipServletRequest request = session.createRequest("BYE");
try
{
request.send();
}
catch (IOException e)
{
e.printStackTrace();
}
.
.
.
}
}
}