Methods Summary |
---|
public synchronized void | clearEventPending()Clears the event pending flag.
eventPending = false;
notify();
|
public gov.nist.siplite.stack.ClientTransaction | cloneWithNewLastResponse(Response lastResponse)Create a new client transaction based on current.
Field lastResponse is filled by input parameter.
ClientTransaction clientTransaction = new ClientTransaction(
(SIPTransactionStack)getSIPStack(), getMessageChannel());
clientTransaction.lastResponse = lastResponse;
clientTransaction.setOriginalRequest(getOriginalRequest());
return clientTransaction;
|
public Request | createAck()Creates an ACK request for this transaction
Request originalRequest = getOriginalRequest();
int statusCode = 0;
if (originalRequest.getMethod().equals(Request.ACK)) {
throw new SipException("Cannot ACK an ACK!",
SipException.INVALID_OPERATION);
} else if (lastResponse == null) {
throw new SipException("bad Transaction state",
SipException.INVALID_STATE);
} else {
statusCode = lastResponse.getStatusCode();
if (statusCode < 200) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"lastResponse = " + lastResponse);
}
throw new SipException("Cannot ACK a provisional response!",
SipException.INVALID_OPERATION);
}
}
Request ackRequest =
originalRequest.createAckRequest((ToHeader)lastResponse.getTo());
// Automatic ACK at transaction layer
if (300 <= statusCode && statusCode <= 699) {
ViaHeader topmostVia = originalRequest.getTopmostVia();
if (topmostVia != null) {
ackRequest.setHeader(topmostVia);
}
return ackRequest;
}
// Pull the record route headers from the last reesponse.
buildRouteSet(ackRequest);
return ackRequest;
|
public Request | createCancel()Creates a new Cancel message from the Request associated with this client
transaction. The CANCEL request, is used to cancel the previous request
sent by this client transaction. Specifically, it asks the UAS to cease
processing the request and to generate an error response to that request.
Request originalRequest = getOriginalRequest();
return originalRequest.createCancelRequest();
|
protected void | fireRetransmissionTimer()Called by the transaction stack when a retransmission timer
fires.
boolean noSend = false;
try {
// Resend the last request sent
// System.out.println("fireRetransmissionTimer ");
if (this.getState() == -1) {
noSend = true;
} else {
MessageProcessor mp = this.getMessageProcessor();
if (mp == null) {
noSend = true;
} else if (mp.toExit()) {
noSend = true;
}
}
int currentState = this.getState();
if (!noSend && (currentState == CALLING_STATE ||
currentState == TRYING_STATE)) {
getMessageChannel().sendMessage(lastRequest);
}
} catch (IOException e) {
raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
}
|
protected void | fireTimeoutTimer()Called by the transaction stack when a timeout timer fires.
Dialog dialogImpl = this.getDialog();
if (getState() == CALLING_STATE ||
getState() == TRYING_STATE ||
getState() == PROCEEDING_STATE) {
// Timeout occured. If this is asociated with a transaction
// creation then kill the dialog.
if (dialogImpl != null) {
if (((SIPTransactionStack)getSIPStack()).isDialogCreated
(this.getOriginalRequest().getMethod())) {
// terminate the enclosing dialog.
dialogImpl.setState(Dialog.TERMINATED_STATE);
} else if (getOriginalRequest().getMethod().equals
(Request.BYE)) {
// Terminate the associated dialog on BYE Timeout.
dialogImpl.setState(Dialog.TERMINATED_STATE);
}
}
}
if (getState() != COMPLETED_STATE) {
raiseErrorEvent(SIPTransactionErrorEvent.TIMEOUT_ERROR);
} else {
setState(TERMINATED_STATE);
}
|
public ViaHeader | getOutgoingViaHeader()Gets the via header for an outgoing request.
return this.getMessageProcessor().getViaHeader();
|
public java.lang.String | getProcessingInfo()Gets the processing information.
return respondTo.getProcessingInfo();
|
public MessageChannel | getRequestChannel()Returns this transaction.
return this;
|
public java.lang.String | getViaHost()Gets the host of the recipient. return this.viaHost;
|
public int | getViaPort()Gets the port of the recipient. return this.viaPort;
|
private void | inviteClientTransaction(Response transactionResponse, MessageChannel sourceChannel)Implements the state machine for invite client transactions.
int statusCode = transactionResponse.getStatusCode();
int currentState = getState();
if (currentState == TERMINATED_STATE) {
// Do nothing in the terminated state.
return;
} else if (currentState == CALLING_STATE) {
if (statusCode/100 == 2) {
// 200 responses are always seen by TU.
respondTo.processResponse(transactionResponse, this);
disableRetransmissionTimer();
disableTimeoutTimer();
setState(TERMINATED_STATE);
} else if (statusCode/100 == 1) {
disableRetransmissionTimer();
disableTimeoutTimer();
respondTo.processResponse(transactionResponse, this);
setState(PROCEEDING_STATE);
} else if (300 <= statusCode && statusCode <= 699) {
// When in either the "Calling" or "Proceeding" states,
// reception of response with status code from 300-699
// MUST cause the client transaction to
// transition to "Completed".
// The client transaction MUST pass the received response up to
// the TU, and the client transaction MUST generate an
// ACK request.
respondTo.processResponse(transactionResponse, this);
// Send back an ACK request
try {
sendMessage((Request) createAck());
} catch (SipException ex) {
InternalErrorHandler.handleException(ex);
}
if (! isReliable()) {
setState(COMPLETED_STATE);
enableTimeoutTimer(TIMER_D);
} else {
// Proceed immediately to the TERMINATED state.
setState(TERMINATED_STATE);
}
}
} else if (currentState == PROCEEDING_STATE) {
if (statusCode / 100 == 1) {
respondTo.processResponse(transactionResponse, this);
} else if (statusCode / 100 == 2) {
setState(TERMINATED_STATE);
respondTo.processResponse(transactionResponse, this);
} else if (300 <= statusCode && statusCode <= 699) {
respondTo.processResponse(transactionResponse, this);
// Send back an ACK request
try {
sendMessage((Request)createAck());
} catch (SipException ex) {
InternalErrorHandler.handleException(ex);
}
if (! isReliable()) {
setState(COMPLETED_STATE);
enableTimeoutTimer(TIMER_D);
} else {
setState(TERMINATED_STATE);
}
}
} else if (currentState == COMPLETED_STATE) {
if (300 <= statusCode && statusCode <= 699) {
// Send back an ACK request
try {
sendMessage((Request)createAck());
} catch (SipException ex) {
InternalErrorHandler.handleException(ex);
}
}
}
|
public boolean | isMessagePartOfTransaction(Message messageToHeaderTest)Deterines if the message is a part of this transaction.
return isMessageTransOrMult(messageToHeaderTest, false);
|
public boolean | isMessageTransOrMult(Message messageToHeaderTest)Deterines if the message is a part of this transaction or it is
multiple 2xx response.
return isMessageTransOrMult(messageToHeaderTest, true);
|
private boolean | isMessageTransOrMult(Message messageToHeaderTest, boolean checkMultResponse)Deterines if the message is a part of this transaction or it is
multiple 2xx response.
// List of Via headers in the message to test
ViaList viaHeaders = messageToHeaderTest.getViaHeaders();
// Flags whether the select message is part of this transaction
boolean transactionMatches;
String messageBranch = ((ViaHeader)viaHeaders.getFirst()).getBranch();
boolean rfc3261Compliant =
(getBranch() != null) &&
(messageBranch != null) &&
getBranch().startsWith(SIPConstants.GENERAL_BRANCH_MAGIC_COOKIE) &&
messageBranch.startsWith(SIPConstants.GENERAL_BRANCH_MAGIC_COOKIE);
/**
* if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
* Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
* "--------- TEST ------------");
* Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
* " testing " + this.getOriginalRequest());
* Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
* "Against " + messageToHeaderTest);
* Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
* "isTerminated = " + isTerminated());
* Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
* "messageBranch = " + messageBranch);
* Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
* "viaList = " + messageToHeaderTest.getViaHeaders());
* Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
* "myBranch = " + getBranch());
* }
*/
transactionMatches = false;
// Response 2xx should be processed even in TERMINATED state
// RFC 3261, 13.2.2.4:
// Multiple 2xx responses may arrive at the UAC for a single INVITE
// request due to a forking proxy.
boolean isResponse = messageToHeaderTest instanceof Response;
if (!isTerminated() ||
(checkMultResponse && isTerminated()
&& isResponse && isInviteTransaction() &&
(((Response)messageToHeaderTest).getStatusCode()/100 == 2))) {
if (rfc3261Compliant) {
if (viaHeaders != null) {
// If the branch parameter is the
// same as this transaction and the method is the same,
if (getBranch().equals
(((ViaHeader)viaHeaders.getFirst()).
getBranch())) {
transactionMatches =
getOriginalRequest().getCSeqHeader().
getMethod().equals
(messageToHeaderTest.getCSeqHeader().
getMethod());
}
}
} else {
transactionMatches =
getOriginalRequest().getTransactionId().equals
(messageToHeaderTest.getTransactionId());
}
}
return transactionMatches;
|
public boolean | isMultipleResponse(Response response)Deterines if the response is multiple (RFC 3261, 13.2.2.4).
boolean returnValue = false;
if ((response.getStatusCode()/100 == 2) && isTerminated()) {
Response lastResponse = getLastResponse();
if (lastResponse != null) {
String newTag = response.getToTag();
returnValue = !newTag.equals(lastResponse.getToTag());
}
}
return returnValue;
|
public boolean | isSecure()Checks if connection is secure. return encapsulatedChannel.isSecure();
|
private void | nonInviteClientTransaction(Response transactionResponse, MessageChannel sourceChannel)Implements the state machine for invite client transactions.
int currentState = getState();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"nonInviteClientTransaction " +
transactionResponse.getFirstLine());
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"currentState = " + currentState);
}
int statusCode = transactionResponse.getStatusCode();
if (currentState == TRYING_STATE) {
if (statusCode / 100 == 1) {
// Response to TU, RFC 3261, 17.1.4, figure 6
respondTo.processResponse(transactionResponse, this);
setState(PROCEEDING_STATE);
enableRetransmissionTimer
(MAXIMUM_RETRANSMISSION_TICK_COUNT);
enableTimeoutTimer(TIMER_F);
} else if (200 <= statusCode && statusCode <= 699) {
// Send the response up to the TU.
respondTo.processResponse(transactionResponse, this);
if (! isReliable()) {
setState(COMPLETED_STATE);
enableTimeoutTimer(TIMER_K);
} else {
setState(TERMINATED_STATE);
}
}
} else if (currentState == PROCEEDING_STATE &&
200 <= statusCode && statusCode <= 699) {
respondTo.processResponse(transactionResponse, this);
disableRetransmissionTimer();
disableTimeoutTimer();
if (! isReliable()) {
setState(COMPLETED_STATE);
enableTimeoutTimer(TIMER_K);
} else {
setState(TERMINATED_STATE);
}
} else if (currentState == PROCEEDING_STATE &&
statusCode / 100 == 1) {
// Response to TU, RFC 3261, 17.1.4, figure 6
respondTo.processResponse(transactionResponse, this);
}
|
public synchronized void | processResponse(Response transactionResponse, MessageChannel sourceChannel)Processes a new response message through this transaction.
If necessary, this message will also be passed onto the TU.
// Log the incoming response in our log file.
if (ServerLog.needsLogging(ServerLog.TRACE_MESSAGES))
this.logResponse(transactionResponse,
System.currentTimeMillis(), "normal processing");
int statusGroup = transactionResponse.getStatusCode()/100;
// Ignore 1xx
if (getState() == COMPLETED_STATE && statusGroup == 1) {
return;
// This block is against RFC 3261 17.1.1.2, figure 5 and
// 17.1.4, figure 6
/*
} else if (PROCEEDING_STATE == this.getState()
&& transactionResponse.getStatusCode() == 100) {
// Ignore 100 if received after 180
return;
*/
} else {
// IMPL_NOTE: investigate if this flag may be completely removed.
while (eventPending) {
try {
// Wait for clearEventPending() call.
wait();
} catch (InterruptedException e) {
// intentionally ignored
// wait for clearEventPending() call
}
}
}
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"processing " +
transactionResponse.getFirstLine()
+ "current state = "
+ getState());
}
this.lastResponse = transactionResponse;
if ((dialog != null) && (statusGroup < 3)) {
// add the route before you process the response.
dialog.addRoute(transactionResponse);
}
String method = transactionResponse.getCSeqHeader().getMethod();
if (dialog != null) {
boolean added = false;
SIPTransactionStack sipStackImpl
= (SIPTransactionStack) getSIPStack();
// A tag just got assigned or changed.
if (dialog.getRemoteTag() == null &&
transactionResponse.getTo().getTag() != null) {
// Dont assign tag on provisional response
if (transactionResponse.getStatusCode() != 100) {
dialog.setRemoteTag(transactionResponse.getToTag());
}
String dialogId = transactionResponse.getDialogId(false);
dialog.setDialogId(dialogId);
if (sipStackImpl.isDialogCreated(method) &&
transactionResponse.getStatusCode() != 100) {
sipStackImpl.putDialog(dialog);
if (statusGroup == 1) {
dialog.setState(Dialog.EARLY_STATE);
} else if (statusGroup == 2) {
dialog.setState(Dialog.CONFIRMED_STATE);
}
added = true;
}
} else if (dialog.getRemoteTag() != null &&
transactionResponse.getToTag() != null &&
! dialog.getRemoteTag().equals
(transactionResponse.getToTag())) {
dialog.setRemoteTag(transactionResponse.getToTag());
String dialogId = transactionResponse.getDialogId(false);
dialog.setDialogId(dialogId);
if (sipStackImpl.isDialogCreated(method)) {
sipStackImpl.putDialog(dialog);
added = true;
}
}
if (sipStackImpl.isDialogCreated(method)) {
// Make a final tag assignment.
if (transactionResponse.getToTag() != null &&
statusGroup == 2) {
// This is a dialog creating method (such as INVITE).
// 2xx response -- set the state to the confirmed
// state.
dialog.setRemoteTag(transactionResponse.getToTag());
dialog.setState(Dialog.CONFIRMED_STATE);
} else if ((
transactionResponse.getStatusCode() == 487 ||
statusGroup == 5 ||
statusGroup == 6) &&
(dialog.getState() == -1 ||
dialog.getState() ==
Dialog.EARLY_STATE)) {
// Invite transaction generated an error.
dialog.setState(Dialog.TERMINATED_STATE);
}
}
// 200 OK for a bye so terminate the dialog.
if (transactionResponse.getCSeqHeader().
getMethod().equals(Request.BYE) &&
transactionResponse.getStatusCode() == 200) {
dialog.setState(Dialog.TERMINATED_STATE);
}
}
try {
if (isInviteTransaction()) {
inviteClientTransaction(transactionResponse, sourceChannel);
} else {
nonInviteClientTransaction(transactionResponse, sourceChannel);
}
} catch (IOException ex) {
setState(TERMINATED_STATE);
raiseErrorEvent(SIPTransactionErrorEvent.TRANSPORT_ERROR);
}
|
public void | sendMessage(Message messageToHeaderSend)Sends a request message through this transaction and
onto the client.
// Message typecast as a request
Request transactionRequest;
transactionRequest = (Request)messageToHeaderSend;
// Set the branch id for the top via header.
ViaHeader topVia =
(ViaHeader) transactionRequest.getViaHeaders().getFirst();
// Tack on a branch identifier to match responses.
topVia.setBranch(getBranch());
// If this is the first request for this transaction,
if (getState() == INITIAL_STATE) {
// Save this request as the one this transaction
// is handling
setOriginalRequest(transactionRequest);
// Change to trying/calling state
if (transactionRequest.getMethod().equals(Request.INVITE)) {
setState(CALLING_STATE);
} else if (transactionRequest.getMethod().equals(Request.ACK)) {
// Acks are never retransmitted.
setState(TERMINATED_STATE);
} else {
setState(TRYING_STATE);
}
if (!isReliable()) {
enableRetransmissionTimer();
}
if (isInviteTransaction()) {
enableTimeoutTimer(TIMER_B);
} else {
enableTimeoutTimer(TIMER_F);
}
} else if (getState() == PROCEEDING_STATE ||
getState() == CALLING_STATE) {
// If this is a TU-generated ACK request,
if (transactionRequest.getMethod().equals(Request.ACK)) {
// Send directly to the underlying
// transport and close this transaction
setState(TERMINATED_STATE);
getMessageChannel().sendMessage(transactionRequest);
return;
}
}
try {
// Send the message to the server
lastRequest = transactionRequest;
getMessageChannel().sendMessage(transactionRequest);
} catch (IOException e) {
setState(TERMINATED_STATE);
throw e;
}
|
public void | sendRequest()Sends specified {@link gov.nist.siplite.message.Request} on a unique
client transaction identifier. This method implies that the application
is functioning as either a User Agent Client or a Stateful proxy, hence
the underlying SipProvider acts statefully.
JAIN SIP defines a retransmission utility specific to user agent
behaviour and the default retransmission behaviour for each method.
When an application wishes to send a message, it creates a Request
message passes that Request to this method, this method returns the
cleintTransactionId generated by the SipProvider. The Request message
gets sent via the ListeningPoint that this SipProvider is attached to.
- User Agent Client - must not send a BYE on a confirmed INVITE until
it has received an ACK for its 2xx response or until the server
transaction times out.
Request sipRequest = getOriginalRequest();
sendMessage(sipRequest);
|
public synchronized void | setEventPending()Sets the event pending flag.
eventPending = true;
|
public void | setResponseInterface(SIPServerResponseInterface newRespondToHeader)Sets the real ResponseInterface this transaction encapsulates.
respondTo = newRespondToHeader;
|
protected void | setViaHost(java.lang.String host)Sets the port of the recipient. this.viaHost = host;
|
protected void | setViaPort(int port)Sets the port of the recipient. this.viaPort = port;
|