Package org.igniterealtime.jbosh
Class BOSHClient
- java.lang.Object
-
- org.igniterealtime.jbosh.BOSHClient
-
public final class BOSHClient extends java.lang.Object
BOSH Client session instance. Each communication session with a remote connection manager is represented and handled by an instance of this class. This is the main entry point for client-side communications. To create a new session, a client configuration must first be created and then used to create a client instance:BOSHClientConfig cfg = BOSHClientConfig.Builder.create( "http://server:1234/httpbind", "jabber.org") .setFrom("user@jabber.org") .build(); BOSHClient client = BOSHClient.create(cfg);
Additional client configuration options are available. See theBOSHClientConfig.Builder
class for more information. Once aBOSHClient
instance has been created, communication with the remote connection manager can begin. No attempt will be made to establish a connection to the connection manager until the first call is made to thesend(ComposableBody)
method. Note that it is possible to send an empty body to cause an immediate connection attempt to the connection manager. Sending an empty message would look like the following:client.send(ComposableBody.builder().build());
For more information on creating body messages with content, see theComposableBody.Builder
class documentation. Once a session has been successfully started, the client instance can be used to send arbitrary payload data. All aspects of the BOSH protocol involving setting and processing attributes in the BOSH namespace will be handled by the client code transparently and behind the scenes. The user of the client instance can therefore concentrate entirely on the content of the message payload, leaving the semantics of the BOSH protocol to the client implementation. To be notified of incoming messages from the remote connection manager, aBOSHClientResponseListener
should be added to the client instance. All incoming messages will be published to all response listeners as they arrive and are processed. As with the transmission of payload data via thesend(ComposableBody)
method, there is no need to worry about handling of the BOSH attributes, since this is handled behind the scenes. If the connection to the remote connection manager is terminated (either explicitly or due to a terminal condition of some sort), all connection listeners will be notified. After the connection has been closed, the client instance is considered dead and a new one must be created in order to resume communications with the remote server. Instances of this class are thread-safe.
-
-
Nested Class Summary
Nested Classes Modifier and Type Class Description (package private) static class
BOSHClient.ExchangeInterceptor
Class used in testing to dynamically manipulate received exchanges at test runtime.
-
Field Summary
Fields Modifier and Type Field Description private static boolean
ASSERTIONS
Flag indicating whether or not we want to perform assertions.private BOSHClientConfig
cfg
Session configuration.private CMSessionParams
cmParams
Connection Manager session parameters.private java.util.Set<BOSHClientConnListener>
connListeners
Connection listeners.private static int
DEFAULT_EMPTY_REQUEST_DELAY
Default empty request delay.private static int
DEFAULT_PAUSE_MARGIN
Default value for the pause margin.private java.util.concurrent.locks.Condition
drained
Condition indicating that there are no outstanding connections.private static int
EMPTY_REQUEST_DELAY
Amount of time to wait before sending an empty request, in milliseconds.private java.util.concurrent.ScheduledFuture
emptyRequestFuture
Future for sending a deferred empty request, if needed.private java.lang.Runnable
emptyRequestRunnable
Processor thread runnable instance.private static java.lang.String
ERROR
Value of the 'type' attribute used for recoverable errors.private java.util.Queue<HTTPExchange>
exchanges
List of active/outstanding requests.private java.util.concurrent.atomic.AtomicReference<BOSHClient.ExchangeInterceptor>
exchInterceptor
Storage for test hook implementation.private HTTPSender
httpSender
HTTPSender instance.private static java.lang.String
INTERRUPTED
Message to use for interrupted exceptions.private java.util.concurrent.locks.ReentrantLock
lock
Lock instance.private static java.util.logging.Logger
LOG
Logger.private java.util.concurrent.locks.Condition
notEmpty
Condition indicating that there are messages to be exchanged.private java.util.concurrent.locks.Condition
notFull
Condition indicating that there are available slots for sending messages.private static java.lang.String
NULL_LISTENER
Message used whena null listener is detected.private static int
PAUSE_MARGIN
The amount of time in milliseconds which will be reserved as a safety margin when scheduling empty requests against a maxpause value.private java.util.List<ComposableBody>
pendingRequestAcks
List of requests which have been made but not yet acknowledged.private java.util.SortedSet<java.lang.Long>
pendingResponseAcks
Set of RIDs which have been received, for the purpose of sending response acknowledgements.private java.lang.Runnable
procRunnable
Processor thread runnable instance.private java.lang.Thread
procThread
Thread which is used to process responses from the connection manager.private RequestIDSequence
requestIDSeq
Request ID sequence to use for the session.private java.util.Set<BOSHClientRequestListener>
requestListeners
Request listeners.private java.lang.Long
responseAck
The highest RID that we've already received a response for.private java.util.Set<BOSHClientResponseListener>
responseListeners
Response listeners.private java.util.concurrent.ScheduledExecutorService
schedExec
ScheduledExcecutor to use for deferred tasks.private static java.lang.String
TERMINATE
Value of the 'type' attribute used for session termination.private static java.lang.String
UNHANDLED
Message used for unhandled exceptions.
-
Constructor Summary
Constructors Modifier Constructor Description private
BOSHClient(BOSHClientConfig sessCfg)
Prevent direct construction.
-
Method Summary
All Methods Static Methods Instance Methods Concrete Methods Modifier and Type Method Description void
addBOSHClientConnListener(BOSHClientConnListener listener)
Adds a connection listener to the session.void
addBOSHClientRequestListener(BOSHClientRequestListener listener)
Adds a request message listener to the session.void
addBOSHClientResponseListener(BOSHClientResponseListener listener)
Adds a response message listener to the session.private void
applyFrom(ComposableBody.Builder builder)
Applies the local station ID information to the request message who's builder has been provided.private void
applyResponseAcknowledgement(ComposableBody.Builder builder, long rid)
Sets the 'ack' attribute of the request to the value of the highest 'rid' of a request for which it has already received a response in the case where it has also received all responses associated with lower 'rid' values.private void
applyRoute(ComposableBody.Builder builder)
Applies routing information to the request message who's builder has been provided.private ComposableBody
applySessionCreationRequest(long rid, ComposableBody orig)
Modifies the specified body message such that it becomes a new BOSH session creation request.private ComposableBody
applySessionData(long rid, ComposableBody orig)
Applies existing session data to the outbound request, returning the modified request.private void
assertLocked()
Assert that the internal lock is held.private void
assertUnlocked()
Assert that the internal lock is *not* held.private void
blockUntilSendable(AbstractBody msg)
Blocks until either the message provided becomes immediately sendable or until the session is terminated.private void
checkForTerminalBindingConditions(AbstractBody body, int code)
Checks to see if the response indicates a terminal binding condition (as per XEP-0124 section 17).private void
clearEmptyRequest()
Clears any scheduled empty requests.void
close()
Forcibly close this client session instance.static BOSHClient
create(BOSHClientConfig clientCfg)
Create a new BOSH client session using the client configuration information provided.void
disconnect()
End the BOSH session by disconnecting from the remote BOSH connection manager.void
disconnect(ComposableBody msg)
End the BOSH session by disconnecting from the remote BOSH connection manager, sending the provided content in the final connection termination message.private void
dispose(java.lang.Throwable cause)
Destroy this session.(package private) void
drain()
Wait until no more messages are waiting to be processed.private void
fireConnectionClosed()
Notifies all connection listeners that the session has been terminated normally.private void
fireConnectionClosedOnError(java.lang.Throwable cause)
Notifies all connection listeners that the session has been terminated due to the exceptional condition provided.private void
fireConnectionEstablished()
Notifies all connection listeners that the session has been successfully established.private void
fireRequestSent(AbstractBody request)
Notifies all request listeners that the specified request is being sent.private void
fireResponseReceived(AbstractBody response)
Notifies all response listeners that the specified response has been received.BOSHClientConfig
getBOSHClientConfig()
Get the client configuration that was used to create this client instance.(package private) CMSessionParams
getCMSessionParams()
Get the current CM session params.private long
getDefaultEmptyRequestDelay()
Calculates the default empty request delay/interval to use for the active session.private TerminalBindingCondition
getTerminalBindingCondition(int respCode, AbstractBody respBody)
Evaluates the HTTP response code and response message and returns the terminal binding condition that it describes, if any.private void
init()
Initialize the session.private boolean
isImmediatelySendable(AbstractBody msg)
Determines if the message specified is immediately sendable or if it needs to block until the session state changes.private static boolean
isPause(AbstractBody msg)
Determines if the message body specified indicates a request to pause the session.private static boolean
isRecoverableBindingCondition(AbstractBody resp)
Determines whether or not the response indicates a recoverable binding condition (as per XEP-0124 section 17).private static boolean
isTermination(AbstractBody msg)
Determines if the message body specified indicates a termination of the session.private boolean
isWorking()
Determines whether or not the session is still active.private HTTPExchange
nextExchange()
Get the next message exchange to process, blocking until one becomes available if nothing is already waiting for processing.boolean
pause()
Attempt to pause the current session.private void
processExchange(HTTPExchange exch)
Process the next, provided exchange.private void
processMessages()
While we are "connected", process received responses.private long
processPauseRequest(AbstractBody req)
Process the request to determine if the empty request delay can be determined by looking to see if the request is a pause request.private void
processRequestAcknowledgements(AbstractBody req, AbstractBody resp)
Check the response for request acknowledgements and take appropriate action.private void
processResponseAcknowledgementData(AbstractBody req)
Process the response in order to update the response acknowlegement data.private HTTPExchange
processResponseAcknowledgementReport(AbstractBody resp)
Process the response in order to check for and respond to any potential ack reports.void
removeBOSHClientConnListener(BOSHClientConnListener listener)
Removes a connection listener from the session.void
removeBOSHClientRequestListener(BOSHClientRequestListener listener)
Removes a request message listener from the session, if previously added.void
removeBOSHClientResponseListener(BOSHClientResponseListener listener)
Removes a response message listener from the session, if previously added.private void
scheduleEmptyRequest(long delay)
Schedule an empty request to be sent if no other requests are sent in a reasonable amount of time.void
send(ComposableBody body)
Send the provided message data to the remote connection manager.private void
sendEmptyRequest()
Sends an empty request to maintain session requirements.(package private) void
setExchangeInterceptor(BOSHClient.ExchangeInterceptor interceptor)
Test method used to forcibly discard next exchange.
-
-
-
Field Detail
-
LOG
private static final java.util.logging.Logger LOG
Logger.
-
TERMINATE
private static final java.lang.String TERMINATE
Value of the 'type' attribute used for session termination.- See Also:
- Constant Field Values
-
ERROR
private static final java.lang.String ERROR
Value of the 'type' attribute used for recoverable errors.- See Also:
- Constant Field Values
-
INTERRUPTED
private static final java.lang.String INTERRUPTED
Message to use for interrupted exceptions.- See Also:
- Constant Field Values
-
UNHANDLED
private static final java.lang.String UNHANDLED
Message used for unhandled exceptions.- See Also:
- Constant Field Values
-
NULL_LISTENER
private static final java.lang.String NULL_LISTENER
Message used whena null listener is detected.- See Also:
- Constant Field Values
-
DEFAULT_EMPTY_REQUEST_DELAY
private static final int DEFAULT_EMPTY_REQUEST_DELAY
Default empty request delay.- See Also:
- Constant Field Values
-
EMPTY_REQUEST_DELAY
private static final int EMPTY_REQUEST_DELAY
Amount of time to wait before sending an empty request, in milliseconds.
-
DEFAULT_PAUSE_MARGIN
private static final int DEFAULT_PAUSE_MARGIN
Default value for the pause margin.- See Also:
- Constant Field Values
-
PAUSE_MARGIN
private static final int PAUSE_MARGIN
The amount of time in milliseconds which will be reserved as a safety margin when scheduling empty requests against a maxpause value. This should give us enough time to build the message and transport it to the remote host.
-
ASSERTIONS
private static final boolean ASSERTIONS
Flag indicating whether or not we want to perform assertions.
-
connListeners
private final java.util.Set<BOSHClientConnListener> connListeners
Connection listeners.
-
requestListeners
private final java.util.Set<BOSHClientRequestListener> requestListeners
Request listeners.
-
responseListeners
private final java.util.Set<BOSHClientResponseListener> responseListeners
Response listeners.
-
lock
private final java.util.concurrent.locks.ReentrantLock lock
Lock instance.
-
notEmpty
private final java.util.concurrent.locks.Condition notEmpty
Condition indicating that there are messages to be exchanged.
-
notFull
private final java.util.concurrent.locks.Condition notFull
Condition indicating that there are available slots for sending messages.
-
drained
private final java.util.concurrent.locks.Condition drained
Condition indicating that there are no outstanding connections.
-
cfg
private final BOSHClientConfig cfg
Session configuration.
-
procRunnable
private final java.lang.Runnable procRunnable
Processor thread runnable instance.
-
emptyRequestRunnable
private final java.lang.Runnable emptyRequestRunnable
Processor thread runnable instance.
-
httpSender
private final HTTPSender httpSender
HTTPSender instance.
-
exchInterceptor
private final java.util.concurrent.atomic.AtomicReference<BOSHClient.ExchangeInterceptor> exchInterceptor
Storage for test hook implementation.
-
requestIDSeq
private final RequestIDSequence requestIDSeq
Request ID sequence to use for the session.
-
schedExec
private final java.util.concurrent.ScheduledExecutorService schedExec
ScheduledExcecutor to use for deferred tasks.
-
procThread
private java.lang.Thread procThread
Thread which is used to process responses from the connection manager. Becomes null when session is terminated.
-
emptyRequestFuture
private java.util.concurrent.ScheduledFuture emptyRequestFuture
Future for sending a deferred empty request, if needed.
-
cmParams
private CMSessionParams cmParams
Connection Manager session parameters. Only available when in a connected state.
-
exchanges
private java.util.Queue<HTTPExchange> exchanges
List of active/outstanding requests.
-
pendingResponseAcks
private java.util.SortedSet<java.lang.Long> pendingResponseAcks
Set of RIDs which have been received, for the purpose of sending response acknowledgements.
-
responseAck
private java.lang.Long responseAck
The highest RID that we've already received a response for. This value is used to implement response acks.
-
pendingRequestAcks
private java.util.List<ComposableBody> pendingRequestAcks
List of requests which have been made but not yet acknowledged. This list remains unpopulated if the CM is not acking requests.
-
-
Constructor Detail
-
BOSHClient
private BOSHClient(BOSHClientConfig sessCfg)
Prevent direct construction.
-
-
Method Detail
-
create
public static BOSHClient create(BOSHClientConfig clientCfg)
Create a new BOSH client session using the client configuration information provided.- Parameters:
clientCfg
- session configuration- Returns:
- BOSH session instance
-
getBOSHClientConfig
public BOSHClientConfig getBOSHClientConfig()
Get the client configuration that was used to create this client instance.- Returns:
- client configuration
-
addBOSHClientConnListener
public void addBOSHClientConnListener(BOSHClientConnListener listener)
Adds a connection listener to the session.- Parameters:
listener
- connection listener to add, if not already added
-
removeBOSHClientConnListener
public void removeBOSHClientConnListener(BOSHClientConnListener listener)
Removes a connection listener from the session.- Parameters:
listener
- connection listener to remove, if previously added
-
addBOSHClientRequestListener
public void addBOSHClientRequestListener(BOSHClientRequestListener listener)
Adds a request message listener to the session.- Parameters:
listener
- request listener to add, if not already added
-
removeBOSHClientRequestListener
public void removeBOSHClientRequestListener(BOSHClientRequestListener listener)
Removes a request message listener from the session, if previously added.- Parameters:
listener
- instance to remove
-
addBOSHClientResponseListener
public void addBOSHClientResponseListener(BOSHClientResponseListener listener)
Adds a response message listener to the session.- Parameters:
listener
- response listener to add, if not already added
-
removeBOSHClientResponseListener
public void removeBOSHClientResponseListener(BOSHClientResponseListener listener)
Removes a response message listener from the session, if previously added.- Parameters:
listener
- instance to remove
-
send
public void send(ComposableBody body) throws BOSHException
Send the provided message data to the remote connection manager. The provided message body does not need to have any BOSH-specific attribute information set. It only needs to contain the actual message payload that should be delivered to the remote server. The first call to this method will result in a connection attempt to the remote connection manager. Subsequent calls to this method will block until the underlying session state allows for the message to be transmitted. In certain scenarios - such as when the maximum number of outbound connections has been reached - calls to this method will block for short periods of time.- Parameters:
body
- message data to send to remote server- Throws:
BOSHException
- on message transmission failure
-
pause
public boolean pause()
Attempt to pause the current session. When supported by the remote connection manager, pausing the session will result in the connection manager closing out all outstanding requests (including the pause request) and increases the inactivity timeout of the session. The exact value of the temporary timeout is dependent upon the connection manager. This method should be used if a client encounters an exceptional temporary situation during which it will be unable to send requests to the connection manager for a period of time greater than the maximum inactivity period. The session will revert back to it's normal, unpaused state when the client sends it's next message.- Returns:
true
if the connection manager supports session pausing,false
if the connection manager does not support session pausing or if the session has not yet been established
-
disconnect
public void disconnect() throws BOSHException
End the BOSH session by disconnecting from the remote BOSH connection manager.- Throws:
BOSHException
- when termination message cannot be sent
-
disconnect
public void disconnect(ComposableBody msg) throws BOSHException
End the BOSH session by disconnecting from the remote BOSH connection manager, sending the provided content in the final connection termination message.- Parameters:
msg
- final message to send- Throws:
BOSHException
- when termination message cannot be sent
-
close
public void close()
Forcibly close this client session instance. The preferred mechanism to close the connection is to send a disconnect message and wait for organic termination. Calling this method simply shuts down the local session without sending a termination message, releasing all resources associated with the session.
-
getCMSessionParams
CMSessionParams getCMSessionParams()
Get the current CM session params.- Returns:
- current session params, or
null
-
drain
void drain()
Wait until no more messages are waiting to be processed.
-
setExchangeInterceptor
void setExchangeInterceptor(BOSHClient.ExchangeInterceptor interceptor)
Test method used to forcibly discard next exchange.- Parameters:
interceptor
- exchange interceptor
-
init
private void init()
Initialize the session. This initializes the underlying HTTP transport implementation and starts the receive thread.
-
dispose
private void dispose(java.lang.Throwable cause)
Destroy this session.- Parameters:
cause
- the reason for the session termination, ornull
for normal termination
-
isPause
private static boolean isPause(AbstractBody msg)
Determines if the message body specified indicates a request to pause the session.- Parameters:
msg
- message to evaluate- Returns:
true
if the message is a pause request,false
otherwise
-
isTermination
private static boolean isTermination(AbstractBody msg)
Determines if the message body specified indicates a termination of the session.- Parameters:
msg
- message to evaluate- Returns:
true
if the message is a session termination,false
otherwise
-
getTerminalBindingCondition
private TerminalBindingCondition getTerminalBindingCondition(int respCode, AbstractBody respBody)
Evaluates the HTTP response code and response message and returns the terminal binding condition that it describes, if any.- Parameters:
respCode
- HTTP response coderespBody
- response body- Returns:
- terminal binding condition, or
null
if not a terminal binding condition message
-
isImmediatelySendable
private boolean isImmediatelySendable(AbstractBody msg)
Determines if the message specified is immediately sendable or if it needs to block until the session state changes.- Parameters:
msg
- message to evaluate- Returns:
true
if the message can be immediately sent,false
otherwise
-
isWorking
private boolean isWorking()
Determines whether or not the session is still active.- Returns:
true
if it is,false
otherwise
-
blockUntilSendable
private void blockUntilSendable(AbstractBody msg)
Blocks until either the message provided becomes immediately sendable or until the session is terminated.- Parameters:
msg
- message to evaluate
-
applySessionCreationRequest
private ComposableBody applySessionCreationRequest(long rid, ComposableBody orig) throws BOSHException
Modifies the specified body message such that it becomes a new BOSH session creation request.- Parameters:
rid
- request ID to useorig
- original body to modify- Returns:
- modified message which acts as a session creation request
- Throws:
BOSHException
-
applyRoute
private void applyRoute(ComposableBody.Builder builder)
Applies routing information to the request message who's builder has been provided.- Parameters:
builder
- builder instance to add routing information to
-
applyFrom
private void applyFrom(ComposableBody.Builder builder)
Applies the local station ID information to the request message who's builder has been provided.- Parameters:
builder
- builder instance to add station ID information to
-
applySessionData
private ComposableBody applySessionData(long rid, ComposableBody orig) throws BOSHException
Applies existing session data to the outbound request, returning the modified request. This method assumes the lock is currently held.- Parameters:
rid
- request ID to useorig
- original/raw request- Returns:
- modified request with session information applied
- Throws:
BOSHException
-
applyResponseAcknowledgement
private void applyResponseAcknowledgement(ComposableBody.Builder builder, long rid)
Sets the 'ack' attribute of the request to the value of the highest 'rid' of a request for which it has already received a response in the case where it has also received all responses associated with lower 'rid' values. The only exception is that, after its session creation request, the client SHOULD NOT include an 'ack' attribute in any request if it has received responses to all its previous requests.- Parameters:
builder
- message builderrid
- current request RID
-
processMessages
private void processMessages()
While we are "connected", process received responses. This method is run in the processing thread.
-
nextExchange
private HTTPExchange nextExchange()
Get the next message exchange to process, blocking until one becomes available if nothing is already waiting for processing.- Returns:
- next available exchange to process, or
null
if no exchanges are immediately available
-
processExchange
private void processExchange(HTTPExchange exch)
Process the next, provided exchange. This is the main processing method of the receive thread.- Parameters:
exch
- message exchange to process
-
clearEmptyRequest
private void clearEmptyRequest()
Clears any scheduled empty requests.
-
getDefaultEmptyRequestDelay
private long getDefaultEmptyRequestDelay()
Calculates the default empty request delay/interval to use for the active session.- Returns:
- delay in milliseconds
-
scheduleEmptyRequest
private void scheduleEmptyRequest(long delay)
Schedule an empty request to be sent if no other requests are sent in a reasonable amount of time.
-
sendEmptyRequest
private void sendEmptyRequest()
Sends an empty request to maintain session requirements. If a request is sent within a reasonable time window, the empty request transmission will be cancelled.
-
assertLocked
private void assertLocked()
Assert that the internal lock is held.
-
assertUnlocked
private void assertUnlocked()
Assert that the internal lock is *not* held.
-
checkForTerminalBindingConditions
private void checkForTerminalBindingConditions(AbstractBody body, int code) throws BOSHException
Checks to see if the response indicates a terminal binding condition (as per XEP-0124 section 17). If it does, an exception is thrown.- Parameters:
body
- response body to evaluatecode
- HTTP response code- Throws:
BOSHException
- if a terminal binding condition is detected
-
isRecoverableBindingCondition
private static boolean isRecoverableBindingCondition(AbstractBody resp)
Determines whether or not the response indicates a recoverable binding condition (as per XEP-0124 section 17).- Parameters:
resp
- response body- Returns:
true
if it does,false
otherwise
-
processPauseRequest
private long processPauseRequest(AbstractBody req)
Process the request to determine if the empty request delay can be determined by looking to see if the request is a pause request. If it can, the request's delay is returned, otherwise the default delay is returned.- Returns:
- delay in milliseconds that should elapse prior to an empty message being sent
-
processRequestAcknowledgements
private void processRequestAcknowledgements(AbstractBody req, AbstractBody resp)
Check the response for request acknowledgements and take appropriate action. This method assumes the lock is currently held.- Parameters:
req
- requestresp
- response
-
processResponseAcknowledgementData
private void processResponseAcknowledgementData(AbstractBody req)
Process the response in order to update the response acknowlegement data. This method assumes the lock is currently held.- Parameters:
req
- request
-
processResponseAcknowledgementReport
private HTTPExchange processResponseAcknowledgementReport(AbstractBody resp) throws BOSHException
Process the response in order to check for and respond to any potential ack reports. This method assumes the lock is currently held.- Parameters:
resp
- response- Returns:
- exchange to transmit if a resend is to be performed, or
null
if no resend is necessary - Throws:
BOSHException
- when a a retry is needed but cannot be performed
-
fireRequestSent
private void fireRequestSent(AbstractBody request)
Notifies all request listeners that the specified request is being sent.- Parameters:
request
- request being sent
-
fireResponseReceived
private void fireResponseReceived(AbstractBody response)
Notifies all response listeners that the specified response has been received.- Parameters:
response
- response received
-
fireConnectionEstablished
private void fireConnectionEstablished()
Notifies all connection listeners that the session has been successfully established.
-
fireConnectionClosed
private void fireConnectionClosed()
Notifies all connection listeners that the session has been terminated normally.
-
fireConnectionClosedOnError
private void fireConnectionClosedOnError(java.lang.Throwable cause)
Notifies all connection listeners that the session has been terminated due to the exceptional condition provided.- Parameters:
cause
- cause of the termination
-
-