This chapter describes creating Session Initiation Protocol (SIP) servlets using Java Enterprise Edition (EE) annotations in conjunction with Plain Old Java Objects (POJOs).
SIP Servlet POJOs are SIP Servlets that do not extend from generic Servlets defined by the GenericServlet interface. They are simple POJOs annotated with an @SipServlet annotation. These POJOs contain annotated methods invoked by Converged Application Server when a SIP message arrives.
Any Java class that is annotated with @SipServlet, but does not extend from javax.servlet.sip.Servlet, is a SIP Servlet POJO. SIP Servlet POJOs support all elements of the @SipServlet annotation. The SIP Container treats POJOs similar to a component class listed in Table EE.5-1 of Java EE specification. These classes act as a Common Dependency Injection (CDI) managed bean and hence support all CDI capabilities explained in the Java EE CDI specification.
SIP Servlet POJOs are instantiated by the procedure to create a non-contextual instance that is not managed by the CDI container. The exact procedure is explained at the end of section titled ”Support for Dependency Injection” in the Java EE specification.The POJO life-cycle closely follows those of other component classes in terms of instantiation and destruction. Thus, shortly after resource and CDI injection completes successfully, the @PostConstruct callback is invoked. Similarly, the @PreDestroy annotation may be applied to one method that is called when the class is taken out of service and is longer be used by the container. Otherwise, the load-on-startup behavior of SIP Servlets applies to SIP Servlet POJOs as well. The behavior can be specified either in the @SipServlet annotation or in the deployment descriptor.
A SIP Servlet POJO uses annotated methods to handle SIP Messages. Any Java method annotated with one or more annotations that carry SIP meta-annotations described in this section is used by the container to deliver messages to the application. SIP servlet containers do not depend on individual annotations that use these meta annotations enabling further extensibility as explained in "Extensibility Using SIP Meta-Annotations".
The @SipMethod annotation associates the name of a SIP method with an annotation. A Java method annotated with a runtime annotation that is itself annotated with SipMethod handles SIP requests or responses with the indicated SIP method. The value of the annotation specifies the name of the SIP method (For example, "INVITE"). If the annotation is specified on a method whose first parameter is not a SipServletRequest or SipServletResponse, a deployment error occurs.
The @SipResponseCode annotation associates a response code with an annotation. A Java method annotated with a runtime annotation that is itself annotated with @SipResponseCode handles SIP responses with the specified code. The value of the annotation specifies the response code. If an annotation is specified on a method whose first parameter is not SipServletResponse, a deployment error occurs.
The @SipResponseRange annotation associates a response filter with an annotation. A Java method annotated with a runtime annotation that is itself annotated with @SipResponseRange handles SIP responses satisfying the filter. If an annotation is specified on a method whose first parameter is not SipServletResponse, a deployment error occurs. The specified range includes both beginning and end values. The element begin specifies the beginning of the response range, and the element end specifies the end of the response range.
The @SipPredicate annotation applies a predicate with a Java method in a SIP Servlet POJO. When a Java method is annotated with runtime annotations that are annotated with @SipPredicate, then those predicates are evaluated by Converged Application Server before executing the method. Thus, while other meta-annotations allow applications to define their own annotations, @SipPredicate enables applications to provide further filtering of messages based on specific logic. For example, an application can filter decisions based on the value of the SIP header or the state of the SIP Session.
The value of the annotation is a type of implementation of javax.servlet.sip.Predicate. Converged Application Server instantiates the class and invokes the Predicate.apply() method to determine whether or not to invoke the method.
Annotations are defined for each SIP method, such as INVITE, ACK, and REGISTER. Each runtime annotation is annotated with the SIP meta annotation @SipMethod.
A method annotated with @Invite is invoked by the SIP Container for SIP requests or SIP responses based on the type of the first parameter of the method. When the parameter is of type SipServletRequest.class, it is invoked for all SIP requests with the method INVITE. When the parameter is of type SipServletResponse.class, it is invoked for all SIP responses for the method INVITE.
Example 3-1 shows a SIP Servlet POJO that uses the @Invite annotation.
Example 3-1 @Invite Annotation
@SipServlet public class ExamplePOJO { @Invite public void handleInviteRequest(SipServletRequest request) { //... } @Invite public void handleInviteResponse(SipServletResponse response) { //... } }
The following method specific annotations are implemented:
@Invite
@Ack
@Options
@Bye
@Cancel
@Register
@Prack
@Subscribe
@Notify
@Message
@Info
@Update
@Refer
@Publish
For descriptions of the associated SIP request methods, see "SIP Requests".
The @AnyMethod annotation can handle SIP messages corresponding to any SIP method. Applications may use @AnyMethod annotation to receive all SIP requests and SIP responses without checking the method. If the annotation is specified on a method whose first parameter is not a SipServletRequest or SipServletResponse, a deployment error occurs.
JSR-359 defines annotations for filtering responses. The defined annotations are @ProvisionalResponse, @SuccessResponse, @RedirectResponse, and @ErrorResponse. These annotations use the SIP meta-annotation @SipResponseRange to specify the response code range.
Table 3-1 lists the Response annotations and their associated response ranges.
Table 3-1 Response Range Annotations
Annotation | Response Range Begin | Response Range End |
---|---|---|
@ProvisionalResponse |
101 |
199 |
@SuccessResponse |
200 |
299 |
@RedirectResponse |
300 |
399 |
@ErrorResponse |
400 |
699 |
Example 3-2 shows the usage of a response range annotation.
Example 3-2 Response Range Annotation
@SuccessResponse
public void handleSuccessResponse(SipServletResponse response) {
//...
}
To further filter responses, a POJO may combine both a method specific annotation and a Response Filter annotation. Example 3-3 shows how to handle success responses for INVITE messages.
Example 3-3 Combining Annotations
@Invite @SuccessResponse
public void handleInviteSuccessResponse(SipServletResponse response) {
//...
}
It is also possible to specify multiple response filter annotations to a Java method, allowing application developers to handle multiple ranges using the same annotation. Example 3-4 shows how to handle multiple response ranges.
Example 3-4 Multiple Response Range Annotations
@Invite @ProvisionalResponse @SuccessResponse
public void handleInviteSuccessResponse(SipServletResponse response) {
//...
}
An application can use the built-in SipPredicate, @BranchResponse, to associate an intermediate final response that arrives on a ProxyBranch with a Java method in a SIP Servlet POJO.
Example 3-5 shows the @BranchResponse definition.
Example 3-5 @BranchResponse Definition
@Retention(RUNTIME) @Target({METHOD}) @SipPredicate(BranchResponse.Predicate.class) public @interface BranchResponse { class Predicate implements javax.servlet.sip.Predicate<SipServletResponse> { @Override public boolean apply(final SipServletResponse response) { return response.isBranchResponse(); } }
Meta-annotations provide built-in extensibility. SIP meta-annotations allow application developers to define and use their own annotations. For example, an application can define an annotation called foo.example.18xResponses for handling only 18x responses. A single annotation can also contain more than one SIP meta-annotation.
Example 3-6 shows a user defined annotation.
Example 3-6 User Defined Annotation
@Retention(RUNTIME) @Target({METHOD}) @SipResponseRange(begin = 200, end = 299) @SipMethod("INVITE") public @interface MySucessfulInviteResponse { }
Example 3-7 shows an annotation that combines a @SipPredicate annotation with other meta annotations
Example 3-7 Combined Annotations
@Retention(RUNTIME) @Target({METHOD}) @SipPredicate(MyInitialInvite.Predicate.class) @SipMethod("INVITE") public @interface MyInitialInvite { class Predicate implements javax.servlet.sip.Predicate<SipServletRequest> { @Override public boolean apply(final SipServletRequest request) { return request.isInitial(); } } }
The application can then use the @MyInitialInvite annotation to select a method for handling an initial invite as shown in Example 3-8.
Converged Application Server ensures that only one java method is invoked for a particular SIP message. When more than one Java method has matching annotations, Converged Application Server selects the Java method with most specific annotations.
For example, given a success response to an INVITE that could go to either of two methods, one of which matches any response to an INVITE, and the other which matches only success responses to an INVITE, Converged Application Server chooses the second method. Those rules are applied in a particular order. For example, assume that two methods each take a SipServletResponse. If one method only matches the INVITE method, and another method only matches 200 OK responses, a 200 OK response to an INVITE goes to the INVITE-matching method because the SIP method matching has higher priority than response-code matching.
The following sections outline the procedure for finding the most specific Java method.
The following shorthand is used to indicate specific annotations:
Jm: A Java method annotated by annotations with SIP meta annotations.
Sm: A @SipMethod annotation.
Sr: A @SipResponseRange annotation.
Sc: A @SipResponseCode annotation.
Sp: A @SipPredicate annotation.
ESm: (Method of the SipServletMessage) eq (value of Sm)
ESr: (Status code of the SipServletResponse) in (range of Sr)
Esc: (Status code of the SipServletResponse) eq (value of Sc)
ESp: Result of evaluation of @SipPredicate.
Converged Application Server determine that a Jm meets the selection criteria if the annotations evaluate the following equation to true. If an annotation is not present in a sub-expression, the equation evaluates to true. Example 3-9 shows the complete precedence rules equation.
If more than one Jm meets the selection criteria, Converged Application Server selects the Jm with the smallest count of Sm. If the number of Sms is equal, the container selects the Jm with the smallest count of Sc. If the number of Scs are equal, the container selects the one with the shortest span of Srs.
The following rules apply to requests:
Sc (SipResponseCode) and Sr (SipResponseRange) are not applicable to Requests.
If none of the methods satisfy the basic rule, the container selects a Java method annotated with @AnyMethod.
If none of the above is true, it results in the default behavior as specified by the section 2.3 of JSR-359.
The following rule applies to responses:
If none of the methods satisfy the basic rule, the container selects a Java method annotated with @AnyMethod.
If an application includes one or more @SipPredicate annotations, the application must ensure that the predicates are not written so as to cause conflict during selection of methods.
During deployment, Converged Application Server scans the annotations present. If more than one Java method has the same SIP meta-annotation specificity using @SipMethod, @SipResponseCode, or @SipResponseRange, the container fails the deployment.
The following examples explain which method is selected when there is more than one Java method that match the selection criteria.
In Example 3-10, for a 200/INVITE response, Converged Application Server selects handleResponse01(), since that is the more specific of the two methods for that message. For a message with another SIP method, the container will select handleResponse02().
Example 3-10 Conflict Resolution Scenario One
@Invite @SuccessResponse public void handleResponse01(SipServletResponse response) { //... } @SuccessResponse public void handleResponse02(SipServletResponse response) { //... }
In Example 3-11, for a 200/INVITE response, the container will select handleResponse02(), since that is the more specific of the two methods for that message. For 201/INVITE message, the container will select handleResponse01().
Example 3-11 Conflict Resolution Scenario Two
@Invite @SuccessResponse public void handleResponse01(SipServletResponse response) { //... } @InviteOkResponse public void handleResponse02(SipServletResponse response) { //... } @Retention(RetentionPolicy.RUNTIME) @Target(METHOD) @SipResponseCode(200) @SipMethod("INVITE") @interface InviteOkResponse { }
In Example 3-12, for a 200 response handleResponse03() is selected since that is the only method that matches the response codes, and, therefore, there is no conflict. For a 201 response, both handleResponse01() and handleResponse02() match the criteria. However since handleResponse01() has a shorter response range than handleResponse02(), Converged Application Server chooses handleResponse01(). For a 302 response, Converged Application Server selects handleResponse02() since that is the only method that match the criteria.
Example 3-12 Conflict Resolution Scenario Three
@SuccessResponse public void handleResponse01(SipServletResponse resp ) { //... } @NonFailureResponses public void handleResponse02(SipServletResponse resp) { //... } @OkResponse public void handleResponse03(SipServletResponse resp ) { //... } @Retention(RUNTIME) @Target(METHOD) @SipResponseRange(begin = 100, end = 399) @interface NonFailureResponses { } @Retention(RUNTIME) @Target(METHOD) @SipResponseCode(200) @interface OkResponse { }
The following examples show situations in which containers fail deployment since method precedence cannot be determined.
In Example 3-13, the container fails the deployment, since both the methods contain the same number of requests, and, for an INVITE request, the container will find two methods with the same specificity.
Example 3-13 Container Deployment Failure Scenario One
@Invite @Message public void handleRequest01(SipServletRequest req) { //... } @Invite @Register public void handleRequest02(SipServletRequest req) { //... }
In Example 3-14, the container fails deployment because the handleResponse02() supports both SUBSCRIBE and INFO methods and 200 and 204 responses. The handleResponse01() method supports SUBSCRIBE and OPTIONS methods and 200 and 201 responses. Hence, for 200/SUBSCRIBE responses, the specificity of the SIP meta annotations is the same.
Example 3-14 Container Deployment Failure Scenario Two
@OptionsOkResponse @SubscribeAccept public void handleResponse01(SipServletResponse resp) { //... } @InfoOkResponse @SubscribeNoNotification public void handleResponse02(SipServletResponse resp) { //... } @Retention(RUNTIME) @Target(METHOD) @SipResponseCode(200) @SipMethod("INFO") @interface InfoOkResponse { } @Retention(RUNTIME) @Target(METHOD) @SipResponseCode(200) @SipMethod("OPTIONS") @interface OptionsOkResponse { } @Retention(RUNTIME) @Target(METHOD) @SipResponseCode(204) @SipMethod("SUBSCRIBE") @interface SubscribeNoNotification { } @Retention(RUNTIME) @Target(METHOD) @SipResponseCode(201) @SipMethod("SUBSCRIBE") @interface SubscribeAccept { }
In Example 3-15, the span of the response ranges are exactly the same and all other SIP meta annotations are the same in both the methods. For overlapping responses, SIP meta annotations in the two methods have the same specificity, and the container fails deployment.
Example 3-15 Container Deployment Failure Scenario Three
@InviteNonFailureResponses public void handleResponse01(SipServletResponse resp) { //... } @Invite @NonFailureFinalResponses public void handleResponse02(SipServletResponse resp) { //... } @Retention(RUNTIME) @Target(METHOD) @SipResponseRange(begin = 100, end = 299) @SipMethod("INVITE") @interface InviteNonFailureResponses { } @Retention(RUNTIME) @Target(METHOD) @SipResponseRange(begin = 200, end = 399) @interface NonFailureFinalResponses { }
In Example 3-16, since all the SIP meta annotations of both methods are the same and the @SipPredicate annotation uses the same class, the container fails deployment.
Example 3-16 Container Deployment Failure Scenario Four
@Invite @MySpecialResponses public void handleResponse01(SipServletResponse resp) { //... } @InviteSpecialResponses public void handleResponse02(SipServletResponse resp) { //... } @Retention(RUNTIME) @Target(METHOD) @SipMethod("INVITE") @SipPredicate(MyPredicate.class) @interface InviteSpecialResponses { } @Retention(RUNTIME) @Target(METHOD) @SipPredicate(MyPredicate.class) @interface MySpecialResponses { } class MyPredicate implements Predicate<SipServletResponse> { @Override public boolean apply(SipServletResponse response) { //... } }