org.cafesip.sipunit
Class EventSubscriber

java.lang.Object
  extended by org.cafesip.sipunit.EventSubscriber
All Implemented Interfaces:
MessageListener, RequestListener, SipActionObject
Direct Known Subclasses:
PresenceSubscriber, ReferSubscriber

public class EventSubscriber
extends java.lang.Object
implements MessageListener, SipActionObject

The EventSubscriber class represents a generic subscription conforming to the event subscription and asynchronous notification framework defined by RFC-3265. This class is used for the Subscriber-side perspective as opposed to the NOTIFY sending side.

This object is created as the result of an initial outbound SUBSCRIBE message or REFER message. This object is used indirectly by a test program to proceed through the subscribing side SUBSCRIBE or REFER <-> NOTIFY sequence(s) and to find out details at any given time about the subscription such as the subscription state, amount of time left on the subscription if still active, termination reason if terminated, errors encountered during received SUBSCRIBE/REFER response and incoming NOTIFY message validation, and details of any received responses and requests if needed.

Event package-specific handling/information is handled by the classes extending this class. The user test programs use those subclasses directly.


Method Summary
 void clearEventErrors()
          This method clears errors accumulated while collecting responses and NOTIFY requests.
 javax.sip.message.Request createSubscribeMessage(int duration, java.lang.String eventId, java.lang.String eventType)
          This method creates and returns to the caller the next SUBSCRIBE message that would be sent out for this subscription, so that the user can modify it before it gets sent (to introduce errors - insert incorrect content, remove content, etc.).
 java.lang.String format()
          The format() method can be used to obtain a human-readable string containing the result of the last operation - either a successful indication or all of the error information associated with the last operation performed.
 java.util.ArrayList<SipRequest> getAllReceivedRequests()
          This method returns all the NOTIFY requests received on this subscription.
 java.util.ArrayList<SipResponse> getAllReceivedResponses()
          This method returns all the responses received on this subscription, including any that required re-initiation of the subscription (ie, authentication challenge).
 javax.sip.ResponseEvent getCurrentResponse()
          This method returns the most recent response received from the network for this subscription.
 javax.sip.Dialog getDialog()
          This method returns the dialog associated with this subscription.
 java.lang.String getDialogId()
          This method returns the dialog ID associated with this subscription, or an empty string if the dialog isn't created yet.
 java.lang.String getErrorMessage()
          The getErrorMessage() method returns a descriptive, human-readable string indicating the cause of the problem encountered during the last operation performed.
 java.util.LinkedList<java.lang.String> getEventErrors()
          This method returns any errors accumulated during collection of responses and NOTIFY requests.
 java.lang.Throwable getException()
          This method is used to get the Exception object generated during the last operation performed.
 SipRequest getLastReceivedRequest()
          This method returns the last request received on this subscription.
 SipResponse getLastReceivedResponse()
          This method returns the last response received on this subscription.
 javax.sip.message.Request getLastSentRequest()
          This method returns the last request that was sent out for this subscription.
 int getReturnCode()
          This method returns the status code of the current or last operation performed.
 java.lang.String getTargetUri()
          This method returns the URI of the user that this Subscription is for.
 java.lang.String getTerminationReason()
          Returns the subscription termination reason for this subscription.
 int getTimeLeft()
          Returns the number of seconds left in the subscription, if active, or the number of seconds that were remaining at the time the subscription was terminated.
 boolean isRemovalComplete()
          This method, called after an operation that ends a subscription (such as PresenceSubscriber.removeBuddy() or ReferSubscriber.unsubscribe()), indicates if an unsubscribe sequence was initiated due to the operation or not.
 boolean isSubscriptionActive()
          Indicates if the subscription state is ACTIVE.
 boolean isSubscriptionPending()
          Indicates if the subscription state is PENDING.
 boolean isSubscriptionTerminated()
          Indicates if the subscription state is TERMINATED.
 void processEvent(java.util.EventObject event)
          For internal SipUnit use only.
 javax.sip.message.Response processNotify(javax.sip.RequestEvent requestEvent)
          This method validates the given (received) NOTIFY request, updates the subscription information based on the NOTIFY contents, and creates and returns the correctly formed response that should be sent back in reply to the NOTIFY, based on the NOTIFY content that was received.
 boolean processResponse(long timeout)
          This method processes the initial response received after sending a SUBSCRIBE or REFER request and takes the transaction to its completion by collecting any remaining responses from the far end for this transaction, handling authentication challenges if needed, and updating this object with the results of the request/response sequence.
 boolean replyToNotify(javax.sip.RequestEvent reqevent, javax.sip.message.Response response)
          This method sends the given response to the network in reply to the given request that was previously received.
 javax.sip.RequestEvent waitNotify(long timeout)
          The waitNotify() method allows received NOTIFY messages to be examined and processed by the test program, one by one.
 
Methods inherited from class java.lang.Object
equals, getClass, hashCode, notify, notifyAll, toString, wait, wait, wait
 

Method Detail

createSubscribeMessage

public javax.sip.message.Request createSubscribeMessage(int duration,
                                                        java.lang.String eventId,
                                                        java.lang.String eventType)
This method creates and returns to the caller the next SUBSCRIBE message that would be sent out for this subscription, so that the user can modify it before it gets sent (to introduce errors - insert incorrect content, remove content, etc.). The idea is to call this method which will create the SUBSCRIBE request correctly, then modify the returned request yourself, then call one of the subscription-related methods (for example: PresenceSubscriber.refreshBuddy(), ReferSubscriber.unsubscribe(), etc.) that take Request as a parameter, which will result in the request being sent out.

If you don't need to modify the next SUBSCRIBE request to introduce errors, don't bother with this method and just call one of the alternative subscription-related methods that doesn't take a Request parameter - it will create and send the request in one step.

Effective use of this method requires knowledge of the JAIN SIP API Request and Header classes. Use those to modify the request returned by this method.

Note that subscription-creating methods like SipPhone.addBuddy() and SipPhone.refer() do not have any signatures that take Request as a parameter. The reason is because a correct initial SUBSCRIBE or REFER request is needed to initialize the Subscription object properly. If you want to send out a bad initial request message to see what your test target does, use the SipPhone's base class SipSession (low-level) methods to send the bad request and get the resulting response.

Parameters:
duration - the duration in seconds to put in the SUBSCRIBE message. If -1, don't include a duration (ExpiresHeader) in the message.
eventId - the event "id" to use in the SUBSCRIBE message, or null for no event "id" parameter. Whatever is indicated here will be used subsequently (for error checking SUBSCRIBE responses and NOTIFYs from the server as well as for sending subsequent SUBSCRIBEs) unless changed by the caller later on another subscription-related method call.
eventType - the eventType value (for example: "presence" or "refer") to use in the EventHeader and AllowEventsHeader
Returns:
a SUBSCRIBE Request object if creation is successful, null otherwise. If null, call getReturnCode(), getErrorMessage() and/or getException() for failure info.

processEvent

public void processEvent(java.util.EventObject event)
Description copied from interface: RequestListener
For internal SipUnit use only.

Specified by:
processEvent in interface RequestListener
Parameters:
event - Event received.

processResponse

public boolean processResponse(long timeout)
This method processes the initial response received after sending a SUBSCRIBE or REFER request and takes the transaction to its completion by collecting any remaining responses from the far end for this transaction, handling authentication challenges if needed, and updating this object with the results of the request/response sequence. It performs any eventpackage-specific validation on the received response and returns false if that validation fails.

Call this method after calling any of the subscription operation methods that send a SUBSCRIBE or REFER request like SipPhone.addBuddy(), PresenceSubscriber.refreshBuddy(), SipPhone.refer(), etc. and getting back a positive indication.

If a success indication is returned by this method, you can call other methods on this object to find out the result of the messaging sequence: isSubscriptionActive/Pending/Terminated() for subscription state information, getTimeLeft() if subscription expiry information has been received.

The next step if this method returns true is to call waitNotify() to retrieve/wait for the NOTIFY request from the far end which may or may not have already come in.

Parameters:
timeout - The maximum amount of time to wait for the subscription transaction to complete, in milliseconds. Use a value of 0 to wait indefinitely.
Returns:
true if the response(s) received were valid and no errors were encountered, false otherwise (call getReturnCode(), getErrorMessage()).

processNotify

public javax.sip.message.Response processNotify(javax.sip.RequestEvent requestEvent)
This method validates the given (received) NOTIFY request, updates the subscription information based on the NOTIFY contents, and creates and returns the correctly formed response that should be sent back in reply to the NOTIFY, based on the NOTIFY content that was received. Call this method after getting a NOTIFY request from method waitNotify().

If a null value is returned by this method, call getReturnCode() and/or getErrorMessage() to see why.

If a non-null response object is returned by this method, it doesn't mean that NOTIFY validation passed. If there was invalid content in the NOTIFY, the response object returned by this method will have the appropriate error code (489 Bad Event, etc.) that should be sent in reply to the NOTIFY. You can call getReturnCode() to find out the status code contained in the returned response (or you can examine the response in detail using JAIN-SIP API). A return code of 200 OK means that the received NOTIFY had correct content and the event information stored in this Subscription object has been updated with the NOTIFY message contents. In this case you can call methods isSubscriptionActive/Pending/Terminated(), getTerminationReason() and/or getTimeLeft() for updated subscription information, and for event-specific information that may have been updated by the received NOTIFY, call the appropriate Subscription subclass methods.

The next step after this is to invoke replyToNotify() to send the response to the network. You may modify/corrupt the response returned by this method (using the JAIN-SIP API) before passing it to replyToNotify().

Validation performed by this method includes: event header existence, correct event type (done by the event-specific subclass), NOTIFY event ID matches that in the sent request (SUBSCRIBE, REFER), subscription state header existence, received expiry not greater than that sent in the request if it was included there, catch illegal reception of NOTIFY request without having sent a request, supported content type/subtype in NOTIFY (done by the event-specific subclass), and other event-specific validation (such as, for presence: matching (correct) presentity in NOTIFY body, correctly formed xml body document, valid document content).

Parameters:
requestEvent - the NOTIFY request event obtained from waitNotify()
Returns:
a correct javax.sip.message.Response that should be sent back in reply, or null if an error was encountered.

replyToNotify

public boolean replyToNotify(javax.sip.RequestEvent reqevent,
                             javax.sip.message.Response response)
This method sends the given response to the network in reply to the given request that was previously received. Call this method after processNotify() has handled the received request.

Parameters:
reqevent - The object returned by waitNotify().
response - The object returned by processNotify(), or a user-modified version of it.
Returns:
true if the response is successfully sent out, false otherwise.

getTimeLeft

public int getTimeLeft()
Returns the number of seconds left in the subscription, if active, or the number of seconds that were remaining at the time the subscription was terminated.

Returns:
Returns the timeLeft in seconds.

format

public java.lang.String format()
Description copied from interface: SipActionObject
The format() method can be used to obtain a human-readable string containing the result of the last operation - either a successful indication or all of the error information associated with the last operation performed.

Specified by:
format in interface SipActionObject
Returns:
A string fully describing the error information associated with the last operation performed, or a successful indication if no error occurred.

getLastReceivedResponse

public SipResponse getLastReceivedResponse()
This method returns the last response received on this subscription.

Specified by:
getLastReceivedResponse in interface MessageListener
Returns:
A SipResponse object representing the last response message received, or null if none has been received.
See Also:
MessageListener.getLastReceivedResponse()

getLastReceivedRequest

public SipRequest getLastReceivedRequest()
This method returns the last request received on this subscription.

Specified by:
getLastReceivedRequest in interface MessageListener
Returns:
A SipRequest object representing the last request message received, or null if none has been received.
See Also:
MessageListener.getLastReceivedRequest()

getAllReceivedResponses

public java.util.ArrayList<SipResponse> getAllReceivedResponses()
This method returns all the responses received on this subscription, including any that required re-initiation of the subscription (ie, authentication challenge). Not included are out-of-sequence (late) responses.

Specified by:
getAllReceivedResponses in interface MessageListener
Returns:
ArrayList of zero or more SipResponse objects.
See Also:
MessageListener.getAllReceivedResponses()

getAllReceivedRequests

public java.util.ArrayList<SipRequest> getAllReceivedRequests()
This method returns all the NOTIFY requests received on this subscription. (Retransmissions aren't included.)

Specified by:
getAllReceivedRequests in interface MessageListener
Returns:
ArrayList of zero or more SipRequest objects.
See Also:
MessageListener.getAllReceivedRequests()

isSubscriptionTerminated

public boolean isSubscriptionTerminated()
Indicates if the subscription state is TERMINATED.

Returns:
true if so, false if not.

isSubscriptionActive

public boolean isSubscriptionActive()
Indicates if the subscription state is ACTIVE.

Returns:
true if so, false if not.

isSubscriptionPending

public boolean isSubscriptionPending()
Indicates if the subscription state is PENDING.

Returns:
true if so, false if not.

getTerminationReason

public java.lang.String getTerminationReason()
Returns the subscription termination reason for this subscription. Call this method when the subscription has been terminated (method isSubscriptionTerminated() returns true).

Returns:
Returns the termination reason or null if the subscription is not terminated.

getErrorMessage

public java.lang.String getErrorMessage()
Description copied from interface: SipActionObject
The getErrorMessage() method returns a descriptive, human-readable string indicating the cause of the problem encountered during the last operation performed. If an exception was involved, this string will contain the name of the Exception class and the exception message.

Specified by:
getErrorMessage in interface SipActionObject
Returns:
A descriptive string describing the cause of the problem encountered during the last operation performed, or an empty string if no problem was encountered.

getException

public java.lang.Throwable getException()
Description copied from interface: SipActionObject
This method is used to get the Exception object generated during the last operation performed. It applies whenever the getReturnCode() method returns internal SipUnit return code EXCEPTION_ENCOUNTERED.

Specified by:
getException in interface SipActionObject
Returns:
The Throwable object generated during the last operation performed, or null if an Exception didn't occur.

getReturnCode

public int getReturnCode()
Description copied from interface: SipActionObject
This method returns the status code of the current or last operation performed. It returns either the SIP response code received from the network (defined in SipResponse, along with the corresponding textual equivalent) or a SipUnit internal status/return code (defined in SipSession, along with the corresponding textual equivalent). SipUnit internal codes are in a specially designated range (SipSession.SIPUNIT_INTERNAL_RETURNCODE_MIN and upward).

Specified by:
getReturnCode in interface SipActionObject
Returns:
The status code of the last operation performed, or the status code so far of the current ongoing operation.

getTargetUri

public java.lang.String getTargetUri()
This method returns the URI of the user that this Subscription is for.

Returns:
The user's URI.

getEventErrors

public java.util.LinkedList<java.lang.String> getEventErrors()
This method returns any errors accumulated during collection of responses and NOTIFY requests. Since this happens automatically, asynchronous of the test program activity, there's not a handy way like a method call return code to report these errors if they happen. They are errors like: No CSEQ header in received NOTIFY, error or exception resending request with authorization header, unexpected null transaction object at response timeout, etc. You should at various points call SipTestCase.assertNoEventErrors() during a test to verify none have been encountered.

The case where a NOTIFY is received by a SipPhone but there is no matching subscription results in 481 response being sent back and an event error entry in each Subscription object associated with that SipPhone (to ensure it will be seen by the test program).

Aside from being put in the event error list, event errors are output with the SipUnit trace if you have it turned on (SipStack.setTraceEnabled(true)). You can clear this list by calling clearEventErrors().

Returns:
LinkedList (never null) of zero or more String

clearEventErrors

public void clearEventErrors()
This method clears errors accumulated while collecting responses and NOTIFY requests. See related method getEventErrors().


getDialog

public javax.sip.Dialog getDialog()
This method returns the dialog associated with this subscription.

Returns:
The JAIN-SIP Dialog object.

getDialogId

public java.lang.String getDialogId()
This method returns the dialog ID associated with this subscription, or an empty string if the dialog isn't created yet.

Returns:
String which is the dialog ID associated with this subscription

waitNotify

public javax.sip.RequestEvent waitNotify(long timeout)
The waitNotify() method allows received NOTIFY messages to be examined and processed by the test program, one by one. Call this method whenever you are expecting a NOTIFY to be received and your test program has nothing else to do until then. If there are already one or more unexamined-as-yet-by-the-test-program NOTIFY messages accumulated when this method is called, it returns the next in line (FIFO) immediately. Otherwise, it waits for the next NOTIFY message to be received from the network for this subscription.

This method blocks until one of the following occurs: 1) A NOTIFY message is received, for this subscription. The received NOTIFY javax.sip.RequestEvent object is returned in this case. The calling program may examine the returned object (requires knowledge of JAIN SIP). The next step for the caller is to pass the object returned by this method to processNotify() for handling. 2) The wait timeout period specified by the parameter to this method expires. Null is returned in this case. 3) An error occurs. Null is returned in this case.

A NOTIFY message whose CSEQ# is not greater than those previously received is discarded and not returned by this method.

Parameters:
timeout - The maximum amount of time to wait, in milliseconds. Use a value of 0 to wait indefinitely.
Returns:
A RequestEvent (received NOTIFY) or null in the case of wait timeout or error. If null is returned, call getReturnCode() and/or getErrorMessage() and, if applicable, getException() for further diagnostics.

getCurrentResponse

public javax.sip.ResponseEvent getCurrentResponse()
This method returns the most recent response received from the network for this subscription. Knowledge of JAIN-SIP API is required to examine the object returned from this method. Alternately, call getLastReceivedResponse() to see the primary values (status, reason) contained in the last received response.

Returns:
javax.sip.ResponseEvent - last received response to a previously sent subscription request.

getLastSentRequest

public javax.sip.message.Request getLastSentRequest()
This method returns the last request that was sent out for this subscription.

Returns:
javax.sip.message.Request last sent out

isRemovalComplete

public boolean isRemovalComplete()
This method, called after an operation that ends a subscription (such as PresenceSubscriber.removeBuddy() or ReferSubscriber.unsubscribe()), indicates if an unsubscribe sequence was initiated due to the operation or not. If so, you need to proceed forward with the SUBSCRIBE/NOTIFY sequence processing to complete the unsubscribe sequence.

Returns:
true if unsubscribe was not necessary (because the subscription was already terminated) or false if a SUBSCRIBE/NOTIFY sequence was initiated due to the subscription-ending operation.


http://www.cafesip.org