/*
* Portions Copyright 2000-2009 Sun Microsystems, Inc. All Rights
* Reserved. Use is subject to license terms.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version
* 2 only, as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License version 2 for more details (a copy is
* included at /legal/license.txt).
*
* You should have received a copy of the GNU General Public License
* version 2 along with this work; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 or visit www.sun.com if you need additional
* information or have any questions.
*/
package gov.nist.siplite.stack;
import gov.nist.microedition.sip.SipClientConnectionImpl;
import gov.nist.siplite.message.*;
import gov.nist.siplite.header.*;
import gov.nist.siplite.address.*;
import gov.nist.core.*;
import java.util.*;
import java.io.IOException;
import com.sun.j2me.log.Logging;
import com.sun.j2me.log.LogChannels;
/**
* Adds a transaction layer to the {@link SIPMessageStack} class. This
* is done by
* replacing the normal MessageChannels returned by the base class with
* transaction-aware MessageChannels that encapsulate the original channels
* and handle the transaction state machine, retransmissions, etc.
*
* <a href="{@docRoot}/uncopyright.html">This code is in the public domain.</a>
*
* @version JAIN-SIP-1.1
*
*/
public abstract class SIPTransactionStack
extends SIPMessageStack implements SIPTransactionEventListener {
/**
* Number of milliseconds between timer ticks (500).
*/
public static final int BASE_TIMER_INTERVAL = 500;
/** Collection of current client transactions. */
private Vector clientTransactions;
/** Collection or current server transactions. */
private Vector serverTransactions;
/** Table of dialogs. */
private Hashtable dialogTable;
/** Max number of server transactions concurrent. */
protected int transactionTableSize;
/**
* Retransmission filter - indicates the stack will retransmit
* 200 OK for invite transactions.
*/
protected boolean retransmissionFilter;
/** A set of methods that result in dialog creations. */
static protected Hashtable dialogCreatingMethods;
static {
// a set of methods that result in dialog creation.
dialogCreatingMethods = new Hashtable();
// Standard set of methods that create dialogs.
dialogCreatingMethods.put(Request.REFER, "");
dialogCreatingMethods.put(Request.INVITE, "");
dialogCreatingMethods.put(Request.SUBSCRIBE, "");
}
/** Default constructor. */
protected SIPTransactionStack() {
super();
this.transactionTableSize = -1;
// Notify may or may not create a dialog. This is handled in
// the code.
// this.dialogCreatingMethods.add(Request.NOTIFY);
// this.dialogCreatingMethods.put(Request.MESSAGE, "");
// Create the transaction collections
clientTransactions = new Vector();
serverTransactions = new Vector();
// Dialog dable.
this.dialogTable = new Hashtable();
// Start the timer event thread.
// System.out.println("Starting timeout");
new Thread(new TransactionScanner()).start();
}
/**
* Prints the Dialog creating methods.
*/
private void printDialogCreatingMethods() {
System.out.println("PRINTING DIALOGCREATINGMETHODS HASHTABLE");
Enumeration e = dialogCreatingMethods.keys();
while (e.hasMoreElements()) {
System.out.println(e.nextElement());
}
System.out.println("DIALOGCREATINGMETHODS HASHTABLE PRINTED");
}
/**
* Returns true if extension is supported.
* @param method the name of the method used for create
* @return true if extension is supported and false otherwise.
*/
static public boolean isDialogCreated(String method) {
// printDialogCreatingMethods();
// System.out.println("CHECKING IF DIALOG HAS BEEN CREATED"
// + " FOR THE FOLLOWING METHOD"
// + method.toUpperCase());
return dialogCreatingMethods.containsKey(method.toUpperCase());
}
/**
* Returns true if method can change dialog state.
* @param method the name of the method used for create
* @return true if extension is supported and false otherwise.
*/
public boolean allowDialogStateChange(String method) {
return dialogCreatingMethods.containsKey(method.toUpperCase()) ||
method.equalsIgnoreCase(Request.BYE) ||
method.equalsIgnoreCase(Request.NOTIFY);
}
/**
* Adds an extension method.
* @param extensionMethod -- extension method to support for dialog
* creation
*/
public void addExtensionMethod(String extensionMethod) {
if (! extensionMethod.equals(Request.NOTIFY)) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"NOTIFY Supported Natively");
}
} else {
this.dialogCreatingMethods.put(extensionMethod, "");
}
}
/**
* Puts a dialog into the dialog table.
* @param dialog -- dialog to put into the dialog table.
*/
public void putDialog(Dialog dialog) {
String dialogId = dialog.getDialogId();
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"putDialog dialogId=" + dialogId);
}
// if (this.getDefaultRouteHeader() != null)
// dialog.addRoute(this.getDefaultRouteHeader(), false);
dialog.setStack(this);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
// new Exception().printStackTrace();
}
synchronized (dialogTable) {
dialogTable.put(dialogId, dialog);
}
}
/**
* Creates a new dialog for requested transaction.
* @param transaction the requested transaction
* @return the new Dialog object
*/
public synchronized Dialog
createDialog(Transaction transaction) {
Request sipRequest = transaction.getOriginalRequest();
Dialog retval = new Dialog(transaction);
return retval;
}
/**
* Returns the dialog for a given dialog ID. If compatibility is
* enabled then we do not assume the presence of tags and hence
* need to add a flag to indicate whether this is a server or
* client transaction.
* @param dialogId is the dialog id to check.
* @return the Dialog object for the requested id
*/
public Dialog getDialog(String dialogId) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"Getting dialog for " + dialogId);
}
synchronized (dialogTable) {
return (Dialog)dialogTable.get(dialogId);
}
}
/**
* Finds a matching client SUBSCRIBE to the incoming notify.
* NOTIFY requests are matched to such SUBSCRIBE requests if they
* contain the same "Call-ID", a "ToHeader" header "tag" parameter which
* matches the "FromHeader" header "tag" parameter of the SUBSCRIBE, and the
* same "Event" header field. Rules for comparisons of the "Event"
* headers are described in section 7.2.1. If a matching NOTIFY request
* contains a "Subscription-State" of "active" or "pending", it creates
* a new subscription and a new dialog (unless they have already been
* created by a matching response, as described above).
*
* @param notifyMessage the request to be matched
* @return the new client transaction object
*/
public ClientTransaction findSubscribeTransaction
(Request notifyMessage) {
synchronized (clientTransactions) {
Enumeration it = clientTransactions.elements();
String thisToHeaderTag = notifyMessage.getTo().getTag();
if (thisToHeaderTag == null)
return null;
EventHeader eventHdr =
(EventHeader)notifyMessage.getHeader(Header.EVENT);
if (eventHdr == null)
return null;
while (it.hasMoreElements()) {
ClientTransaction ct =
(ClientTransaction)it.nextElement();
Request sipRequest = ct.getOriginalRequest();
String fromTag = sipRequest.getFromHeader().getTag();
EventHeader hisEvent =
(EventHeader)sipRequest.getHeader(Header.EVENT);
// Event header is mandatory but some slopply clients
// dont include it.
if (hisEvent == null) continue;
if (sipRequest.getMethod().equals(Request.SUBSCRIBE) &&
Utils.equalsIgnoreCase(fromTag, thisToHeaderTag) &&
hisEvent != null && eventHdr.match(hisEvent) &&
Utils.equalsIgnoreCase
(notifyMessage.getCallId().getCallId(),
sipRequest.getCallId().getCallId()))
return ct;
}
}
return null;
}
/**
* Finds the transaction corresponding to a given request.
* @param sipMessage request for which to retrieve the transaction.
* @param isServer search the server transaction table if true.
* @return the transaction object corresponding to the request or null
* if no such mapping exists.
*/
public Transaction
findTransaction(Message sipMessage, boolean isServer) {
if (isServer) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"searching server transaction for "
+ sipMessage + " size = " +
this.serverTransactions.size());
}
synchronized (this.serverTransactions) {
Enumeration it = serverTransactions.elements();
while (it.hasMoreElements()) {
ServerTransaction sipServerTransaction =
(ServerTransaction)it.nextElement();
if (sipServerTransaction
.isMessagePartOfTransaction(sipMessage))
return sipServerTransaction;
}
}
} else {
synchronized (this.clientTransactions) {
Enumeration it = clientTransactions.elements();
while (it.hasMoreElements()) {
ClientTransaction clientTransaction =
(ClientTransaction)it.nextElement();
if (clientTransaction
.isMessagePartOfTransaction(sipMessage))
return clientTransaction;
}
}
}
return null;
}
/**
* Gets the transaction to cancel. Search the server transaction
* table for a transaction that matches the given transaction.
* @param cancelRequest the request to be found
* @param isServer true if this is a server request
* @return the transaction object requested
*/
public Transaction
findCancelTransaction(Request cancelRequest, boolean isServer) {
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"findCancelTransaction request = \n"
+ cancelRequest +
"\nfindCancelRequest isServer = " + isServer);
}
if (isServer) {
synchronized (this.serverTransactions) {
Enumeration li = this.serverTransactions.elements();
while (li.hasMoreElements()) {
Transaction transaction =
(Transaction)li.nextElement();
Request sipRequest =
(Request) (transaction.getRequest());
ServerTransaction sipServerTransaction =
(ServerTransaction) transaction;
if (sipServerTransaction.doesCancelMatchTransaction
(cancelRequest))
return sipServerTransaction;
}
}
} else {
synchronized (this.clientTransactions) {
Enumeration li = this.clientTransactions.elements();
while (li.hasMoreElements()) {
Transaction transaction = (Transaction)li.nextElement();
Request sipRequest =
(Request) (transaction.getRequest());
ClientTransaction sipClientTransaction =
(ClientTransaction) transaction;
if (sipClientTransaction.doesCancelMatchTransaction
(cancelRequest))
return sipClientTransaction;
}
}
}
return null;
}
/**
* Construcor for the stack. Registers the request and response
* factories for the stack.
* @param messageFactory User-implemented factory for processing
* messages.
*/
protected SIPTransactionStack(SIPStackMessageFactory messageFactory) {
this();
super.sipMessageFactory = messageFactory;
}
/**
* Thread used to throw timer events for all transactions.
*/
class TransactionScanner implements Runnable {
/**
* Main transaction scanner processing loop.
*/
public void run() {
// Iterator through all transactions
Enumeration transactionIterator;
// One transaction in the set
Transaction nextTransaction;
// Loop while this stack is running
while (isAlive()) {
try {
// Sleep for one timer "tick"
Thread.sleep(BASE_TIMER_INTERVAL);
// System.out.println("clientTransactionTable size " +
// clientTransactions.size());
// System.out.println("serverTransactionTable size " +
// serverTransactions.size());
// Check all client transactions
Vector fireList = new Vector();
Vector removeList = new Vector();
// Check all server transactions
synchronized (serverTransactions) {
transactionIterator =
serverTransactions.elements();
while (transactionIterator.hasMoreElements()) {
nextTransaction =
(Transaction)
transactionIterator.nextElement();
// If the transaction has terminated,
if (nextTransaction.isTerminated()) {
// Keep the transaction hanging around
// to catch the incoming ACK.
if (((ServerTransaction)nextTransaction).
collectionTime == 0) {
// Remove it from the set
if (Logging.REPORT_LEVEL <=
Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180,
"removing" + nextTransaction);
}
removeList.addElement(nextTransaction);
} else {
((ServerTransaction)nextTransaction).
collectionTime --;
}
// If this transaction has not
// terminated,
} else {
// Add to the fire list -- needs to be moved
// outside the synchronized block to prevent
// deadlock.
fireList.addElement(nextTransaction);
}
}
for (int j = 0; j < removeList.size(); j++) {
serverTransactions.removeElement
(removeList.elementAt(j));
}
}
removeList = new Vector();
synchronized (clientTransactions) {
transactionIterator = clientTransactions.elements();
while (transactionIterator.hasMoreElements()) {
nextTransaction = (Transaction)
transactionIterator.nextElement();
// If the transaction has terminated,
// and SipClientConnection instance,
// associated with it has TERMINATED state
boolean removeTransaction = false;
if (nextTransaction.isTerminated()) {
SipClientConnectionImpl conn =
(SipClientConnectionImpl)
nextTransaction.getApplicationData();
if (conn == null) { // no connection
removeTransaction = true;
} else if (conn.getState() ==
SipClientConnectionImpl.TERMINATED) {
removeTransaction = true;
}
}
if (removeTransaction) {
// Remove it from the set
if (Logging.REPORT_LEVEL <=
Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180,
"Removing clientTransaction " +
nextTransaction);
}
removeList.addElement(nextTransaction);
// If this transaction has not
// terminated,
} else {
// Add to the fire list -- needs to be moved
// outside the synchronized block to prevent
// deadlock.
fireList.addElement(nextTransaction);
}
}
for (int j = 0; j < removeList.size(); j++) {
clientTransactions.removeElement
(removeList.elementAt(j));
}
}
removeList = new Vector();
synchronized (dialogTable) {
Enumeration values = dialogTable.elements();
while (values.hasMoreElements()) {
Dialog d = (Dialog) values.nextElement();
// System.out.println("dialogState = " +
// d.getState() +
// " isServer = " + d.isServer());
if (d.getState() ==
Dialog.TERMINATED_STATE) {
if (Logging.REPORT_LEVEL <=
Logging.INFORMATION) {
String dialogId =
d.getDialogId();
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180,
"Removing Dialog " +
dialogId);
}
removeList.addElement(d);
}
if (d.isServer() && (! d.ackSeen) &&
d.isInviteDialog()) {
Transaction transaction =
d.getLastTransaction();
// If stack is managing the transaction
// then retransmit the last response.
if (transaction.getState() ==
Transaction.TERMINATED_STATE
&& transaction instanceof
ServerTransaction
&& ((ServerTransaction) transaction)
.isMapped) {
Response response =
transaction.getLastResponse();
// Retransmit to 200 until ack received.
if (response.getStatusCode() == 200) {
try {
if (d.toRetransmitFinalResponse())
transaction
.sendMessage(response);
} catch (IOException ex) {
/* Will eventully time out */
d.setState(Dialog.TERMINATED_STATE);
if (response.getErrorListener() !=
null) {
response.getErrorListener().
notifyError("Failed to " +
"retransmit the following " +
"response: " + response);
}
} finally {
// Need to fire the timer so
// transaction will eventually
// time out whether or not
// the IOException occurs
fireList.addElement(transaction);
}
}
}
}
}
for (int j = 0; j < removeList.size(); j++) {
Dialog d =
(Dialog)removeList.elementAt(j);
dialogTable.remove(d.getDialogId());
}
}
for (int i = 0; i < fireList.size(); i++) {
nextTransaction =
(Transaction) fireList.elementAt(i);
nextTransaction.fireTimer();
}
} catch (Exception e) {
e.printStackTrace();
// Ignore
}
}
}
}
/**
* Creates or terminates a dialog if a subscription matching the given
* NOTIFY request exists. To create a dialog a list of transactions
* is searched for the matching transaction and then the corresponding
* SipClientConnection is notified. To terminate a dialog a list of
* dialogs is searched for the existing subscription that matches
* the given NOTIFY request.
* @param requestReceived NOTIFY request that may create or terminate
* a dialog.
*/
private void createOrTerminateDialog(Request requestReceived) {
// System.out.println(">>> NOTIFY: Scanning dialogs...");
synchronized (dialogTable) {
Enumeration e = dialogTable.elements();
while (e.hasMoreElements()) {
Dialog nextDialog = (Dialog)e.nextElement();
Subscription s = nextDialog.subscriptionList.
getMatchingSubscription(requestReceived);
if (s != null) {
SubscriptionStateHeader ssh = (SubscriptionStateHeader)
requestReceived.getHeader(Header.SUBSCRIPTION_STATE);
if (ssh != null && ssh.isTerminated()) {
nextDialog.subscriptionList.removeSubscription(s);
} else {
nextDialog.setState(Dialog.CONFIRMED_STATE);
}
return;
}
}
}
// System.out.println(">>> NOTIFY: Scanning transactions...");
// Iterator through all client transactions
Enumeration transactionIterator;
ClientTransaction currClientTransaction = null;
// Loop through all client transactions
synchronized (clientTransactions) {
transactionIterator = clientTransactions.elements();
currClientTransaction = null;
String receivedToTag = requestReceived.getToTag();
CallIdHeader receivedCid = requestReceived.getCallId();
EventHeader receivedEvent = (EventHeader)
requestReceived.getHeader(Header.EVENT);
while (transactionIterator.hasMoreElements()) {
currClientTransaction =
(ClientTransaction)transactionIterator.nextElement();
Request request = currClientTransaction.getRequest();
String method = request.getMethod();
String fromTag = request.getFromHeaderTag();
CallIdHeader cid = request.getCallId();
EventHeader hEvent = (EventHeader)
request.getHeader(Header.EVENT);
boolean isSameEvent = (hEvent != null) &&
hEvent.match(receivedEvent);
if (((method.equals(Request.SUBSCRIBE) && isSameEvent) ||
method.equals(Request.REFER)) &&
fromTag != null && fromTag.equals(receivedToTag) &&
cid != null && cid.equals(receivedCid)) {
SipClientConnectionImpl sipClientConnection =
(SipClientConnectionImpl)
currClientTransaction.getApplicationData();
if (sipClientConnection != null) {
sipClientConnection.handleMatchingNotify(
requestReceived);
} else {
if (Logging.REPORT_LEVEL <= Logging.WARNING) {
Logging.report(Logging.WARNING,
LogChannels.LC_JSR180,
"SIPTransactionStack.createOrTerminateDialog(): " +
"Cannot find SCC for the given NOTIFY.");
}
}
break;
}
}
}
}
/**
* Handles a new SIP request.
* It finds a server transaction to handle
* this message. If none exists, it creates a new transaction.
* @param requestReceived Request to handle.
* @param requestMessageChannel Channel that received message.
* @return A server transaction.
*/
protected SIPServerRequestInterface
newSIPServerRequest(Request requestReceived,
MessageChannel requestMessageChannel) {
try {
// Iterator through all server transactions
Enumeration transactionIterator;
// Next transaction in the set
ServerTransaction nextTransaction;
// Transaction to handle this request
ServerTransaction currentTransaction = null;
if (requestReceived.getMethod().equals(Request.NOTIFY)) {
createOrTerminateDialog(requestReceived);
}
// Loop through all server transactions
synchronized (serverTransactions) {
transactionIterator = serverTransactions.elements();
currentTransaction = null;
while (transactionIterator.hasMoreElements() &&
currentTransaction == null) {
nextTransaction =
(ServerTransaction)transactionIterator.nextElement();
// If this transaction should handle this request,
if (!nextTransaction.isTerminated() &&
nextTransaction.isMessagePartOfTransaction(
requestReceived)) {
// Mark this transaction as the one
// to handle this message
currentTransaction = nextTransaction;
}
}
// If no transaction exists to handle this message
if (currentTransaction == null) {
currentTransaction =
createServerTransaction(requestMessageChannel);
currentTransaction.setOriginalRequest(requestReceived);
if (!isDialogCreated(requestReceived.getMethod())) {
// Server transactions must always be added
// to the corresponding vector.
serverTransactions.addElement(currentTransaction);
currentTransaction.isMapped = true;
} else {
// Create the transaction but dont map it.
String dialogId = requestReceived.getDialogId(true);
Dialog dialog = getDialog(dialogId);
if (dialog == null) {
// This is a dialog creating request
dialog = createDialog(currentTransaction);
dialog.setStack(this);
dialog.addRoute(requestReceived);
if (dialog.getRemoteTag() != null &&
dialog.getLocalTag() != null) {
putDialog(dialog);
currentTransaction.setDialog(dialog);
}
} else {
// This is a dialog creating request that is
// part of an existing dialog
// (eg. re-Invite). Re-invites get a non null
// server transaction Id (unlike the original
// invite).
if (requestReceived.getCSeqHeader().getSequenceNumber()
> dialog.getRemoteSequenceNumber()) {
try {
currentTransaction.map();
} catch (IOException ex) {
/* Ignore */
}
}
}
serverTransactions.addElement(currentTransaction);
currentTransaction.toListener = true;
}
}
// attach to request
requestReceived.setTransaction(currentTransaction);
// Set ths transaction's encapsulated request
// interface from the superclass
currentTransaction.setRequestInterface
(super.newSIPServerRequest
(requestReceived, currentTransaction));
return currentTransaction;
}
} catch (RuntimeException ex) {
ex.printStackTrace();
throw ex;
}
}
/**
* Handles a new SIP response.
* It finds a client transaction to handle
* this message. If none exists, it sends the message directly to the
* superclass.
* @param responseReceived Response to handle.
* @param responseMessageChannel Channel that received message.
* @return A client transaction.
*/
SIPServerResponseInterface
newSIPServerResponse(Response responseReceived,
MessageChannel responseMessageChannel) {
// System.out.println("response = " + responseReceived.encode());
// Iterator through all client transactions
Enumeration transactionIterator;
// Next transaction in the set
ClientTransaction nextTransaction;
// Transaction to handle this request
ClientTransaction currentTransaction;
// Loop through all client transactions
synchronized (clientTransactions) {
transactionIterator = clientTransactions.elements();
currentTransaction = null;
int i = -1;
while (transactionIterator.hasMoreElements() &&
currentTransaction == null) {
i++;
nextTransaction =
(ClientTransaction)transactionIterator.nextElement();
// If this transaction should handle this request,
if (nextTransaction.isMessageTransOrMult(responseReceived)) {
if (nextTransaction.isMultipleResponse(responseReceived)) {
// RFC 3261, 13.2.2.4:
// Multiple 2xx responses may arrive at the UAC for
// a single INVITE request due to a forking proxy.
// create a new client transaction
currentTransaction =
nextTransaction.cloneWithNewLastResponse
(responseReceived);
currentTransaction.setState
(Transaction.PROCEEDING_STATE);
currentTransaction.setApplicationData
(nextTransaction.getApplicationData());
Dialog dialog = new Dialog(currentTransaction);
dialog.setDialogId(responseReceived.getDialogId(false));
dialog.setRemoteTag(responseReceived.getToTag());
dialog.setStack(this);
putDialog(dialog);
currentTransaction.setDialog(dialog);
// change from old to new transaction
clientTransactions.setElementAt(currentTransaction, i);
} else { // not multiple response
// Mark this transaction as the one to
// handle this message
currentTransaction = nextTransaction;
}
}
}
}
// If no transaction exists to handle this message,
if (currentTransaction == null) {
// Pass the message directly to the TU
return super.newSIPServerResponse
(responseReceived, responseMessageChannel);
}
// Set ths transaction's encapsulated response interface
// from the superclass
currentTransaction.setResponseInterface(super.newSIPServerResponse
(responseReceived,
currentTransaction));
return currentTransaction;
}
/**
* Creates a client transaction to handle a new request.
* Gets the real
* message channel from the superclass, and then creates a new client
* transaction wrapped around this channel.
* @param nextHop Hop to create a channel to contact.
* @return the requested message channel
*/
public MessageChannel createMessageChannel(Hop nextHop) {
synchronized (clientTransactions) {
// New client transaction to return
Transaction returnChannel;
// Create a new client transaction around the
// superclass' message channel
MessageChannel mc = super.createMessageChannel(nextHop);
if (mc == null)
return null;
returnChannel =
createClientTransaction(mc);
clientTransactions.addElement(returnChannel);
((ClientTransaction)returnChannel).setViaPort(nextHop.getPort());
((ClientTransaction)returnChannel).setViaHost(nextHop.getHost());
return returnChannel;
}
}
/**
* Creates a client transaction from a raw channel.
* @param rawChannel is the transport channel to encapsulate.
* @return the requested message channel
*/
public MessageChannel createMessageChannel
(MessageChannel rawChannel) {
synchronized (clientTransactions) {
// New client transaction to return
Transaction returnChannel =
createClientTransaction(rawChannel);
clientTransactions.addElement(returnChannel);
((ClientTransaction)returnChannel).setViaPort
(rawChannel.getViaPort());
((ClientTransaction)returnChannel).setViaHost
(rawChannel.getHost());
return returnChannel;
}
}
/**
* Creates a client transaction from a raw channel.
* @param transaction the requested transaction
* @return the requested message channel
*/
public MessageChannel createMessageChannel
(Transaction transaction) {
synchronized (clientTransactions) {
// New client transaction to return
Transaction returnChannel =
createClientTransaction(transaction.getMessageChannel());
clientTransactions.addElement(returnChannel);
((ClientTransaction)returnChannel).setViaPort
(transaction.getViaPort());
((ClientTransaction)returnChannel).setViaHost
(transaction.getViaHost());
return returnChannel;
}
}
/**
* Creates a client transaction that encapsulates a MessageChannel.
* Useful for implementations that want to subclass the standard
* @param encapsulatedMessageChannel
* Message channel of the transport layer.
* @return the requested client transaction
*/
public ClientTransaction
createClientTransaction(MessageChannel
encapsulatedMessageChannel) {
return new ClientTransaction
(this, encapsulatedMessageChannel);
}
/**
* Creates a server transaction that encapsulates a MessageChannel.
* Useful for implementations that want to subclass the standard
* @param encapsulatedMessageChannel
* Message channel of the transport layer.
* @return the requested server transaction
*/
public ServerTransaction
createServerTransaction(MessageChannel encapsulatedMessageChannel) {
return new ServerTransaction
(this, encapsulatedMessageChannel);
}
/**
* Creates a raw message channel. A raw message channel has no
* transaction wrapper.
* @param hop hop for which to create the raw message channel.
* @return the requested message channel
*/
public MessageChannel createRawMessageChannel(Hop hop) {
return super.createMessageChannel(hop);
}
/**
* Adds a new client transaction to the set of existing transactions.
* @param clientTransaction -- client transaction to add to the set.
*/
public void addTransaction
(ClientTransaction clientTransaction) {
synchronized (clientTransactions) {
clientTransactions.addElement(clientTransaction);
}
}
/**
* Adds a new client transaction to the set of existing transactions.
* @param serverTransaction -- server transaction to add to the set.
*/
public void addTransaction
(ServerTransaction serverTransaction) throws IOException {
synchronized (serverTransactions) {
this.serverTransactions.addElement(serverTransaction);
}
}
}