/*
* 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 org.jvnet.glassfish.comms.clb.core.sip;
import javax.servlet.sip.Address;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.URI;
import javax.servlet.sip.SipURI;
import com.ericsson.ssa.sip.Header;
import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipServletResponseImpl;
import com.ericsson.ssa.sip.SipURIImpl;
import com.ericsson.ssa.sip.ViaImpl;
import com.ericsson.ssa.sip.dns.SipTransports;
import com.ericsson.ssa.sip.dns.TargetTuple;
import com.ericsson.ssa.sip.dns.TargetResolver;
import com.sun.enterprise.Switch;
import java.util.ListIterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.sip.SipSession;
import org.jvnet.glassfish.comms.clb.core.CLBConstants;
import org.jvnet.glassfish.comms.clb.core.util.LoadbalancerUtil;
import org.jvnet.glassfish.comms.util.LogUtil;
/**
*
* @author kshitiz
*/
public class SipClientToClbFEMapper {
private static final Logger logger = LogUtil.CLB_LOGGER.getLogger();
private ConcurrentHashMap<String, Address> feAddrMapForRegisterReq;
private ConcurrentHashMap<String, TimerTask>
feAddrCleanupTasksMap;
private boolean reuseConnections;
private static final SipClientToClbFEMapper instance =
new SipClientToClbFEMapper();
private static final Timer timer = Switch.getSwitch().getTimer();
private SipClientToClbFEMapper() {
//Only set at time of instance creation
//This value is not dynamically reconfigurable
reuseConnections = LoadbalancerUtil.isReuseClientFEConnection();
if (reuseConnections) {
feAddrMapForRegisterReq = new ConcurrentHashMap<String, Address>();
feAddrCleanupTasksMap = new ConcurrentHashMap<String, TimerTask>();
}
}
public static SipClientToClbFEMapper getInstance() {
return instance;
}
/**
* Create mapping of FE address for client. This will only be done for
* REGISTER request OR dialog creating request
*
* @param response response being send to client
*
* @throws org.jvnet.glassfish.comms.clb.core.sip.SipRoutingException
*/
public void createFEAddressMapping(SipServletResponseImpl response)
throws SipRoutingException {
if (!reuseConnections) {
return;
}
SipServletRequestImpl request = response.getRequestImpl();
if (request == null) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("There is no request associated with response");
}
return;
}
if (!request.getRemote().getProtocol().equals(SipTransports.TCP_PROT)) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Protocol used in not TCP");
}
return;
}
String method = request.getMethod();
SipSession session = null;
boolean isRegisterMethod = false;
if (method.equals("REGISTER")) {
isRegisterMethod = true;
} else {
//only consider dialog creating requests
if (SipFactoryImpl.isDialogCreational(method)) {
//Create mapping only if sip session exists
session = request.getSession(false);
if (session == null || !session.isValid()) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Session is null or invalid");
}
return;
}
}
}
Address contactAddress = null;
try {
contactAddress = request.getAddressHeader(Header.CONTACT);
} catch (ServletParseException ex) {
throw new SipRoutingException("Unable to get Contact header", ex);
}
if (isRegisterMethod && contactAddress.getExpires() == 0) {
return;
}
String contact = null;
if (contactAddress.getURI().isSipURI()) {
contact = createContact((SipURI) contactAddress.getURI());
} else {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Contact address is not sip uri");
}
return;
}
Header viaHeader = request.getRawHeader(Header.VIA);
ListIterator<String> viaHeaderIter = viaHeader.getValues();
ViaImpl firstViaHeader = new ViaImpl(viaHeaderIter.next());
String felbParam =
firstViaHeader.getParameter(CLBConstants.FE_LB_PARAM);
//Request is not proxied, there will be no Path header added by FE
if (felbParam == null) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("felb param is null");
}
return;
}
Address feAddress = createSipAddress(
firstViaHeader.getHost(), firstViaHeader.getPort());
int statusCode = response.getStatus();
if ( statusCode >= 200 && statusCode < 300) {
if (isRegisterMethod) {
String expires = request.getHeader(Header.EXPIRES);
if(expires == null) {
return;
}
storeFEAddrForRegisterReq(contact, feAddress,
Integer.parseInt(expires));
} else {
session.setAttribute(CLBConstants.CLB_FE_ADDRESS, feAddress);
}
}
}
public void removeFEAddressMapping(SipServletRequestImpl request) throws SipRoutingException {
if (!reuseConnections) {
return;
}
String method = request.getMethod();
if (!method.equals("REGISTER")) {
return;
}
Address contactAddress = null;
try {
contactAddress = request.getAddressHeader(Header.CONTACT);
} catch (ServletParseException ex) {
throw new SipRoutingException("Unable to get Contact header", ex);
}
if(contactAddress.getExpires() > 0){
return;
}
String contact = null;
if (contactAddress.getURI().isSipURI()) {
contact = createContact((SipURI) contactAddress.getURI());
} else {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Contact address is not sip uri");
}
return;
}
removeFEAddrForRegisterReq(contact);
}
private void removeFEAddrForRegisterReq(String contact) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Removing mapping from feAddrMapForRegisterReq for contact : " + contact);
}
feAddrMapForRegisterReq.remove(contact);
if (logger.isLoggable(Level.FINER)) {
logger.finer("feAddrMapForRegisterReq : " + feAddrMapForRegisterReq);
}
TimerTask task = feAddrCleanupTasksMap.remove(contact);
if(task != null && task.cancel()){
timer.purge();
}
}
private void storeFEAddrForRegisterReq(String contact, Address pathValue,
int expires) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Adding path mapping to feAddrMapForRegisterReq for contact : " + contact + " with value : " + pathValue);
}
if (contact == null || pathValue == null) {
return;
}
TimerTask task = feAddrCleanupTasksMap.remove(contact);
if(task != null && task.cancel()){
timer.purge();
}
feAddrMapForRegisterReq.put(contact, pathValue);
if (logger.isLoggable(Level.FINER)) {
logger.finer("feAddrMapForRegisterReq : " + feAddrMapForRegisterReq);
}
if (logger.isLoggable(Level.FINER)) {
logger.finer("Adding timer task with expires of " + expires + "seconds");
}
task = new FEAddressMapCleanupTask(contact);
timer.schedule(task, expires * 1000);
feAddrCleanupTasksMap.put(contact, task);
}
/**
* Push route containing FE address if a mapping exists for request uri
*
* @param request Sip request
* @param bindHostName Bind host name
* @param bindPort Bind port
* @throws org.jvnet.glassfish.comms.clb.core.sip.SipRoutingException
*/
public void pushRouteHeader(SipServletRequestImpl request,
String bindHostName, int bindPort) throws SipRoutingException {
if (!reuseConnections) {
return;
}
URI requestUri = request.getRequestURI();
String contact = null;
if (requestUri.isSipURI()) {
SipURI sipUri = (SipURI) requestUri;
contact = createContact(sipUri);
} else {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Contact address is not sip uri");
}
return;
}
Address storedPathValue = feAddrMapForRegisterReq.get(contact);
if (storedPathValue == null) {
SipSession session = request.getSession(false);
if (session == null || !session.isValid()) {
if (logger.isLoggable(Level.FINER)) {
logger.finer("Session is null or invalid");
}
return;
}
storedPathValue = (Address) session.getAttribute(
CLBConstants.CLB_FE_ADDRESS);
}
if (storedPathValue != null) {
request.pushRoute(storedPathValue);
TargetTuple tt = null;
try {
tt = TargetResolver.getInstance().resolveRequest(request, request.getContentLength());
} catch (Exception ex) {
throw new SipRoutingException("Unable to resolve address for request", ex);
}
tt.setBindIp(bindHostName);
tt.setBindPort(bindPort);
tt.setCLBEndpoint(true);
request.setResolvedRemote(tt);
if (logger.isLoggable(Level.FINER)) {
logger.finer("Request after adding FE as route header :\n" + request);
}
}
}
private String createContact(SipURI sipUri) {
String contact;
contact = sipUri.getUser() + "@" + sipUri.getHost() + ":" + sipUri.getPort();
return contact;
}
private Address createSipAddress(String hostName, int port) {
SipURI feSipUri = new SipURIImpl();
feSipUri.setHost(hostName);
feSipUri.setPort(port);
feSipUri.setLrParam(true);
feSipUri.setParameter(CLBConstants.FE_LB_PARAM, "TRUE");
Address feAddress = SipFactoryImpl.getInstance().createAddress(feSipUri);
return feAddress;
}
class FEAddressMapCleanupTask extends TimerTask{
String contact;
public FEAddressMapCleanupTask(String contact) {
this.contact = contact;
}
@Override
public void run() {
removeFEAddrForRegisterReq(contact);
feAddrCleanupTasksMap.remove(contact);
}
}
}