disableRetransmissionTimer(); enableTimeoutTimer(TIMER_J);
} else if (TransactionState.COMPLETED == this.getRealState()) {
    public void sendMessage(SIPMessage messageToSend) throws IOException {        // Message typecast as a response        SIPResponse transactionResponse;        // Status code of the response being sent to the client        int statusCode;        // Get the status code from the response        transactionResponse = (SIPResponse) messageToSend;        statusCode = transactionResponse.getStatusCode();        SIPDialog dialog = this.dialog;        try {            // Provided we have set the banch id for this we set the BID for the            // outgoing via.            if (this.getOriginalRequest().getTopmostVia().getBranch() != null)                transactionResponse.getTopmostVia().setBranch(this.getBranch());            else                transactionResponse.getTopmostVia().removeParameter(                        ParameterNames.BRANCH);            // Make the topmost via headers match identically for the            // transaction            // rsponse.            if (!this.getOriginalRequest().getTopmostVia().hasPort())                transactionResponse.getTopmostVia().removePort();        } catch (ParseException ex) {            ex.printStackTrace();        }        // Method of the response does not match the request used to        // create the transaction - transaction state does not change.        if (!transactionResponse.getCSeq().getMethod().equals(                getOriginalRequest().getMethod())) {            sendResponse(transactionResponse);            return;        }        // Dialog state machine state adjustment.        if (this.dialog != null) {            if (this.dialog.getRemoteTag() == null                    && transactionResponse.getTo().getTag() != null                    && ((SIPTransactionStack) this.getSIPStack())                            .isDialogCreated(transactionResponse.getCSeq()                                    .getMethod())) {                this.dialog.setRemoteTag(transactionResponse.getTo().getTag());                ((SIPTransactionStack) this.getSIPStack())                        .putDialog(this.dialog);                if (statusCode / 100 == 1)                    this.dialog.setState(SIPDialog.EARLY_STATE);            }            if (((SIPTransactionStack) this.getSIPStack())                    .isDialogCreated(transactionResponse.getCSeq().getMethod())                    && transactionResponse.getCSeq().getMethod().equals(                            getOriginalRequest().getMethod())) {                               if (statusCode / 100 == 2) {                    // The state changes when the ACK is received for an invite                    // transaction                    // For other dialogs, the state changes when you send out                    // the response.                                        if (!this.isInviteTransaction()) {                        this.dialog.setState(SIPDialog.CONFIRMED_STATE);                    } else {                        if (this.dialog.getState() == null)                            this.dialog.setState(SIPDialog.EARLY_STATE);                    }                } else if (statusCode >= 300 && statusCode <= 699  &&		    ( this.dialog.getState() == null ||		      this.dialog.getState().equals(DialogState.EARLY))) {		    // The Dialog is terminated IFF this status code 		    // is detected when the Dialog is in the EARLY state		    // Bug was reported by Daniel Machin Vazquez-Illa                    // <dmachin@dit.upm.es>                    this.dialog.setState(SIPDialog.TERMINATED_STATE);                }            } else if (transactionResponse.getCSeq().getMethod().equals(                    Request.BYE)                    && statusCode / 100 == 2 && dialog != null) {                // Dialog will be terminated when the transction is terminated.                if (!isReliable())                    this.dialog.setState(SIPDialog.COMPLETED_STATE);                else                    this.dialog.setState(SIPDialog.TERMINATED_STATE);            }        }        // If the TU sends a provisional response while in the        // trying state,        if (getRealState() == TransactionState.TRYING) {            if (statusCode / 100 == 1) {                this.setState(TransactionState.PROCEEDING);            } else if (200 <= statusCode && statusCode <= 699) {                // Check -- bug report from christophe                if (!isInviteTransaction()) {                    this.setState(TransactionState.COMPLETED);                } else {                    if (statusCode / 100 == 2) {                        this.setState(TransactionState.TERMINATED);                    } else                        this.setState(TransactionState.COMPLETED);                }                if (!isReliable()) {                    enableRetransmissionTimer();                }                enableTimeoutTimer(TIMER_J);            }            // If the transaction is in the proceeding state,        } else if (getRealState() == TransactionState.PROCEEDING) {            if (isInviteTransaction()) {                // If the response is a failure message,                if (statusCode / 100 == 2) {                    // Set up to catch returning ACKs                    // Antonis Karydas: Suggestion                    // Recall that the CANCEL's response will go                    // through this transaction                    // and this may well be it. Do NOT change the                    // transaction state if this                    // is a response for a CANCEL.                    // Wait, instead for the 487 from TU.                    if (!transactionResponse.getCSeq().getMethod().equals(                            Request.CANCEL)) {                        this.collectionTime = TIMER_J;                        this.setState(TransactionState.TERMINATED);                        if (!isReliable()) {			    // test for dialog existance ( Bug report			    // from Bill Roome )			    if ( this.dialog != null) 					this.dialog.setRetransmissionTicks();                            enableRetransmissionTimer();                        }                        enableTimeoutTimer(TIMER_J);                    }                } else if (300 <= statusCode && statusCode <= 699) {                    // Set up to catch returning ACKs                    this.setState(TransactionState.COMPLETED);                    if (!isReliable()) {                        enableRetransmissionTimer();                    }                    // Changed to TIMER_H as suggested by                    // Antonis Karydas                    enableTimeoutTimer(TIMER_H);                    // If the response is a success message,                } else if (statusCode / 100 == 2) {                    // Terminate the transaction                    this.setState(TransactionState.TERMINATED);                    disableRetransmissionTimer();                    disableTimeoutTimer();                }                // If the transaction is not an invite transaction                // and this is a final response,            } else if (200 <= statusCode && statusCode <= 699) {                // Set up to retransmit this response,                // or terminate the transaction                this.setState(TransactionState.COMPLETED);                if (!isReliable()) {                    disableRetransmissionTimer();                    enableTimeoutTimer(TIMER_J);                } else {                    this.setState(TransactionState.TERMINATED);                }            }            // If the transaction has already completed,        } else if (TransactionState.COMPLETED == this.getRealState()) {            return;        }        try {            // Send the message to the client.            // Record the last message sent out.            lastResponse = transactionResponse;            this.sendResponse(transactionResponse);        } catch (IOException e) {            this.setState(TransactionState.TERMINATED);            this.collectionTime = 0;            throw e;        }    }