/*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.mobicents.servlet.sip.core.dispatchers;
import gov.nist.javax.sip.message.SIPMessage;
import java.text.ParseException;
import javax.servlet.sip.ar.SipRouteModifier;
import javax.sip.ClientTransaction;
import javax.sip.Dialog;
import javax.sip.InvalidArgumentException;
import javax.sip.ServerTransaction;
import javax.sip.SipException;
import javax.sip.SipProvider;
import javax.sip.Transaction;
import javax.sip.TransactionUnavailableException;
import javax.sip.header.ContentTypeHeader;
import javax.sip.header.MaxForwardsHeader;
import javax.sip.header.ViaHeader;
import javax.sip.message.Request;
import javax.sip.message.Response;
import org.apache.log4j.Logger;
import org.mobicents.servlet.sip.JainSipUtils;
import org.mobicents.servlet.sip.SipFactories;
import org.mobicents.servlet.sip.core.ExtendedListeningPoint;
import org.mobicents.servlet.sip.core.SipNetworkInterfaceManager;
import org.mobicents.servlet.sip.core.SipSessionRoutingType;
import org.mobicents.servlet.sip.core.session.MobicentsSipSession;
import org.mobicents.servlet.sip.message.SipServletRequestImpl;
import org.mobicents.servlet.sip.message.TransactionApplicationData;
/**
* @author <A HREF="mailto:jean.deruelle@gmail.com">Jean Deruelle</A>
*
*/
public abstract class RequestDispatcher extends MessageDispatcher {
private static transient Logger logger = Logger.getLogger(RequestDispatcher.class);
public RequestDispatcher() {}
// public RequestDispatcher(SipApplicationDispatcher sipApplicationDispatcher) {
// super(sipApplicationDispatcher);
// }
/**
* Forward statefully a request whether it is initial or subsequent
* and keep track of the transactions used in application data of each transaction
* @param sipServletRequest the sip servlet request to forward statefully
* @param sipRouteModifier the route modifier returned by the AR when receiving the request
* @throws ParseException
* @throws TransactionUnavailableException
* @throws SipException
* @throws InvalidArgumentException
*/
protected final void forwardRequestStatefully(SipServletRequestImpl sipServletRequest, SipSessionRoutingType sipSessionRoutingType, SipRouteModifier sipRouteModifier) throws Exception {
final SipNetworkInterfaceManager sipNetworkInterfaceManager = sipApplicationDispatcher.getSipNetworkInterfaceManager();
Request clonedRequest = (Request)sipServletRequest.getMessage().clone();
//Add via header
String transport = JainSipUtils.findTransport(clonedRequest);
((SIPMessage)clonedRequest).setApplicationData(null);
ViaHeader viaHeader = JainSipUtils.createViaHeader(
sipNetworkInterfaceManager, clonedRequest, null);
MobicentsSipSession session = sipServletRequest.getSipSession();
if(session != null) {
if(SipSessionRoutingType.CURRENT_SESSION.equals(sipSessionRoutingType)) {
//an app was found or an app was returned by the AR but not found
String handlerName = session.getHandler();
if(handlerName != null) {
viaHeader.setParameter(APP_ID,
sipServletRequest.getSipSession().getSipApplicationSession().getKey().getId());
viaHeader.setParameter(RR_PARAM_APPLICATION_NAME,
sipApplicationDispatcher.getHashFromApplicationName(sipServletRequest.getSipSession().getKey().getApplicationName()));
} else {
// if the handler name is null it means that the app returned by the AR was not deployed
// and couldn't be called,
// we specify it so that on response handling this app can be skipped
viaHeader.setParameter(APP_ID,
sipServletRequest.getSipSession().getSipApplicationSession().getKey().getId());
viaHeader.setParameter(APP_NOT_DEPLOYED,
sipServletRequest.getSipSession().getKey().getApplicationName());
}
clonedRequest.addHeader(viaHeader);
} else {
if(SipRouteModifier.NO_ROUTE.equals(sipRouteModifier)) {
// no app was returned by the AR
viaHeader.setParameter(NO_APP_RETURNED,
"noappreturned");
} else {
viaHeader.setParameter(MODIFIER,
sipRouteModifier.toString());
}
clonedRequest.addHeader(viaHeader);
}
} else {
if(SipRouteModifier.NO_ROUTE.equals(sipRouteModifier)) {
// no app was returned by the AR and no application was selected before
// if the handler name is null it means that the app returned by the AR was not deployed
// and couldn't be called,
// we specify it so that on response handling this app can be skipped
viaHeader.setParameter(NO_APP_RETURNED,
"noappreturned");
} else {
viaHeader.setParameter(MODIFIER,
sipRouteModifier.toString());
}
clonedRequest.addHeader(viaHeader);
}
ExtendedListeningPoint extendedListeningPoint =
sipNetworkInterfaceManager.findMatchingListeningPoint(transport, false);
if(logger.isDebugEnabled()) {
logger.debug("Matching listening point found "
+ extendedListeningPoint);
}
SipProvider sipProvider = extendedListeningPoint.getSipProvider();
ServerTransaction serverTransaction = (ServerTransaction) sipServletRequest.getTransaction();
Dialog dialog = sipServletRequest.getDialog();
//decrease the Max Forward Header
MaxForwardsHeader maxForwardsHeader = (MaxForwardsHeader) clonedRequest
.getHeader(MaxForwardsHeader.NAME);
if (maxForwardsHeader == null) {
maxForwardsHeader = SipFactories.headerFactory.createMaxForwardsHeader(JainSipUtils.MAX_FORWARD_HEADER_VALUE);
clonedRequest.addHeader(maxForwardsHeader);
} else {
if(maxForwardsHeader.getMaxForwards() - 1 > 0) {
maxForwardsHeader.setMaxForwards(maxForwardsHeader.getMaxForwards() - 1);
} else {
//Max forward header equals to 0, thus sending too many hops response
sendErrorResponse(Response.TOO_MANY_HOPS, serverTransaction, (Request) sipServletRequest.getMessage(), sipProvider);
return;
}
}
if(logger.isDebugEnabled()) {
if(SipRouteModifier.NO_ROUTE.equals(sipRouteModifier)) {
logger.debug("Routing Back to the container the following request "
+ clonedRequest);
} else {
logger.debug("Routing externally the following request "
+ clonedRequest);
}
}
if(logger.isDebugEnabled()) {
logger.debug("Dialog existing " + (dialog != null));
}
if(dialog == null) {
Transaction transaction = ((TransactionApplicationData)
serverTransaction.getApplicationData()).getSipServletMessage().getTransaction();
if(transaction == null || transaction instanceof ServerTransaction) {
ClientTransaction ctx = sipProvider.getNewClientTransaction(clonedRequest);
//keeping the server transaction in the client transaction's application data
TransactionApplicationData appData = new TransactionApplicationData(sipServletRequest);
appData.setTransaction(serverTransaction);
ctx.setApplicationData(appData);
//keeping the client transaction in the server transaction's application data
((TransactionApplicationData)serverTransaction.getApplicationData()).setTransaction(ctx);
if(logger.isInfoEnabled()) {
logger.info("Sending the request through a new client transaction " + clonedRequest);
}
ctx.sendRequest();
} else {
if(logger.isInfoEnabled()) {
logger.info("Sending the request through the existing transaction " + clonedRequest);
}
((ClientTransaction)transaction).sendRequest();
}
} else if ( clonedRequest.getMethod().equals("ACK") ) {
if(logger.isInfoEnabled()) {
logger.info("Sending the ACK through the dialog " + clonedRequest);
}
dialog.sendAck(clonedRequest);
} else {
Request dialogRequest=
dialog.createRequest(clonedRequest.getMethod());
Object content=clonedRequest.getContent();
if (content!=null) {
ContentTypeHeader contentTypeHeader= (ContentTypeHeader)
clonedRequest.getHeader(ContentTypeHeader.NAME);
if (contentTypeHeader!=null) {
dialogRequest.setContent(content,contentTypeHeader);
}
}
// Copy all the headers from the original request to the
// dialog created request:
// => not needed already done by the clone method
// ListIterator<String> headerNamesListIterator=clonedRequest.getHeaderNames();
// while (headerNamesListIterator.hasNext()) {
// String name=headerNamesListIterator.next();
// Header header=dialogRequest.getHeader(name);
// if (header==null ) {
// ListIterator<Header> li=clonedRequest.getHeaders(name);
// if (li!=null) {
// while (li.hasNext() ) {
// Header h = li.next();
// dialogRequest.addHeader(h);
// }
// }
// }
// else {
// if ( header instanceof ViaHeader) {
// ListIterator<Header> li= clonedRequest.getHeaders(name);
// if (li!=null) {
// dialogRequest.removeHeader(name);
// Vector v=new Vector();
// while (li.hasNext() ) {
// Header h=li.next();
// v.addElement(h);
// }
// for (int k=(v.size()-1);k>=0;k--) {
// Header h=(Header)v.elementAt(k);
// dialogRequest.addHeader(h);
// }
// }
// }
// }
// }
ClientTransaction clientTransaction =
sipProvider.getNewClientTransaction(dialogRequest);
//keeping the server transaction in the client transaction's application data
TransactionApplicationData appData = new TransactionApplicationData(sipServletRequest);
appData.setTransaction(serverTransaction);
clientTransaction.setApplicationData(appData);
//keeping the client transaction in the server transaction's application data
((TransactionApplicationData)serverTransaction.getApplicationData()).setTransaction(clientTransaction);
dialog.setApplicationData(appData);
if(logger.isInfoEnabled()) {
logger.info("Sending the request through the dialog " + clonedRequest);
}
dialog.sendRequest(clientTransaction);
}
}
}