/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright 1997-2009 Sun Microsystems, Inc. All rights reserved.
* Copyright (c) Ericsson AB, 2004-2008. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License"). You
* may not use this file except in compliance with the License. You can obtain
* a copy of the License at https://glassfish.dev.java.net/public/CDDL+GPL.html
* or glassfish/bootstrap/legal/LICENSE.txt. See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at glassfish/bootstrap/legal/LICENSE.txt.
* Sun designates this particular file as subject to the "Classpath" exception
* as provided by Sun in the GPL Version 2 section of the License file that
* accompanied this code. If applicable, add the following below the License
* Header, with the fields enclosed by brackets [] replaced by your own
* identifying information: "Portions Copyrighted [year]
* [name of copyright owner]"
*
* Contributor(s):
*
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license." If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above. However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/
package com.ericsson.ssa.sip;
import com.ericsson.ssa.container.SipBindingResolver;
import com.ericsson.ssa.container.SipContainerThreadPool;
import com.ericsson.ssa.container.reporter.Reporter;
import com.ericsson.ssa.container.sim.ApplicationDispatcher;
import com.ericsson.ssa.sip.ProxyImpl.ProxyFacade;
import com.ericsson.ssa.sip.persistence.ReplicationUnitOfWork;
import com.ericsson.ssa.sip.timer.GeneralTimer;
import com.ericsson.ssa.sip.timer.GeneralTimerListener;
import com.ericsson.ssa.sip.timer.TimerServiceImpl;
import com.ericsson.ssa.sip.transaction.ClientTransaction;
import com.ericsson.ssa.sip.transaction.ClientTransactionRegistrationListener;
import com.ericsson.ssa.sip.transaction.InviteClientTransaction;
import org.glassfish.comms.api.proxy.ProxyBranchEvent;
import org.glassfish.comms.api.proxy.ProxyBranchListener;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.sip.ProxyBranch;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipServletResponse;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.TooManyHopsException;
import javax.servlet.sip.URI;
import javax.servlet.sip.Address;
import javax.servlet.sip.ar.SipApplicationRoutingDirective;
/**
* Part of the Proxy implementation.
*
* @author ehsroha
*/
public class ProxyBranchImpl extends TargetSet implements ProxyBranch,
GeneralTimerListener, ClientTransactionRegistrationListener {
protected static final Logger _log = LogUtil.SIP_LOGGER.getLogger();
static int TimerC = 180; // >3min
private SipServletResponseImpl _bestResponse = null;
private SipServletResponse _lastResponse = null;
private final SipServletRequestImpl _request;
private GeneralTimer _timer = null;
private int _proxyBranchTimeout;
private List<ProxyBranchImpl> _proxyBranches = null;
private boolean _isCanceled = false;
private boolean _pendingCancel = false;
private Header _pendingCancelReason;
private SipServletResponseImpl _provisional = null;
private boolean isStarted = false;
private OutboundInterface oi = null;
private java.util.List<Address> routesForCancel = new ArrayList<Address>();
/**
* Whether there is a specific setting to recurse on ProxyBranch level.
* This value is null unless explicitely set. A null value means that
* it should be taken from proxy level.
*/
private Boolean _recurse = null;
/**
* Whether there is a specific setting to recordRoute on ProxyBranch level.
* This value is null unless explicitely set. A null value means that
* it should be taken from proxy level.
*/
private Boolean _recordRoute = null;
/**
* Whether there is a specific setting to addToPath on ProxyBranch level.
* This value is null unless explicitely set. A null value means that
* it should be taken from proxy level.
*/
private Boolean _addToPath = null;
/**
* Client transaction for this request
* Is set when the client transaction is
* created. May remain null if there is no
* client transaction associated with this
* branch. This is the case when there are
* more path nodes in the application path.
*/
private ClientTransaction clientTransaction;
private ProxyBranchInternalRequest internalReq;
/**
* The constructor of the Proxy Branch implementation.
*
* @param request
* the request which this branch should proxy to
*/
public ProxyBranchImpl(ProxyImpl p, SipServletRequestImpl request) {
super(p);
_request = request;
_proxyBranchTimeout = p.getProxyTimeout();
}
protected List<TargetSet> getTargetSets() {
if (getRecursedProxyBranchesImpl() == null) {
return super.getTargetSets();
}
return new ArrayList<TargetSet>(getRecursedProxyBranchesImpl());
}
public synchronized SipServletResponseImpl findBestResponse() {
if (_bestResponse != null) {
// Already 3xx redirection?
if (_bestResponse.isAlreadyRedirected() &&
(getRecursedProxyBranchesImpl() != null)) {
return super.findBestResponse();
}
}
return _bestResponse;
}
/**
* Returns the best response.
*
* @return the best response.
*/
public synchronized SipServletResponseImpl getBestResponse() {
return _bestResponse;
}
/**
* Rule 1: Set best response if no response priviously existed for this
* branch.
* <p>
* Rule 2: 2xx response will override any response
* <p>
* Rule 3: 6xx response will override any response except 2xx
* <p>
* Rule 4: Except rule 1 and 2 a response 3xx-5xx will override a previous
* set response, which had a higher status code.
*
* @param resp
*/
private synchronized void setBestResponse(SipServletResponseImpl resp) {
// 700 is one higher than possible
int bestStatus = (_bestResponse == null) ? 700 : _bestResponse.getStatus();
if ((bestStatus / 100) != 2) {
int status = resp.getStatus() / 100;
if (status == 2) {
_bestResponse = resp;
} else if (status == 6) {
_bestResponse = resp;
} else if (((status > 2) && (status < 6) &&
((bestStatus / 100) != 6)) &&
(resp.getStatus() < bestStatus)) {
_bestResponse = resp;
}
}
}
/**
* Whether this ProxyBranchImpl is matching the URI or not.
*
* @param uri
* the incoming uri
* @return this instance if it is matching the URI otherwise null
*/
public ProxyBranchImpl findBranch(URI uri) {
if (getRecurse()) {
ProxyBranchImpl branch = super.findBranch(uri);
if (branch != null) {
return branch;
}
}
return uri.equals(_request.getRequestURI()) ? this : null;
}
public void addTopLevelBranch(List<ProxyBranchImpl> branches) {
branches.add(this);
}
private void startTimer(SipServletResponseImpl resp) {
if (_timer == null) {
synchronized (this) {
if (_timer == null) {
int time = getProxyBranchTimeout() * 1000;
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE,
"start timer " + (time / 1000) +
" sec for branch = " + _request.getRequestURI());
}
// needed for CANCEL to reach next hop
if (resp != null) {
setProvisionalResponse(resp);
}
// start timer
_timer = TimerServiceImpl.getInstance()
.createTimer(this, time, null);
}
}
}
}
private synchronized void stopTimer() {
if (_timer != null) {
_timer.cancel();
_timer = null;
}
}
/**
* Response management of the proxy branch
* @param resp
* @param pc
* @return true if it is a virtual proxy branch otherwise false
*/
public boolean doInitialResponse(SipServletResponseImpl resp,
ProxyContext pc) {
setLastResponse(resp);
int status = resp.getStatus() / 100;
if (status == 1) {
if (resp.getMethod().equals("INVITE") && isPendingCancel()) {
final SipServletRequestImpl cancel;
synchronized (this) {
// needed for cancel to reach next hop-node
setProvisionalResponse(resp);
if (_log.isLoggable(Level.INFO)) {
_log.log(Level.INFO,
"CANCEL is pending, lets cancel this branch " +
_request.getRequestURI() + " for response " +
resp.getStatus());
}
cancel = cancelIntern(_pendingCancelReason);
if (cancel != null) {
setCanceled();
}
}
if (cancel != null) {
Reporter reporter = ApplicationDispatcher.getInstance().getServletReporters();
if (reporter != null) {
String interceptedAt = cancel.getApplicationSessionImpl().getApplicationName() + "/" + cancel.getSessionImpl().getHandler();
reporter.logOutgoingRequest(Reporter.InterceptionType.SERVLET, cancel, interceptedAt);
}
// lets CANCEL this branch outside synchronized block...
SipContainerThreadPool.getInstance().execute(new Callable() {
// execute in another thread...
public Object call() throws Exception {
DialogFragment dialog = cancel.getDialog();
if (dialog != null) {
dialog.getDialogLifeCycle().setThreadLocalUnitOfWork();
try {
cancel.popDispatcher().dispatch(cancel);
} finally {
ReplicationUnitOfWork.clearThreadLocal();
}
} else {
if (_log.isLoggable(Level.INFO)) {
_log.log(Level.INFO, "can not send message {0} since the dialog is null", new Object[]{cancel});
}
}
return null;
}
});
}
} else {
// lets start proxy branch timer
startTimer(resp);
}
getProxyImpl().invokeServletAndForward(resp, pc);
} else {
// we have a final response
setBestResponse(resp);
// need to stop timer
stopTimer();
if ((status == 3) && getProxy().getRecurse()) {
// If recurse is set and this is a 3xx response,
// fork new requests to list of Contact
AddressImpl address = null;
URI uri = null;
List<URI> contacts = new ArrayList<URI>(5);
for (ListIterator<String> contactsIt = resp.getHeaders(
Header.CONTACT); contactsIt.hasNext();) {
String contact = (String) contactsIt.next();
try {
address = new AddressImpl(contact);
uri = address.getURI();
contacts.add(uri);
} catch (ServletParseException e) {
if (_log.isLoggable(Level.WARNING)) {
_log.log(Level.WARNING,
"Could not recurse on Contact = " + contact);
}
}
}
if (contacts.size() > 0) {
List<ProxyBranchImpl> branches = getProxyImpl()
.recurseTo(resp.getRequestImpl(),
contacts);
if (!branches.isEmpty()) {
addRecursedProxyBranches(branches);
}
}
// inform that this 3xx redirect has forked to all its contacts.
resp.setAlreadyRedirected();
} else {
// continue to next branch for 3xx-6xx response
if (status != 2) {
// invoke next branch of parent target set
if (getParent().hasNext()) {
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE, "start next branch");
}
getParent().next();
}
}
}
}
return false;
}
public ProxyImpl.ProxyFacade getProxy() {
try {
return (ProxyFacade) getProxyImpl().getOriginalRequestImpl()
.getProxy();
} catch (TooManyHopsException e) {
return null;
}
}
public int getProxyBranchTimeout() {
return _proxyBranchTimeout;
}
private synchronized List<ProxyBranchImpl> getRecursedProxyBranchesImpl() {
return _proxyBranches;
}
private synchronized void addRecursedProxyBranches(
List<ProxyBranchImpl> branches) {
if (_proxyBranches == null) {
_proxyBranches = branches;
} else {
_proxyBranches.addAll(branches);
}
}
public List<ProxyBranch> getRecursedProxyBranches() {
List<ProxyBranchImpl> branches = getRecursedProxyBranchesImpl();
return (branches == null) ? null : new ArrayList<ProxyBranch>(branches);
}
private synchronized void setLastResponse(SipServletResponse resp) {
_lastResponse = resp;
}
public synchronized SipServletResponse getResponse() {
return _lastResponse;
}
public SipServletRequest getRequest() {
if (internalReq == null ) {
internalReq = new ProxyBranchInternalRequest(_request);
}
return internalReq;
}
public SipServletRequestImpl getRequestImpl() {
return _request;
}
@Override
public boolean getParallel() {
return getParent().getParallel();
}
@Override
public boolean isStarted() {
return isStarted;
}
@Override
public boolean getRecurse() {
if (_recurse != null) {
// Setting override that of proxy level.
return _recurse;
}
return getProxyImpl().getRecurse();
}
public void setRecurse(boolean recurse) {
_recurse = Boolean.valueOf(recurse);
}
public boolean getRecordRoute() {
if (_recordRoute != null) {
// Setting override that of proxy level.
return _recordRoute;
}
return getProxyImpl().getRecordRoute();
}
public void setRecordRoute(boolean rr) {
if (isStarted()) {
throw new IllegalStateException("Proxy branch is already started");
}
_recordRoute = Boolean.valueOf(rr);
}
public boolean getAddToPath() {
if (_addToPath != null) {
// Setting override that of proxy level.
return _addToPath;
}
return getProxyImpl().getAddToPath();
}
public void setAddToPath(boolean addToPath) {
// ProxyImpl.validateSetAddToPath(_request);
if (_request.getMethod().equals("REGISTER")) {
_addToPath = Boolean.valueOf(addToPath);
} // else ignore as spec and TCK don't like exceptions
}
public SipURI getRecordRouteURI() {
if (!getRecordRoute()) {
throw new IllegalStateException("record-routing is not enabled");
}
return new ProxyImpl.RecordRouteURI(_request);
}
public SipURI getPathURI() {
return new ProxyImpl.PathURI(_request);
}
/**
* Not doing anything inside the method.
*/
public void setOutboundInterface(InetSocketAddress address) {
getProxyImpl().validateOutboundInterface(address.getAddress());
validateSession();
this.oi = new OutboundInterface(address);
}
/**
* Not doing anything inside the method.
*/
public void setOutboundInterface(InetAddress address) {
getProxyImpl().validateOutboundInterface(address);
validateSession();
this.oi = new OutboundInterface(address);
}
public OutboundInterface getOutboundInterface() {
return this.oi;
}
public void setOutboundInterface(OutboundInterface oi) {
this.oi = oi;
}
private void validateSession() {
if (!getRequestImpl().getSession().isValid()) {
throw new IllegalArgumentException("Session is invalid");
}
}
public void setProxyBranchTimeout(int seconds) {
validateProxyBranchTimeoutArgument(seconds);
this._request.setTimerC(seconds);
_proxyBranchTimeout = seconds;
}
private void validateProxyBranchTimeoutArgument(int seconds) {
if (seconds <= 0) {
throw new IllegalArgumentException(
"Invalid BranchTimeout must be > 0");
}
if (getParallel() && (seconds > getProxyImpl().getProxyTimeout())) {
throw new IllegalArgumentException(
"BranchTimeout can not be greater than the overall proxy timeout value");
}
}
public void timeout(GeneralTimer timer) {
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE,
"timer fired for branch = " + _request.getRequestURI());
}
if (getBestResponse() == null) {
// Give ProxyBranchListners a chance to extend timer
int newTimer = callBranchExpired();
if (newTimer == -1) {
// Not extended
branchExpired();
} else {
// Timer extended
// Extent this timer
setProxyBranchTimeout(newTimer);
stopTimer();
startTimer(null);
// Extend client transaction timer
if ((clientTransaction != null) &&
clientTransaction instanceof InviteClientTransaction) {
((InviteClientTransaction) clientTransaction).prolongTimerC(newTimer);
}
}
}
}
private void branchExpired() {
// cancel this branch if cancel is allowed (RFC3841)
if (!getProxyImpl().getNoCancel() || getProxyImpl().has6xx()) {
cancel();
}
// invoke next branch of parent target set
if (getParent().hasNext()) {
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE, "start next branch");
}
getParent().next();
}
}
/**
* Call all the ProxyBranchListeners with branchExpired.
*
* @return new (extended) proxyBranchTimeout if called by a proxybranchListener, -1 otherwise.
*/
private int callBranchExpired() {
List<ProxyBranchListener> proxyBranchListeners = getProxyImpl()
.getSipSession()
.getApplicationSessionImpl()
.getSipApplicationListeners()
.getProxyBranchListeners();
// Have result of invocation as final int[],
// since it need to be final and mutable at the same time.
final int[] anInt = new int[] { -1 };
if (proxyBranchListeners.size() > 0) {
// Create a dynamic proxy object that acts as a proxy for this ProcyBranch.
// calls to setProxyBranchTimeout are intercepted.
ProxyBranch proxyBranchDynamicProxy = (ProxyBranch) Proxy.newProxyInstance(getClass()
.getClassLoader(),
new Class[] { ProxyBranch.class },
new InvocationHandler() {
public Object invoke(Object proxy, Method method,
Object[] args) throws Throwable {
// Intercept setProxyBranchTimeout calls
if (method.getName().equals("setProxyBranchTimeout")) {
validateProxyBranchTimeoutArgument((Integer) args[0]);
// Remember (last) call to setProxyBranchTimeout
anInt[0] = (Integer) args[0];
return null;
} else {
// Default, forward call to corresponding method on this Proxy
return method.invoke(ProxyBranchImpl.this, args);
}
}
});
// Fire event with dynamic proxy in event
ProxyBranchEvent event = new ProxyBranchEvent(proxyBranchDynamicProxy);
for (ProxyBranchListener proxyBranchListener : proxyBranchListeners) {
proxyBranchListener.branchExpired(event);
}
}
// return last call to proxyBranchTimeout. -1 in no such.
return anInt[0];
}
public void proxyTo() throws IllegalStateException {
prepareProxyTo();
// the request need to be cloned
final SipServletRequestImpl forward = (SipServletRequestImpl) getRequestImpl()
.clone();
if (getRecordRoute()) {
// state Record Route in request to inform
// that a RecordRoute header must be added
forward.indicateRecordRoute();
}
if (getAddToPath()) {
// state Path in request to inform
// that a Path header shall be added
forward.indicatePath();
}
if (getProxyImpl().getNoCancel()) {
forward.setNoCancel();
}
// JSR289: when a request is proxied, its routing directive should
// be implicitly set to CONTINUE. See also issue 773.
forward.setImplicitRoutingDirective(SipApplicationRoutingDirective.CONTINUE,
null);
// no need to copy region, expected to be set already
forward.setTransactionRequest(getRequestImpl());
Reporter reporter = ApplicationDispatcher.getInstance().getServletReporters();
if (reporter != null) {
String interceptedAt = forward.getApplicationSessionImpl().getApplicationName() + "/" + forward.getSessionImpl().getHandler();
reporter.logOutgoingRequest(Reporter.InterceptionType.SERVLET, forward, interceptedAt);
}
if (getOutboundInterface() == null) {
setOutboundInterface(getProxyImpl().getOutboundInterface());
}
OutboundInterface.resolve(forward, this);
//Copy the routes...
forward.setPushedRouteList(routesForCancel);
SipContainerThreadPool.getInstance().execute(new Callable() {
public Object call() throws Exception {
if (_log.isLoggable(Level.FINER)) {
_log.log(Level.FINER,
"Proxy request " + forward.toDebugString() + ", " +
toString());
}
// Activate the dialog lifecycle u-o-w
forward.getDialog().getDialogLifeCycle()
.setThreadLocalUnitOfWork();
try {
// push the request to the dispatcher
forward.setClientTransactionRegistrationListener(ProxyBranchImpl.this);
forward.popDispatcher().dispatch(forward);
return null;
} finally {
ReplicationUnitOfWork.clearThreadLocal();
}
}
});
isStarted = true;
if (_log.isLoggable(Level.FINEST)) {
_log.log(Level.FINEST, "Lets leave...");
}
}
protected void prepareProxyTo() throws IllegalStateException {
if (getParent() == null) {
throw new IllegalStateException(
"Need to add parent target set before calling.");
}
// analyze how to add proxy to path...
if (getProxyImpl().isFirstProxyBranchSetAndTest()) {
// lets add the proxy context to the application path
getRequestImpl().getDialog()
.addToPath(getRequestImpl().getProxyContext());
} else {
// need to clone the dialog if this is not the first branch
DialogFragment clone = (DialogFragment) getRequestImpl().getDialog()
.cloneFromCallerToCalleeUntil(getRequestImpl()
.getProxyContext(),
false);
// update dialog of request...
getRequestImpl().setDialog(clone);
// need to add proper fragment id...
getRequestImpl().setFragmentId(clone.getFragmentId());
if (getRequestImpl().isContactIndicated()) {
// since it's a new fragment id the contact must be replaced...
Header contact = getRequestImpl().getRawHeader(Header.CONTACT);
contact.setReadOnly(false);
contact.removeValues();
DialogManager.getInstance().addContact(getRequestImpl());
}
// need to update transaction path since there are new nodes.
// lets start by cleaning the top of the stack...
PathNode p = null;
int size = 0;
// need to take it in reverse order since its a stack...
Iterator<PathNode> i = clone.getCallee2CallerPath();
if (i.hasNext()) {
// just skip the first since its this proxy context...
i.next();
}
while (i.hasNext()) {
p = i.next();
if (getRequestImpl().getSupervised()) {
// this is the same as poping the stack, but poping the
// stack on a request will pop application stack
size = getRequestImpl()._transactionStack.size();
getRequestImpl()._transactionStack.remove(size - 1);
}
}
// ...and replace it with the new nodes.
i = clone.getCallee2CallerPath();
if (i.hasNext()) {
// update the request with the new clone...
ProxyContext pc = (ProxyContext) i.next();
getRequestImpl().setProxyContext(pc);
}
while (i.hasNext()) {
// add the rest of the nodes..
p = i.next();
if (getRequestImpl().getSupervised()) {
getRequestImpl().pushTransactionDispatcher(p);
}
}
}
// Add the Proxy to transaction path of the request
// so that a 487 to a Cancel is propagated
// via the proxy as one of the candidates for
// best response.
_request.pushTransactionDispatcher(_request.getProxyContext());
if (_log.isLoggable(Level.FINEST)) {
_log.log(Level.FINEST,
getRequestImpl().toDebugString() + ", " + toString());
}
}
private void setProvisionalResponse(SipServletResponseImpl resp) {
_provisional = resp;
}
private SipServletResponseImpl getProvisionalResponse() {
return _provisional;
}
private SipServletRequestImpl createCancel() {
SipServletRequestImpl cancel = _request.createCancelImpl();
if (routesForCancel != null && !routesForCancel.isEmpty()) {
MultiLineHeader routeHdr = new MultiLineHeader(Header.ROUTE, true);
for (Address route : routesForCancel) {
routeHdr.setAddressValue(route, false);
}
cancel.setHeader(routeHdr);
}
SipServletResponseImpl provisonal = getProvisionalResponse();
// add saved top via from provisional response...
if (provisonal.getCancelVia() != null) {
cancel.setHeader(provisonal.getCancelVia());
}
// find next pathNode if any...
PathNode next = provisonal.getPreviousVisited();
if (next != null) {
cancel.pushApplicationDispatcher(next);
cancel.pushTransactionDispatcher(next);
} else if (_request.getProxyContext() != null) {
cancel.pushTransactionDispatcher(_request.getProxyContext());
}
// no need to keep reference to provisional anymore, save memory
setProvisionalResponse(null);
return cancel;
}
public boolean isPendingCancel() {
return _pendingCancel;
}
public void setPendingCancel(Header reason) {
_pendingCancel = true;
_pendingCancelReason = reason;
}
private synchronized boolean isCanceled() {
return _isCanceled;
}
private void setCanceled() {
_isCanceled = true;
}
public void cancel(java.lang.String[] protocol, int[] reasonCode,
java.lang.String[] reasonText) {
Header reasonHeader = ProxyImpl.makeReasonHeader(protocol, reasonCode,
reasonText);
cancel(reasonHeader);
}
public void cancel() {
cancel(null);
}
public void cancel(Header reasonHeader) {
SipServletRequestImpl cancel = null;
synchronized (this) {
cancel = cancelIntern(reasonHeader);
}
if (cancel != null) {
// lets CANCEL this branch...
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE,
cancel + " will be sent for branch = " +
_request.getRequestURI());
}
cancel.popDispatcher().dispatch(cancel);
synchronized (this) {
setCanceled();
}
}
}
private SipServletRequestImpl cancelIntern(Header reason) {
// don't need to CANCEL if branch has final
// response already or it has been canceled
// before or waiting for provisonal response
SipServletRequestImpl cancel = null;
if (!"INVITE".equals(_request.getMethod())) {
// FIXME Consider stopping or extending timer F in NoninviteClientTransaction
return null;
}
if (!isCanceled()) {
// already final response?
SipServletResponseImpl bestResp = getBestResponse();
if (bestResp == null) {
// no final response exist for the branch
if (getProvisionalResponse() == null) {
// have not got provisonal response
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE,
"pending CANCEL for branch = " +
_request.getRequestURI());
}
// set pending cancel...
setPendingCancel(reason);
} else {
// time to create CANCEL request
cancel = createCancel();
if (reason != null) {
cancel.addHeader(reason);
}
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE, cancel.toDebugString());
}
}
} else {
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE,
"Will not CANCEL, already got final response " +
bestResp.getStatus() + " for current branch = " +
_request.getRequestURI());
}
}
} else {
if (_log.isLoggable(Level.FINE)) {
_log.log(Level.FINE,
"CANCEL already for branch = " + _request.getRequestURI());
}
}
return cancel;
}
public void registerClientTransaction(ClientTransaction t) {
this.clientTransaction = t;
}
}