7 SIP Servlet Concurrency

This chapter describes how to develop Session Initiation Protocol (SIP) Servlet applications that support concurrency in Oracle Communications Converged Application Server. It also explains ways to develop portable applications without concurrency issues.

Multiple Servlets executing simultaneously may have active access to shared resources like the SipSession and SipApplicationSession objects. These resources may also be accessed concurrently from ServletTimer objects, from other Java Platform, Enterprise Edition (Java EE) modules in a converged Java EE application, and by the SIP Servlet Container. Operations, such as updating values of session attributes, carried out by different threads on these objects can create concurrency issues, including deadlock, for some applications.

Note:

For details on the application program interface (API) described in this chapter, see the Java SIP Servlet API 2.0 JavaDocs.

Specifying Concurrency Mode

A SIP Servlet application can specify the required level of concurrency control Converged Application Server should apply while executing applications with the @SipApplication annotation or in the deployment descriptor. Converged Application Server supports two values for the ConcurrencyMode enum: VENDORSPECIFIC and APPLICATIONSESSION.

The following shows an example of a servlet specifying the concurrency mode.

Example 7-1 Setting the Concurrency Mode

package com.example;
import javax.servlet.sip.SipServlet;
@SipApplication (name = ”SampleApplication”, concurrencyMode = 
       ConcurrencyMode.APPLICATIONSESSION)

Table 7-1 lists the available concurrency modes.

Table 7-1 Concurrency Modes

Mode Description

VENDORSPECIFIC

Indicates that Converged Application Server can choose any concurrency level it deems appropriate. The level of guarantee of thread safety is determined by Converged Application Server. This is the default.

APPLICATIONSESSION

Indicates that Converged Application Server performs concurrency control at the level of the application session. It ensures that two messages belonging to the same application session are never processed simultaneously. It also ensures that various timer tasks or internal threads managed by Converged Application Server that access the application session are not executed at the same time.


Concurrency Utilities

The Concurrency Utilities for Java EE specification explains how an application can use Concurrency Utilities in an enterprise server environment. Converged Application Server uses those utilities to execute asynchronous tasks and to let you develop thread safe applications. A SIP Servlet application may use any of the Managed Objects specified "Default Managed Objects".

Propagating SipApplicationSession Context

When a SIP Servlet application specifies a concurrency control of ConcurrencyMode.APPLICATIONSESSION, Converged Application Server makes sure that appropriate concurrency control is maintained. To effectively maintain the concurrency control at the SipApplicationSession level, the active SIP application session also needs to be passed as context information.

To enable this, Converged Application Server exposes one or more ManagedExecutorService and ManagedScheduledExecutorService objects that execute submitted tasks in a thread pool managed by the Converged Application Server. The ManagedExecutorService object provides methods for submitting tasks for execution. And the ManagedScheduledExecutorService object provides methods for submitting delayed or periodic tasks for execution at specific times.

While executing the tasks, Converged Application Server makes sure that concurrency control specified by the application is maintained.

Note:

Applications should use the default managed objects as specified in "Default Managed Objects", to propagate a SIP Application Session.

When a servlet (or any other part of the application) submits a task using the default ManagedExecutorService or ManagedScheduledExecutorService, apart from the context information specified in the Concurrency Utilities for EE specification, the active application session is also passed as the context information. The active application session is responsible for the execution of the thread where the application logic is running. Some examples are the application session of the SipServletMessage that triggered servlet execution, the application session of the executing ServletTimer, the relevant application session of the application listener, and the application session of the ConvergedHttpSession of the HTTP servlet.

If the task is submitted from a thread where no application session is active, then the application can use the mechanism specified in "Specifying Application Session Programmatically" for specifying an application session as the context.

To submit a scheduled task, the servlet container launches a new thread.

Specifying Application Session Programmatically

A thread might want to submit a task with a different application session as its context than the context active when the task was submitted. In that case, the application is expected to create a contextual object proxy using ContextService for submitting the task or by using ManagedTask. The application can then specify an application session of its choosing as the context by using one of the following execution properties:

  • javax.servlet.sip.ApplicationSessionKey: Specifies the SIP application key.

  • javax.servlet.sip.ApplicationSessionId: Specifies the application session ID.

  • javax.servlet.sip.ApplicationSession.create: Indicates that the container creates a new SipApplicationSession and uses it as the context.

Applications can also access these constants from javax.servlet.sip.SipServlet fields SIP_APPLICATIONSESSION_KEY, SIP_APPLICATIONSESSION_ID, and SIP_APPLICATIONSESSION_CREATE respectively. Table 7-2 lists the actions that Converged Application server takes based on application settings.

Table 7-2 Application Settings and Actions Taken by Converged Application Server

Application Settings Resulting Converged Application Server Actions

Both javax.servlet.sip.ApplicationSessionKey and javax.servlet.sip.ApplicationSessionId are specified by the application.

javax.servlet.sip.ApplicationSessionId given precedence.

Application session (specified by either javax.servlet.sip.ApplicationSessionKey or javax.servlet.sip.ApplicationSessionId) is invalid or cannot be found.

Container aborts the execution of the task.

ManagedTaskListener.taskAborted is called throwing an AbortedException.

javax.servlet.sip.ApplicationSession.create, and javax.servlet.sip.ApplicationSessionId are specified by the application.

Discards javax.servlet.sip.ApplicationSession.create

Both javax.servlet.sip.ApplicationSession.create and javax.servlet.sip.ApplicationSessionKey are specified.

Uses the specified application session key to create the SipApplicationSession.

If Converged Application Server finds an existing application session with the specified application session key, that application session will be used as the context.

Only javax.servlet.sip.ApplicationSession.create is specified.

Creates a new SipApplicationSession and uses that as the context.


To avoid concurrency issues, applications are required to submit asynchronous tasks whenever they use an application session different from the active application session. That rule applies for all kinds of application components, such as SIP Servlets, HTTP Servlets, other Java EE components, Servlet Timers, and asynchronous tasks.

Maintaining Thread Safety with Multiple Application Session Contexts

Converged Application Server ensures that tasks are run in a thread safe manner with respect to the SIP application session specified as the context. However, an application should be careful while sharing the objects between tasks with a different application session context. The thread safety of such objects, whether it is an application-specific object or an object received from the container, must be maintained by the application. For example, objects such as SipURI, or Address should be cloned before they are shared between tasks.

ContextService

As specified in section 3.3 of the Concurrency Utilities for Java EE specification, javax.enterprise.concurrent.ContextService allows applications to create contextual objects without using a managed executor. Just like a submitted contextual task, whenever a method on the contextual object is invoked, the method executes with the thread context of the associated application component instance.

If a contextual proxy created using the default SIP context service (see "Default Managed Objects") and if that contextual proxy is executed in a thread known to Converged Application Server (for example, an HTTP Servlet thread or a MDB thread), the application session context is set properly. This application session context is based on the thread that created the contextual proxy. The application may use the execution properties mentioned in "Specifying Application Session Programmatically" to use a different application session as the context.

Note:

If the contextual proxy is executed directly on a thread that already has an active SIP application session and the contextual proxy is created with another SIP application session as the context, the container throws an IllegalStateException.

Default Managed Objects

Converged Application Server provides preconfigured, default managed objects shown in Table 7-3.

Table 7-3 Default Managed Objects

Java EE Object Converged Application Server Object

ManagedExecutorService

java:comp/ManagedSipExecutorService

ManagedScheduledExecutorService

java:comp/ManagedScheduledSipExecutorService

ContextService

java:comp/SipContextService

ManagedThreadFactory

java:comp/ManagedSipThreadFactory


The types of contexts propagated by the default objects include naming context, classloader, and security information apart from the application session.

Note:

An application must not use these default managed objects if it does not want the application session context to be propagated.

Accessing an Active Application Session

You may want to access the application session that is active on the current thread, whether it is from a thread running a submitted or scheduled task or from a thread executing the servlet.

It is possible to access SipApplicationSession using SipSessionsUtil, and the ID, or application key. Converged Application Server provides a more direct access to the active application with the SipSessionsUtil.getCurrentApplicationSession() method. This method is especially useful when the tasks are not Common Dependency Injection (CDI) managed beans where the CDI built-in beans for injecting SipApplicationSession cannot be used.

Accessing Tasks Futures

A Future represents the result of an asynchronous computation. The methods of the Future interface enable applications to check if the computation is complete, to wait for its completion, and to retrieve the result of the computation.

Your application can access the Future (java.util.concurrent.Future) objects of submitted or scheduled tasks that are not completed nor canceled. For

Important:

Converged Application Server does not support both the get methods of the ScheduledFuture interface. Applications can use the lifecycle events associated with a ManagedTaskListener.

For information on the possible task lifecycle events that can occur when a ManagedTaskListener is associated with a task, see:

https://docs.oracle.com/javaee/7/api/javax/enterprise/concurrent/ManagedTaskListener.html

Accessing the Futures of Tasks in a Sip Application Session

Applications can access the Futures of tasks belonging to a SipApplicationSession by one of the following methods:

About Saving Future Objects

Applications should not attempt to save the Future object of a scheduled task in the Sip application session. Doing so will cause the application server to throw the following error:

Example 7-2 Error Seen When Future Objects are Saved in Sip Application Sessions

java.lang.IllegalArgumentException: Attribute must be serializable for distributable application

Concurrency Examples

Example 7-3 shows submitting a task with an active SipApplication context.

Example 7-3 Task with Active SipApplication Context

@SipServlet
public class ExamplePOJO1{
  @Resource(lookup=”java:comp/ManagedSipExecutorService”)
  ManagedExecutorService mes;
  @Invite
  protected void onInvite(SipServletRequest req) {
    // Create a task instance. MySipTask implements Callable...
    MySipTask sipTask = new MySipTask();
    // Submit the task to the ManagedExecutorService...
    Future sipFuture = mes.submit(sipTask);
  }
}

Example 7-4 shows submitting a task with a different SipApplicationSession context.

Example 7-4 Task with a Different SipApplication Context

@SipServlet
public class ExamplePOJO2{
  @Resource(lookup=”java:comp/ManagedScheduledSipExecutorService”)
  ManagedScheduledExecutorService mses;
  @Resource(lookup=”java:comp/SipContextService”)
  ContextService ctxSvc;
  @Invite
  protected void onInvite(SipServletRequest req) {
    SipApplicationSession sas = //...lookup using SipSessionsUtil
    // Set any custom context data through execution properties...
    Map<String, String> execProps = new HashMap<>();
    execProps.put("javax.servlet.sip.ApplicationSessionId", sas.getId());
    // Create a task instance. MySipTask implements Callable...
    MySipTask sipTask = new MySipTask();
    Callable task = ctxSvc.createContextualProxy
    (sipTask, execProps, Callable.class);
    // Submit the task to the ManagedScheduledExecutorService...
    Future sipFuture = mses.submit(task);
  }
}

Example 7-5 shows a more complete TaskHandler class.

Example 7-5 TaskHandler Class

@Dependent
public class TaskHandler {
 
  @Resource(lookup = "java:comp/SipContextService")
  ContextService sipCS;
 
  @Resource(lookup = "java:comp/ManagedSipExecutorService")
  ManagedExecutorService sipMES;
 
  @Inject
  SipSessionsUtil ssu;
 
  public void doIt(final String sasId, final String appState) {
    Map<String, String> props = new HashMap<>();
    props.put(SipServlet.SIP_APPLICATIONSESSION_ID, sasId);
 
    final SipSessionsUtil util = ssu;
    Runnable task = () -> {
      final SipApplicationSession session =
        util.getCurrentApplicationSession();
      session.setAttribute("counter", 1);
      session.setAttribute("appState", appState);
    };
 
    task = sipCS.createContextualProxy(task, props, Runnable.class);
    task.run();
  }
 
  public void doAsync(final String sasId, final String appState) {
    Map<String, String> props = new HashMap<>();
    props.put(SipServlet.SIP_APPLICATIONSESSION_ID, sasId);
 
    final SipSessionsUtil util = ssu;
 
    Runnable task = (Runnable & Serializable) () -> {
      final SipApplicationSession session =
        util.getCurrentApplicationSession();
      int counter = (int) session.getAttribute("counter");
      session.setAttribute("counter", ++counter);
      session.setAttribute("appState", appState);
    };
 
    task = (Runnable) sipCS.createContextualProxy
      (task, props, Runnable.class, Serializable.class);
    sipMES.submit(task);
  }