/*
* 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.config.Constants;
import com.ericsson.ssa.config.ConvergedContext;
import com.ericsson.ssa.config.ConvergedContextImpl;
import com.ericsson.ssa.sip.SipApplicationSessionImpl;
import com.ericsson.ssa.sip.SipApplicationSessionUtil;
import com.ericsson.ssa.sip.persistence.ReplicationUnitOfWork;
import com.ericsson.ssa.utils.UUIDUtil;
import org.apache.catalina.Globals;
import org.apache.catalina.session.StandardSession;
import org.apache.catalina.session.StandardSessionFacade;
import org.apache.catalina.util.RequestUtil;
import org.jvnet.glassfish.comms.util.LogUtil;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.UUID;
import java.util.logging.Logger;
import java.util.logging.Level;
import javax.servlet.ServletContext;
import javax.servlet.sip.ConvergedHttpSession;
import javax.servlet.sip.SipApplicationSession;
/**
* Class representing a ConvergedHttpSession.
*
* @author jluehe
*/
public class ConvergedHttpSessionFacade extends StandardSessionFacade
implements ConvergedHttpSession {
private static final Logger logger = LogUtil.SIP_LOGGER.getLogger();
private static final String HTTP_PROTOCOL = "http";
private static final String HTTPS_PROTOCOL = "https";
// The parent SipApplicationSession
private SipApplicationSessionImpl sipAppSession;
// The nested HTTP session for which this object acts as a facade
private StandardSession httpSession;
/*
* The servlet context associated with the session manager of the session
* for which this object acts as a facade
*/
private ServletContext servletCtxt;
private ConvergedContext convergedCtxt;
// The SipSessionManager to look up the SipApplicationSession parent
private SipSessionManager sipSessionManager;
public void invalidate() {
try {
super.invalidate();
} finally {
removeFromSAS();
}
}
private void removeFromSAS() {
try {
SipApplicationSessionImpl sas =
(SipApplicationSessionImpl) getApplicationSession(false);
if (sas != null) {
// don't attempt to remove the httpsession again from its sessionmanager,
// super.invalidate() would already have removed it.
sas.removeSession(httpSession, false);
}
} catch (Exception e) { // could be remotelock or illegalstate exception.
logger.log(Level.WARNING, e.getMessage(), e);
}
}
/**
* Constructor
*/
public ConvergedHttpSessionFacade(StandardSession httpSession,
ConvergedContextImpl ctxt) {
super(httpSession);
this.httpSession = httpSession;
this.convergedCtxt = ctxt;
this.sipSessionManager = ctxt.getSipSessionManager();
this.servletCtxt = ctxt.getServletContext();
}
/**
* Returns the parent <code>SipApplicationSession</code> if it exists.
* If none exists, a new one is created (and this ConvergedHttpSession
* added as its child) and returned.
*
* @return the parent SipApplicationSession
*/
public SipApplicationSession getApplicationSession() {
return getApplicationSession(true);
}
/**
* Returns the parent <code>SipApplicationSession</code> if it exists.
* If none exists, and <code>create</code> is set to true, a new one is
* created (and this ConvergedHttpSession added as its child) and returned.
*
* @param create true if new SipApplicationSession is to be created if
* none already exists, false otherwise
*
* @return the parent SipApplicationSession
*/
public SipApplicationSession getApplicationSession(boolean create) {
if (sipAppSession != null) {
return sipAppSession;
}
// Check to see if our nested HTTP session already has any
// SipApplicationSession id associated with it.
String sipAppSessionId = httpSession.getSipApplicationSessionId();
if (sipAppSessionId == null) {
// Check to see if the request URL that was used to resume the
// HTTP session contains an encoded SipApplicationSession id.
sipAppSessionId = parseRequestParameter(
Constants.SAS_ID_URI_PARAMETER);
}
if (sipAppSessionId != null) {
// Look up the SipApplicationSession with the given id
try {
sipAppSession = sipSessionManager.findSipApplicationSession(sipAppSessionId);
} catch (RemoteLockException e) {
throw new RemoteLockRuntimeException(e);
}
} else if (create) {
// Create a new SipApplicationSession
String bekey = (String) httpSession.getBeKey();
if (bekey != null) {
sipAppSession = sipSessionManager.createSipApplicationSession(
SipApplicationSessionUtil.createSasId(bekey,
convergedCtxt.getAppName(), UUIDUtil.randomUUID()),
convergedCtxt.getSipApplicationListeners());
} else {
sipAppSession = sipSessionManager.createSipApplicationSession(
convergedCtxt.getSipApplicationListeners());
}
ReplicationUnitOfWork uow =
ReplicationUnitOfWork.getThreadLocalUnitOfWork();
if (uow != null) {
uow.lockApplicationSession(sipAppSession);
}
}
if ((sipAppSession != null) &&
(httpSession.getSipApplicationSessionId() == null)) {
// Associate the nested HTTP session with the SipApplicationSession
httpSession.setSipApplicationSessionId(sipAppSession.getId());
((SipApplicationSessionImpl) sipAppSession).addSession(this);
}
return sipAppSession;
}
/**
* Encodes the HTTP URL with the jsessionid. ";jsessionid=http-session-id".
* The URL parameter should be an absolute URL. For example,
* http://server:7001/mywebapp/foo.jsp. Where "/mywebapp" is the context
* path of the the current ServletContext, because that is where the
* httpSession belongs to.
*
* @param url the HTTP URL String to be encoded
*
* @return encoded URL with jsessionid
*/
public String encodeURL(String url) {
if (!isEncodeable(url)) {
return url;
} else {
return toEncoded(url);
}
}
/**
* Converts the given relative path to an absolute URL by prepending
* the contextPath for the current ServletContext, the given scheme
* ("http" or "https"), and the host:port, and then encoding the
* resulting URL with the jsessionid.
*
* For example, this method converts:
*
* from: "/foo.jsp"
* to: "http://server:8888/mywebapp/foo.jsp;jsessionid=http-session-id"
*
* Where, "/mywebapp" is the contextPath for the current ServletContext
* server is the front end host defined for the web server.
*
* @param relativePath relative to the current webapp
* @param scheme the scheme ("http" or "https")
*
* @return encoded URL with jsessionid
*/
public String encodeURL(String relativePath, String scheme) {
if ((relativePath == null) || !relativePath.startsWith("/")) {
throw new IllegalArgumentException("Illegal relative path: " +
relativePath); // XXX LOC
}
if ((scheme == null) ||
(!scheme.equals(HTTP_PROTOCOL) &&
!scheme.equals(HTTPS_PROTOCOL))) {
throw new IllegalArgumentException("Invalid protocol: " + scheme); // XXX LOC
}
String requestUrlString = (String) httpSession.getNote(
Constants.REQUEST_URL_SESSION_NOTE);
if (requestUrlString == null) {
return null;
}
URL requestUrl = null;
try {
requestUrl = new URL(requestUrlString);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
StringBuilder sb = new StringBuilder(scheme);
sb.append("://");
sb.append(requestUrl.getHost());
sb.append(':');
sb.append(requestUrl.getPort());
sb.append(servletCtxt.getContextPath());
sb.append(relativePath);
return encodeURL(sb.toString());
}
/**
* @param urlString The URL to be encoded
*
* @return true if the given URL needs to be encoded, false otherwise
*/
private boolean isEncodeable(String urlString) {
if (convergedCtxt != null && convergedCtxt.getCookies()) {
return false;
}
// Is the given (absolute) URL valid?
URL url = null;
try {
url = new URL(urlString);
} catch (MalformedURLException e) {
return false;
}
String myContextPath = servletCtxt.getContextPath();
if (myContextPath != null) {
String file = url.getFile();
// Make sure the context path in the given URL matches our own
if ((file == null) || !file.startsWith(myContextPath)) {
return false;
}
if (file.indexOf(";jsessionid=" + httpSession.getIdInternal()) >= 0) {
return false;
}
}
return true;
}
/**
* Encodes the given URL with the HTTP session id and the beroute
* (or bekey, whichever is present)
*
* @param url The URL to be encoded
*
* @return The encoded URL
*/
private String toEncoded(String url) {
if (url == null) {
return url;
}
String path = url;
String query = "";
String anchor = "";
int question = url.indexOf('?');
if (question >= 0) {
path = url.substring(0, question);
query = url.substring(question);
}
int pound = path.indexOf('#');
if (pound >= 0) {
anchor = path.substring(pound);
path = path.substring(0, pound);
}
StringBuilder encoded = new StringBuilder(path);
if (encoded.length() > 0) { // Encoded params can't be first.
// http session id and version
encoded.append(";jsessionid=");
encoded.append(httpSession.getIdInternal());
HashMap<String, String> sessionVersions = (HashMap<String, String>)
httpSession.getNote(Constants.SESSION_VERSIONS_SESSION_NOTE);
if (sessionVersions != null) {
String sessionVersion = RequestUtil.makeSessionVersionString(
sessionVersions);
if (sessionVersion != null) {
encoded.append(Globals.SESSION_VERSION_PARAMETER);
encoded.append(sessionVersion);
}
}
// beroute
String beroute = (String) httpSession.getNote(
Constants.BEROUTE_SESSION_NOTE);
if (beroute != null) {
encoded.append(Constants.BEROUTE_URI_PARAMETER);
encoded.append(beroute);
}
// bekey
String bekey = (String) httpSession.getBeKey();
if (bekey != null) {
encoded.append(Constants.BEKEY_URI_PARAMETER);
encoded.append(bekey);
}
}
encoded.append(anchor);
encoded.append(query);
return encoded.toString();
}
/*
* Checks to see if the request url of the request that was used to
* create or resume the underlying HTTP session contains, in encoded
* form, any parameter with the given name, and if so, returns its value.
*/
private String parseRequestParameter(String search) {
String requestUrlString = (String) httpSession.getNote(
Constants.REQUEST_URL_SESSION_NOTE);
if (requestUrlString == null) {
return null;
}
String value = null;
int index = requestUrlString.indexOf(search);
if (index != -1) {
int beginId = index + search.length();
int semicolon = requestUrlString.indexOf(';', beginId);
if (semicolon != -1) {
value = requestUrlString.substring(beginId, semicolon);
} else {
value = requestUrlString.substring(beginId);
}
}
return value;
}
public boolean isValid() {
return httpSession.isValid();
}
}