Methods Summary |
---|
public void | addSipListener(SipListener sipListener)This method registers the SipListener object to this SipProvider, once
registered the SIP Listener can send events on the SipProvider and
recieve events emitted from the SipProvider. As JAIN SIP resticts a
unicast Listener special case, that is, that one and only one Listener
may be registered on the SipProvider concurrently.
If an attempt is made to re-register the existing SipListener this
method returns silently. A previous SipListener must be removed from the
SipProvider before another SipListener can be registered to
the SipProvider.
synchronized (sipStack) {
Enumeration it = sipStack.getSipProviders();
while (it.hasMoreElements()) {
SipProvider provider = (SipProvider) it.nextElement();
if (provider.sipListener != null &&
provider.sipListener != sipListener)
throw new TooManyListenersException();
}
}
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"add SipListener " + sipListener);
}
this.sipListener = sipListener;
sipStack.sipListener = sipListener;
synchronized (sipStack) {
Enumeration it = sipStack.getSipProviders();
while (it.hasMoreElements()) {
SipProvider provider = (SipProvider) it.nextElement();
provider.sipListener = sipListener;
}
}
|
public boolean | equals(java.lang.Object obj)Compares to this instance for equivalence.
return super.equals(obj);
|
public void | equipADialogForTransaction(ServerTransaction transaction, Request sipRequest)Find or create a dialog with the dialog-id obtained from the request.
Initializing the dialog's route information.
Bind the dialog and the transaction to each other.
// So I can handle timeouts.
// IMPL_NOTE: do we need it here, or in the calling code?
transaction.addEventListener(this);
String dialogId = sipRequest.getDialogId(true);
Dialog dialog = sipStack.getDialog(dialogId);
if (dialog == null) {
dialog = sipStack.createDialog(transaction);
}
dialog.setStack(this.sipStack);
dialog.addRoute(sipRequest);
if (dialog.getRemoteTag() != null &&
dialog.getLocalTag() != null) {
this.sipStack.putDialog(dialog);
}
transaction.setDialog(dialog);
|
public ListeningPoint | getListeningPoint()Returns the ListeningPoint of this SipProvider.
A SipProvider has a single Listening Point at any specific point in time.
return this.listeningPoint;
|
public CallIdHeader | getNewCallId()Returns a unique CallIdHeader for identifying dialogues between two
SIP applications.
String callId = SIPUtils.generateCallIdentifier
(this.getSipStack().getIPAddress());
CallIdHeader callid = new CallIdHeader();
callid.setCallId(callId);
return callid;
|
public ClientTransaction | getNewClientTransaction(Request request)Once an application wants to a send a new request it must first request
a new client transaction identifier. This method is called by an
application to create the client transaction befores it sends the Request
via the SipProvider on that transaction. This methods returns a new
unique client transaction identifier that can be passed to the stateful
sendRequest method on the SipProvider and the sendAck/sendBye
methods on the Dialog in order to send a request.
if (request == null) {
throw new NullPointerException("null request");
}
if (request.getTransaction() != null) {
throw new TransactionUnavailableException
("Transaction already assigned to request");
}
if (request.getMethod().equals(Request.CANCEL)) {
ClientTransaction ct = (ClientTransaction)
sipStack.findCancelTransaction(request, false);
if (ct != null) {
ClientTransaction retval = sipStack.createClientTransaction
(ct.getMessageChannel());
((Transaction)retval).setOriginalRequest(request);
((Transaction)retval).addEventListener(this);
sipStack.addTransaction((ClientTransaction)retval);
((ClientTransaction)retval).setDialog((Dialog)ct.getDialog());
return retval;
}
}
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"could not find existing transaction for "
+ request.getFirstLine());
}
String dialogId = request.getDialogId(false);
Dialog dialog = sipStack.getDialog(dialogId);
// isDialog - is request sends inside dialog
boolean isDialog = false;
if (dialog != null) {
int dialogState = dialog.getState();
if (dialogState == Dialog.EARLY_STATE ||
dialogState == Dialog.CONFIRMED_STATE ||
dialogState == Dialog.COMPLETED_STATE) {
isDialog = true;
}
}
Enumeration it = sipStack.getRouter().getNextHops(request, isDialog);
if (it == null || !it.hasMoreElements()) {
// could not route the request as out of dialog.
// maybe the user has no router or the router cannot resolve
// the route.
// If this is part of a dialog then use the route from the dialog
if (dialog != null) {
try {
Hop hop = dialog.getNextHop();
if (hop != null) {
ClientTransaction ct = null;
ct = (ClientTransaction) sipStack
.createMessageChannel(hop);
String branchId = SIPUtils.generateBranchId();
if (request.getTopmostVia() != null) {
request.getTopmostVia().setBranch(branchId);
} else {
// Find a message processor to assign this
// transaction to.
MessageProcessor messageProcessor =
this.listeningPoint.messageProcessor;
ViaHeader via = messageProcessor.getViaHeader();
request.addHeader(via);
}
ct.setOriginalRequest(request);
ct.setBranch(branchId);
ct.setDialog(dialog);
ct.addEventListener(this);
return ct;
} // end if
} catch (Exception ex) {
throw new TransactionUnavailableException(ex.getMessage());
}
} else {
throw new TransactionUnavailableException("no route!");
}
} else {
try {
// An out of dialog route was found. Assign this to the
// client transaction.
while (it.hasMoreElements()) {
Hop hop = (Hop) it.nextElement();
ClientTransaction ct = null;
ct = (ClientTransaction) sipStack
.createMessageChannel(hop);
// ClientTransaction ct =
// (ClientTransaction) sipStack.createMessageChannel(hop);
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION,
LogChannels.LC_JSR180, "hop = " + hop + "ct " + ct);
}
if (ct == null) continue;
String branchId = SIPUtils.generateBranchId();
if (request.getTopmostVia() != null) {
request.getTopmostVia().setBranch(branchId);
} else {
// Find a message processor to assign
// this transaction to.
MessageProcessor messageProcessor =
listeningPoint.messageProcessor;
ViaHeader via = messageProcessor.getViaHeader();
request.addHeader(via);
}
ct.setOriginalRequest(request);
ct.setBranch(branchId);
if (sipStack.isDialogCreated(request.getMethod())) {
// create a new dialog to contain this transaction
// provided this is necessary.
// This could be a re-invite
// (noticed by Brad Templeton)
if (dialog != null) {
ct.setDialog(dialog);
} else {
sipStack.createDialog(ct);
}
} else {
ct.setDialog(dialog);
}
// The provider is the event listener for all transactions.
ct.addEventListener(this);
return (ClientTransaction) ct;
} // end while()
} catch (SipException ex) {
throw new TransactionUnavailableException(ex.getMessage());
}
} // end else
throw new TransactionUnavailableException
("Could not create transaction - could not resolve next hop! ");
|
public ServerTransaction | getNewServerTransaction(Request request)An application has the responsibility of deciding to respond to a
Request that does not match an existing server transaction. The method
is called by an application that decides to respond to an unmatched
Request statefully. This methods return a new unique server transaction
identifier that can be passed to the stateful sendResponse methods in
order to respond to the request.
try {
ServerTransaction transaction = null;
Request sipRequest = (Request) request;
if (sipStack.isDialogCreated(sipRequest.getMethod())) {
if (sipStack.findTransaction((Request)request, true) != null)
throw new TransactionAlreadyExistsException
("server transaction already exists!");
transaction = (ServerTransaction) this.currentTransaction;
if (transaction == null)
throw new TransactionUnavailableException
("Transaction not available");
if (!transaction
.isMessagePartOfTransaction((Request) request)) {
throw new TransactionUnavailableException
("Request Mismatch");
}
transaction.setOriginalRequest(sipRequest);
try {
sipStack.addTransaction(transaction);
} catch (IOException ex) {
throw new TransactionUnavailableException
("Error sending provisional response");
}
equipADialogForTransaction(transaction, sipRequest);
} else {
transaction = (ServerTransaction)
sipStack.findTransaction((Request) request, true);
if (transaction != null)
throw new TransactionAlreadyExistsException
("Transaction exists! ");
transaction = (ServerTransaction) this.currentTransaction;
if (transaction == null)
throw new TransactionUnavailableException
("Transaction not available!");
if (!transaction
.isMessagePartOfTransaction((Request) request))
throw new TransactionUnavailableException
("Request Mismatch");
transaction.setOriginalRequest(sipRequest);
// Map the transaction.
try {
sipStack.addTransaction(transaction);
} catch (IOException ex) {
throw new TransactionUnavailableException
("Could not send back provisional response!");
}
String dialogId = sipRequest.getDialogId(true);
Dialog dialog = sipStack.getDialog(dialogId);
if (dialog != null) {
dialog.addTransaction(transaction);
dialog.addRoute(sipRequest);
}
}
return transaction;
} catch (RuntimeException ex) {
ex.printStackTrace();
throw ex;
}
|
public SipStack | getSipStack()Returns the SipStack that this SipProvider is attached to. A SipProvider
can only be attached to a single SipStack object which belongs to
the same SIP stack as the SipProvider.
return this.sipStack;
|
public void | handleEvent(SipEvent sipEvent, Transaction transaction)Handles the SIP event - because we have only one listener and we are
already in the context of a separate thread, we dont need to enque
the event and signal another thread.
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"handleEvent " + sipEvent +
"currentTransaction = " + transaction +
"this.sipListener = " + this.sipListener);
}
if (this.sipListener == null)
return;
EventWrapper eventWrapper = new EventWrapper();
eventWrapper.sipEvent = sipEvent;
eventWrapper.transaction = transaction;
if (transaction != null &&
transaction instanceof ClientTransaction) {
((ClientTransaction)transaction).setEventPending();
}
this.eventScanner.addEvent(eventWrapper);
|
public void | removeSipListener(SipListener sipListener)Removes the SipListener from this SipProvider. This method returns
silently if the sipListener argument is not registered
with the SipProvider.
if (sipListener == this.sipListener) {
this.sipListener = null;
}
|
public void | sendRequest(Request request)Sends specified Request and returns void i.e.
no transaction record is associated with this action. This method
implies that the application is functioning statelessly specific to this
Request, hence the underlying SipProvider acts statelessly.
Once the Request message has been passed to this method, the SipProvider
will forget about this Request. No transaction semantics will be
associated with the Request and no retranmissions will occur on the
Request by the SipProvider, if these semantics are required it is the
responsibility of the application not the JAIN SIP Stack.
- Stateless Proxy - A stateless proxy simply forwards every request
it receives downstream and discards information about the request
message once the message has been forwarded. A stateless proxy does not
have any notion of a transaction.
// request sends out of dialog
Enumeration it = sipStack.getRouter().getNextHops(request, false);
if (it == null || !it.hasMoreElements()) {
throw new SipException("could not determine next hop!",
SipException.GENERAL_ERROR);
}
// Will slow down the implementation because it involves
// a search to see if a transaction exists.
// Just to double check adding some assertion
// checking under debug.
Transaction tr = sipStack.findTransaction(request, false);
if (tr != null) {
throw new SipException("Cannot send: stateless Transaction found!",
SipException.GENERAL_ERROR);
}
while (it.hasMoreElements()) {
Hop nextHop = (Hop) it.nextElement();
Request sipRequest = request;
String bid = sipRequest.getTransactionId();
ViaHeader via = sipRequest.getTopmostVia();
via.setBranch(bid);
Request newRequest;
// Do not create a transaction for this request. If it has
// Mutliple route headers then take the first one off the
// list and copy into the request URI.
if (sipRequest.getHeader(Header.ROUTE) != null) {
newRequest = (Request) sipRequest.clone();
Enumeration rl =
newRequest.getHeaders(Header.ROUTE);
RouteHeader route = (RouteHeader) rl.nextElement();
newRequest.setRequestURI(route.getAddress().getURI());
sipRequest.removeHeader(Header.ROUTE, true);
} else {
newRequest = sipRequest;
}
MessageChannel messageChannel =
sipStack.createRawMessageChannel(nextHop);
try {
if (messageChannel != null) {
messageChannel.sendMessage((Message)newRequest);
return;
} else {
throw new SipException("Could not forward request.",
SipException.GENERAL_ERROR);
}
} catch (IOException ex) {
continue;
}
}
|
public void | sendResponse(Response sipResponse)Sends specified {@link Response} and returns void i.e.
no transaction record is associated with this action. This method implies
that the application is functioning as either a stateless proxy or a
stateless User Agent Server.
- Stateless proxy - A stateless proxy simply forwards every response
it receives upstream and discards information about the response message
once the message has been forwarded. A stateless proxy does not
have any notion of a transaction.
- Stateless User Agent Server - A stateless UAS does not maintain
transaction state. It replies to requests normally, but discards
any state that would ordinarily be retained by a UAS after a response
has been sent. If a stateless UAS receives a retransmission of a
request, it regenerates the response and resends it, just as if it
were replying to the first instance of the request. A UAS cannot be
stateless unless the request processing for that method would always
result in the same response if the requests are identical. Stateless
UASs do not use a transaction layer; they receive requests directly
from the transport layer and send responses directly to the transport
layer.
ViaHeader via = sipResponse.getTopmostVia();
if (via == null) {
throw new SipException("No via header in response!",
SipException.INVALID_MESSAGE);
}
int port = via.getPort();
String transport = via.getTransport();
// check to see if Via has "received paramaeter". If so
// set the host to the via parameter. Else set it to the
// Via host.
String host = via.getReceived();
if (host == null) {
host = via.getHost();
}
if (port == -1) {
port = 5060; // IMPL_NOTE: move to SIPConstants
}
Hop hop = new Hop(host + ":" + port + "/" + transport);
MessageChannel messageChannel = sipStack.createRawMessageChannel(hop);
messageChannel.sendMessage(sipResponse);
|
public void | setListeningPoint(ListeningPoint listeningPoint)This method sets the listening point of the SipProvider.
A SipProvider can only have a single listening point at any
specific time. This method returns
silently if the same listeningPoint argument is re-set
on the SipProvider.
JAIN SIP supports recieving messages from
any port and interface that a server listens on for UDP, on that same
port and interface for TCP in case a message may need to be sent
using TCP, rather than UDP, if it is too large. In order to satisfy this
functionality an application must create two SipProviders and set
identical listeningPoints except for transport on each SipProvder.
Multiple SipProviders are prohibited to listen on the same
listening point.
if (listeningPoint == null)
throw new NullPointerException("Null listening point");
ListeningPoint lp = (ListeningPoint) listeningPoint;
lp.sipProviderImpl = this;
this.listeningPoint = (ListeningPoint) listeningPoint;
|
protected void | stop()Stops processing messages for this provider. Post an empty
message to our message processing queue that signals us to
quit.
// Put an empty event in the queue and post ourselves a message.
if (Logging.REPORT_LEVEL <= Logging.INFORMATION) {
Logging.report(Logging.INFORMATION, LogChannels.LC_JSR180,
"Exiting provider");
}
synchronized (this) {
listeningPoint.removeSipProvider();
}
this.eventScanner.stop();
|
public void | transactionErrorEvent(SIPTransactionErrorEvent transactionErrorEvent)Invoked when an error has ocurred with a transaction.
Propagate up to the listeners.
Transaction transaction =
(Transaction) transactionErrorEvent.getSource();
if (transactionErrorEvent.getErrorID() ==
SIPTransactionErrorEvent.TRANSPORT_ERROR) {
if (Logging.REPORT_LEVEL <= Logging.ERROR) {
Logging.report(Logging.ERROR, LogChannels.LC_JSR180,
"TransportError occured on " + transaction);
}
// handle Transport error as timeout.
Object errorObject = transactionErrorEvent.getSource();
Timeout timeout = Timeout.TRANSACTION;
TimeoutEvent ev = null;
if (errorObject instanceof ServerTransaction) {
ev = new TimeoutEvent(this, (ServerTransaction)
errorObject);
} else {
ev = new TimeoutEvent(this, (ClientTransaction)
errorObject,
timeout);
}
this.handleEvent(ev, (Transaction) errorObject);
} else {
// This is a timeout event.
Object errorObject = transactionErrorEvent.getSource();
Timeout timeout = Timeout.TRANSACTION;
TimeoutEvent ev = null;
if (errorObject instanceof ServerTransaction) {
ev = new TimeoutEvent(this, (ServerTransaction)
errorObject);
} else {
ev = new TimeoutEvent(this, (ClientTransaction)
errorObject,
timeout);
}
this.handleEvent(ev, (Transaction) errorObject);
}
|