Developer Guidelines

This section provides application development guidelines for the JSR 309 Connector.

General Guidelines

Consider the following guidelines when designing and implementing your application:

Early Media Guidelines

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:

Redundant Media Servers Guidelines

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.

Configuring Network Time Protocol (NTP) for Accurate SIP Timers

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:

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.

Configuring the Application Server

Using Serialization

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:

  1. 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.

  2. All JSR 309 Listener classes must implement the Serializable interface. See Example B.

See additional guidelines in Guidelines for Using Serialization.

Disabling 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
)

Guidelines for Using Serialization

Follow these rules and guidelines for using serialization in your user application:

  1. 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.

  2. All non-transient fields must be serializable (or primitive).

  3. 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.

  4. 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.

  5. readObject implementations always start by calling the defaultReadObject() method.

  6. 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.

  7. 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.

  8. Avoid using java.io.* because the files may not exist on all application servers. Instead use getResourceAsStream.

  9. Avoid storing values in a ServletContext. A ServletContext is not serializable and also the different instances may exist in different JVMs.

  10. 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.

  11. In OCCAS the SAS.doAction() method also locks/unlocks the call state.

  12. When using Maps to store serializable information, both keys and data must be serializable. Note the following rule:

Configuration for a Distributed Environment

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.

Example Code

Example A

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();
           }
        }
    .
    .
    .
      }
}

Example B

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();
      }
      .
      .
      .
   }
 }
}