/*
* 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.config;
import com.ericsson.ssa.sip.RemoteLockException;
import com.ericsson.ssa.sip.RemoteLockRuntimeException;
import com.ericsson.ssa.sip.SipApplicationSessionImpl;
import com.ericsson.ssa.sip.SipApplicationSessionUtil;
import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipSessionManager;
import com.ericsson.ssa.sip.URIImpl;
import com.ericsson.ssa.sip.UriUtil;
import com.ericsson.ssa.utils.UUIDUtil;
import com.ericsson.ssa.container.sim.InvocationContextUtil;
import org.jvnet.glassfish.comms.deployment.backend.SipApplication;
import org.jvnet.glassfish.comms.deployment.backend.SipApplicationListeners;
import org.jvnet.glassfish.comms.security.auth.impl.AuthInfoImpl;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.sip.Address;
import javax.servlet.sip.AuthInfo;
import javax.servlet.sip.Parameterable;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.SipApplicationSession;
import javax.servlet.sip.SipFactory;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.SipURI;
import javax.servlet.sip.URI;
/**
* Facade for the SipFactoryImpl singelton.
*
* A new instance of SipFactoryFacade is bound to each context, with key
* "javax.servlet.sip.SipFactory"
*
* @author lmcpepe
*/
public class SipFactoryFacade implements SipFactory {
private static final Logger logger = LogUtil.SIP_LOGGER.getLogger();
private ConvergedContext m_Ctx;
private String m_ApplicationName;
private SipFactoryImpl m_ConcreteSipFactory;
private SipApplication m_SipApplication;
private SipSessionManager m_SipSessionManager;
//
private Method sessionKey = null;
/**
* @param sipApplication
*/
public SipFactoryFacade(ConvergedContext ctx, SipFactoryImpl sipfactory, SipApplication sipApplication) {
m_Ctx = ctx;
m_ApplicationName = ctx.getAppName();
m_SipSessionManager = ctx.getSipSessionManager();
m_ConcreteSipFactory = sipfactory;
m_SipApplication = sipApplication;
}
/**
* Constructor called from startup to bind a Referece to a new facade in JNDI.
* A default Facade is provided to @Resource annotated SipFactory in Java EE components
*
* To create an Application Session on a default Facade, the method
* createApplicationSessionByAppname(String appName) has to be used.
*
*/
public SipFactoryFacade() {
m_ConcreteSipFactory = SipFactoryImpl.getInstance();
}
/**
* Set context specific data for a Facade, this configures the Facad to be context avare
*
* @param ctx
* @param sipApplication
*/
public void setContextData(ConvergedContext ctx, SipApplication sipApplication) {
m_Ctx = ctx;
m_ApplicationName = ctx.getAppName();
m_SipSessionManager = ctx.getSipSessionManager();
m_SipApplication = sipApplication;
}
public SipURI createSipURI(String user, String host) {
return m_ConcreteSipFactory.createSipURI(user, host);
}
public URI createURI(String uri) throws ServletParseException {
return m_ConcreteSipFactory.createURI(uri);
}
public Address createAddress(String address) throws ServletParseException {
return m_ConcreteSipFactory.createAddress(address);
}
public Address createAddress(URI uri) {
return m_ConcreteSipFactory.createAddress(uri);
}
public Address createAddress(URI uri, String string) {
return m_ConcreteSipFactory.createAddress(uri, string);
}
public Parameterable createParameterable(String s) throws ServletParseException {
return m_ConcreteSipFactory.createParameterable(s);
}
public SipServletRequest createRequest(SipApplicationSession appSession, String method, Address from, Address to) {
SipServletRequestImpl request = m_ConcreteSipFactory.createRequest(appSession, method, from, to);
postCreateRequest(request);
return request;
}
public SipServletRequest createRequest(SipApplicationSession appSession, String method, URI from, URI to) {
SipServletRequestImpl request = m_ConcreteSipFactory.createRequest(appSession, method, from, to);
postCreateRequest(request);
return request;
}
public SipServletRequest createRequest(SipApplicationSession appSession, String method, String from, String to) throws ServletParseException {
SipServletRequestImpl request = m_ConcreteSipFactory.createRequest(appSession, method, from, to);
postCreateRequest(request);
return request;
}
@Deprecated
public SipServletRequest createRequest(SipServletRequest origRequest, boolean sameCallId) {
SipServletRequestImpl request = m_ConcreteSipFactory.createRequest(origRequest, sameCallId);
postCreateRequest(request);
return request;
}
/**
* create a unique SAS (keyed by a random string)
* <br>
* Used by the application
* @return the created SAS
*/
public SipApplicationSession createApplicationSession() {
SipApplicationSessionImpl as = createApplicationSessionImpl();
as.setShouldBePersisted(); // could delay until ID is requested
return as;
}
/**
* Create a unique SAS (keyed by a random string)
* <br>
* Used internally
* @return the created SAS
*/
public SipApplicationSessionImpl createApplicationSessionImpl() {
SipApplicationSessionImpl as = m_SipSessionManager.createSipApplicationSession(m_Ctx.getSipApplicationListeners());
return as;
}
public SipServletRequestImpl createRequestImpl(SipApplicationSessionImpl appSession, String method, Address from, Address to, boolean sameCallID, SipServletRequestImpl origRequest) {
SipServletRequestImpl request = m_ConcreteSipFactory.createRequestImpl(appSession, method, from, to, sameCallID, origRequest);
postCreateRequest(request);
return request;
}
public void initKeyMethod(Method m) {
sessionKey = m;
}
/**
* The SipApplicationKey annotation is processed by a classloader specific for
* deployment. We need to make sure that the classloader is the same as this
* application.
*
* For any other annotation, it is not an issue. However 289 specification mentions
* that SAK is annotated on a static method. So, the execution will happen on the
* the class declared using the deployment classloader.
*/
public synchronized Method getSessionKey(ClassLoader cl) {
if (sessionKey.getDeclaringClass().getClassLoader() != cl) {
try {
Class c = cl.loadClass(sessionKey.getDeclaringClass().getName());
sessionKey =
c.getMethod(sessionKey.getName(), sessionKey.getParameterTypes());
} catch (Exception e) {
logger.log(Level.WARNING, e.getMessage(), e);
}
}
return sessionKey;
}
/**
* @return Returns the m_sipApplicationListeners.
*/
public SipApplicationListeners getSipApplicationListeners() {
return m_Ctx.getSipApplicationListeners();
}
private void postCreateRequest(SipServletRequestImpl request) {
request.setRole(m_ApplicationName);
if (request.getSessionImpl().getHandler() == null) {
try {
request.getSessionImpl().setHandler(m_SipApplication.getDefaultServlet());
} catch (ServletException ignore) {
}
}
}
/**
* Implements the jsr289 session key lookup concept
* Checks if there is any ref to clear and then if the SAS that we are
* looking for is already created
*/
public SipApplicationSessionImpl createApplicationSession(SipServletRequestImpl request) {
SipApplicationSessionImpl sas = null;
sas = extractUriEncodedSas(request);
if (sas != null) {
return sas;
}
String beKey = request.getBeKey();
sas = extractSasFromBekeyAndSak(request, beKey);
if (sas != null) {
return sas;
}
if (beKey != null) {
return m_SipSessionManager.createSipApplicationSession(SipApplicationSessionUtil.createSasId(beKey, m_Ctx.getAppName(), UUIDUtil.randomUUID()), m_Ctx.getSipApplicationListeners());
} else {
return createApplicationSessionImpl();
}
}
private SipApplicationSessionImpl extractUriEncodedSas(SipServletRequestImpl request) {
try {
String uriEncodedSasId = UriUtil.getAndDecodeParameter(request.getRequestURI(), URIImpl.SASID_PARAM);
if (uriEncodedSasId != null) {
String appName = SipApplicationSessionUtil.getApplicationName(uriEncodedSasId);
if (!appName.equals(m_ApplicationName)) {
logger.log(Level.WARNING, "com.ericsson.ssa.config.SipFactoryFacade.appname_mismatch", new Object[] {
appName, m_ApplicationName
});
} else {
SipApplicationSessionImpl proposedSas = m_SipSessionManager.findSipApplicationSession(uriEncodedSasId);
if ((proposedSas != null) && proposedSas.isValid()) {
return proposedSas;
}
logger.log(Level.WARNING, "com.ericsson.ssa.config.SipFactoryFacade.uri_encoded_sas_not_found");
}
}
} catch (UnsupportedEncodingException e1) {
logger.log(Level.WARNING, "com.ericsson.ssa.config.SipFactoryFacade.uri_encoded_sas_corrupt", e1);
} catch (RemoteLockException e) {
throw new RemoteLockRuntimeException(e);
}
return null;
}
private SipApplicationSessionImpl extractSasFromBekeyAndSak(SipServletRequestImpl request, String beKey) {
if (sessionKey != null) {
// Cleanup move to xxx
// if (logger.isLoggable(Level.FINE))
// logger.log(Level.FINE, "Cleanup Removing SAS : " + pr.get());
// activeSessions.remove(pr.getSessionId());
String sakKey = null;
// Save Current Context ClassLoader
ClassLoader storedClassLoader = Thread.currentThread().getContextClassLoader();
try {
ClassLoader cl = m_Ctx.getLoader().getClassLoader();
Thread.currentThread().setContextClassLoader(cl);
InvocationContextUtil.preInvoke(m_Ctx);
sakKey = (String) getSessionKey(cl).invoke(null, request);
} catch (Exception e) {
logger.log(Level.WARNING, "com.ericsson.ssa.config.SipFactoryFacade.session_key_not_working", e);
}finally {
InvocationContextUtil.postInvoke(m_Ctx);
Thread.currentThread().setContextClassLoader(storedClassLoader);
}
// NOTE, (SAK == null) is interpreted as if the application do not want to tie this
// request to an existing SAS, i.e. as if the SAK mechanism was temporarily disabled.
// The request will thus be tied to a new SAS where the ID contains a UUID (and possibly a bekey).
if (sakKey != null) {
String key = null;
if (beKey != null) {
key = beKey;
if (!beKey.equals(sakKey)) {
logger.log(Level.WARNING, "com.ericsson.ssa.config.SipFactoryFacade.session_key_not_match_bekey", beKey);
}
} else {
key = sakKey;
}
String sasId = SipApplicationSessionUtil.createSasId(key, m_Ctx.getAppName(), null);
// There is a key now look for an existing SAS
SipApplicationSessionImpl ref = null;
try {
ref = m_SipSessionManager.findSipApplicationSession(sasId);
} catch (RemoteLockException e) {
throw new RemoteLockRuntimeException(e);
}
if ((ref != null) && !ref.isValid()) {
m_SipSessionManager.removeSipApplicationSession(ref);
ref = null;
}
if (ref != null) { // There is a cached value to be recycled
return ref;
}
// Need to create a new SAS and store it
return m_SipSessionManager.createSipApplicationSession(sasId, m_Ctx.getSipApplicationListeners());
}
}
return null;
}
public AuthInfo createAuthInfo() {
return new AuthInfoImpl();
}
public SipApplicationSession createApplicationSessionByAppName(String sipAppName) throws IllegalStateException {
SipApplicationSession sas = null;
if (sipAppName == null) {
// We should probably throw an exception, in the new upcoming spec
throw new IllegalStateException("Application Name Not Specified");
}
//TODO Likely to be changed to the following in the updated spec
// else if (sipAppName.equals(m_ApplicationName)){
// throw new IllegalStateException("Method cannot be called from same applicaton");
// }
else {
String internalSipFactoryey = "sip/" + sipAppName + "/SipFactory";
SipFactory sf = (SipFactoryMap.getInstance()).getSipFactoryFacade(internalSipFactoryey);
if (sf == null) {
// Application not deployed
throw new IllegalStateException("SipFactory for " + sipAppName + " could not be retreived");
}
sas = sf.createApplicationSession();
}
return sas;
}
public SipApplicationSession createApplicationSessionByKey(String sipApplicationKey) throws IllegalStateException {
if (sipApplicationKey == null) {
throw new IllegalArgumentException("The key must not be null.");
}
String sasId = SipApplicationSessionUtil.createSasId(sipApplicationKey, m_Ctx.getAppName(), null);
// There is a key now look for an existing SAS
SipApplicationSessionImpl ref = null;
try {
ref = m_SipSessionManager.findSipApplicationSession(sasId);
if ((ref != null) && !ref.isValid()) {
m_SipSessionManager.removeSipApplicationSession(ref);
ref = null;
}
} catch (RemoteLockException ex) {
throw new RemoteLockRuntimeException(ex);
}
if (ref != null) { // There is a cached value to be recycled
return ref;
}
// Need to create a new SAS and store it
SipApplicationSessionImpl sas = m_SipSessionManager.createSipApplicationSession(sasId, m_Ctx.getSipApplicationListeners());
sas.setShouldBePersisted(); // on a SAS created by key we want to persist
return sas;
}
}