/*
* 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.container.sim;
import com.ericsson.ssa.config.SipFactoryFacade;
import com.ericsson.ssa.sip.AuthModule;
import com.ericsson.ssa.sip.SecurityInterceptor;
import com.ericsson.ssa.fm.FmEventSender;
import com.ericsson.ssa.sip.AddressImpl;
import com.ericsson.ssa.sip.DialogFragment;
import com.ericsson.ssa.sip.DialogSet;
import com.ericsson.ssa.sip.RemoteLockException;
import com.ericsson.ssa.sip.SipApplicationSessionImpl;
import com.ericsson.ssa.sip.SipFactoryImpl;
import com.ericsson.ssa.sip.SipServletRequestImpl;
import com.ericsson.ssa.sip.SipSessionImplBase;
import com.ericsson.ssa.sip.SipSessionManager;
import com.ericsson.ssa.sip.dialog.DialogLifeCycle;
import com.ericsson.ssa.sip.transaction.ContainerTransaction;
import org.apache.catalina.Context;
import org.apache.catalina.InstanceListener;
import org.apache.catalina.Wrapper;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardWrapper;
import org.apache.catalina.valves.ValveBase;
import org.jvnet.glassfish.comms.deployment.backend.ServletMapping;
import org.jvnet.glassfish.comms.deployment.backend.SipApplication;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import java.util.logging.Level;
// inserted by hockey (automatic)
import org.jvnet.glassfish.comms.util.LogUtil;
import java.util.logging.Logger;
import javax.servlet.ServletException;
import javax.servlet.ServletRequestEvent;
import javax.servlet.ServletRequestListener;
import javax.servlet.sip.ServletParseException;
import javax.servlet.sip.ar.SipApplicationRouterInfo;
import javax.servlet.sip.SipServletRequest;
import javax.servlet.sip.TooManyHopsException;
import javax.servlet.sip.URI;
public class ServletDispatcher extends ValveBase {
private static final Logger logger = LogUtil.SIP_LOGGER.getLogger();
/**
* The parsed sip.xml object model
*/
SipApplication sipApplicationModel;
private SipFactoryFacade m_sipFactory = null;
private HashMap<String, Wrapper> wrappers = new HashMap<String, Wrapper>(5,
0.75f);
private AuthModule authModule = null;
private SipSessionManager m_SipSessionManager = null;
private String contextId = null;
String appName = null;
/**
* Constructor.
*/
public ServletDispatcher(SipSessionManager manager) {
super();
m_SipSessionManager = manager;
appName = m_SipSessionManager.getContext().getAppName();
}
/**
* @return Returns the sipApplicationModel.
*/
public SipApplication getSipApplicationModel() {
return sipApplicationModel;
}
/**
* @param sipApplicationModel
* The sipApplicationModel to set.
*/
public void setSipApplicationModel(SipApplication sipApplicationModel) {
this.sipApplicationModel = sipApplicationModel;
}
/**
* For HTTP the next valve is invoked
*
*/
@Override
public int invoke(org.apache.catalina.Request request,
org.apache.catalina.Response response)
throws IOException, ServletException {
return INVOKE_NEXT;
}
/**
* Creates a Session and atach it to the Request.
*
* @param req
* Request
* @param appSess
* Application Session
* @param handlerName
* Servlet to handle the request
* @param info
* Application Router info used to initiate the session
* @throws RemoteLockException
*/
private void createSession(SipServletRequestImpl req,
SipApplicationSessionImpl appSess, String handlerName,
URI subscriberURI) {
SipSessionImplBase s = (SipSessionImplBase) req.getSessionImpl();
if (s == null) {
// check if we are spiraling: look up early dialog
String id = DialogSet.createKey(req.getCallId(),
req.getFromImpl().getParameter(AddressImpl.TAG_PARAM));
DialogSet existingDs = DialogSet.getEarlyDialog(id);
if (existingDs != null) {
// spiraling indeed!
req.setDialog(existingDs.createAdditionalDialogFragment());
} else {
// not spiraling
// creates a new sip session and dialog structure
DialogSet ds = new DialogSet(req.getMethod(), req.getCallId(), req.getFromImpl(),
req.getBeKey(), SipFactoryImpl.isDialogCreational(req.getMethod()));
req.setDialog(ds.getInitialDialogFragment());
DialogSet.registerEarlyDialog(req);
}
}
// creates a new sip session
DialogFragment d = req.getDialog();
s = m_SipSessionManager.createSipSession(d.getDialogSet(), req.getToImpl(),
appSess, handlerName);
// Initial request from outside the container.
// So, swap the local-remote.
s.swapLocalRemote();
if (handlerName != null && !handlerName.equals("")) {
TargettedRequestHandler.linkCorrespondingSession(req, s);
}
s.setSubscriberURI(subscriberURI);
req.setSession(s);
final DialogLifeCycle dialogLifeCycle = d.getDialogLifeCycle();
dialogLifeCycle.associateTransaction(req.getTransactionId());
dialogLifeCycle.initUnitOfWork(appSess);
dialogLifeCycle.setThreadLocalUnitOfWork();
if (appSess != null) {
appSess.setShouldBePersisted();
}
}
private URI getSubscriberURI(SipApplicationRouterInfo info) {
// JSR 289 chapter 15.4.1
//
// TODO what about region?
String subscriberURIStr = info.getSubscriberURI();
URI subscriberURI = null;
if (!("".equals(subscriberURIStr)) && subscriberURIStr != null) {
try {
subscriberURI = m_sipFactory.createURI(subscriberURIStr);
} catch (ServletParseException e) {
if (logger.isLoggable(Level.WARNING)) {
logger.warning(
"Invalid subscriber URI specified in router " +
"info, using null: " + e);
}
}
}
return subscriberURI;
}
/**
* This method checks the servlet mapping roles and call the service method
* of the servlet (wrapper).
*
* @param request
* @param application
* @throws ServletInvocationException
* @throws RemoteLockException
* @throws IOException
* @throws Exception
*/
public boolean invoke(SipServletRequestImpl request,
SipApplicationRouterInfo info) throws TooManyHopsException, ServletInvocationException, IOException {
boolean matched = false;
//authentication based on login-config only for M1.
if (sipApplicationModel != null) {
if (authModule == null) {
authModule = new AuthModule(sipApplicationModel, this.appName, contextId);
}
}
SipApplicationSessionImpl as = null;
List<String> servlets = findServlets(request);
// Found servlet(s) in an application which can handle the request
// (mapping roles),
// but if empty WE DO NOT need to do anything here. AD will invoke
// this method with next the application.
if (!servlets.isEmpty()) {
URI subscriberURI = getSubscriberURI(info);
request.setSubscriberURI(subscriberURI);
as = TargettedRequestHandler.getApplicationSession(request, info);
if (as == null) {
as = m_sipFactory.createApplicationSession(request);
}
Iterator<String> i = servlets.iterator();
if (i.hasNext()) {
String servletToInvoke = i.next();
matched = true;
// Setting a current servlet in the AS to be used as
// DefaultHandler.
as.setCurrentServlet(servletToInvoke);
createSession(request, as, servletToInvoke, subscriberURI);
SecurityInterceptor si = SecurityInterceptor.getInstance();
if (!si.verifyRequest(request, authModule, servletToInvoke)) {
return matched;
}
request.setRegion(info.getRoutingRegion());
// XXX why not set the subscriber URI here as well?
// is there really any use of setting the subscriberURI for error responses?
// who will ever look into these, before the reinvocation?
SipServletWrapper wrap = (SipServletWrapper) wrappers.get(servletToInvoke);
//Object securityContext = authModule.preSetRunAsIdentity(servletToInvoke);
doPreInvoke(request);
// wrap.allocate().service(request, null);
wrap.invokeServlet(request);
doPostInvoke(request);
// if(securityContext == null){
// authModule.postSetRunAsIdentity(securityContext);
// }
// If the invoked Servlet has not sent a response/request
// Need to put in a 64*T1 guard
if (!request.hasSentOnThread()) {
new ContainerTransaction(request);
}
}
}
return matched;
}
/**
* The method will check the deployment discriptors of the servlets and
* initialize the corresponding wrappers.
*/
public void initialize() {
Map<String, org.jvnet.glassfish.comms.deployment.backend.Servlet> servlets = sipApplicationModel.getServlets();
for (Iterator<String> it = servlets.keySet().iterator(); it.hasNext();) {
String servletName = it.next();
Wrapper wrapper = initializeWrapper(servletName);
wrappers.put(servletName, wrapper);
getContainer().addChild(wrapper);
}
// Register Application with Application Dispatcher
ApplicationDispatcher.getInstance().addServletDispatcher(appName, this);
// Set Session Key in SipFactory
Method sasKey = sipApplicationModel.getSipApplicationKey();
if (sasKey != null) {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"Found @SipApplicationKey method :" + sasKey.getName());
}
} else {
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE,
"No @SipApplicationKey method found");
}
}
m_sipFactory.initKeyMethod(sipApplicationModel.getSipApplicationKey());
}
public void unInitialize() {
// Stop SipServlets
Iterator<Wrapper> it = wrappers.values().iterator();
while (it.hasNext()) {
getContainer().removeChild(it.next());
}
wrappers.clear();
// Unregister this ServletDispatcher from the Application Dispatcher
// if it has not already been removed.
// During undeployment, a call to ConvergedContext.setAvailable(false)
// will have already unregistered this ServletDispatcher.
// See SailFin Issue 1270
if (ApplicationDispatcher.getInstance().getServletDispatcher(appName) != null) {
ApplicationDispatcher.getInstance().removeServletDispatcher(appName);
}
}
/**
* Initializes the SipServlet Wrappers with servlet class, loader and parent
*
* @param servletName
* @return
*/
protected Wrapper initializeWrapper(String servletName) {
Wrapper wrapper = new SipServletWrapper(m_sipFactory,
sipApplicationModel.getServlet(servletName));
StandardContext context = (StandardContext) getContainer();
String[] instanceListeners = context.findInstanceListeners();
for (int i = 0; i < instanceListeners.length; i++) {
try {
Class clazz = Class.forName(instanceListeners[i]);
InstanceListener listener = (InstanceListener) clazz.newInstance();
wrapper.addInstanceListener(listener);
} catch (Throwable t) {
logger.log(Level.SEVERE,
"Failed creating instance listener for Wrapper", t);
}
}
wrapper.setServletClass(sipApplicationModel.getServlet(servletName).getServletClass());
wrapper.setParent(container);
return wrapper;
}
/**
* Find all servlets that trigerring rules matches a particular request, and
* keeping the order of the servlets in the sip.xml file.
*
* @param request
* The request
* @return A <code>List</code> of servlet name to invoke
*/
protected List<String> findServlets(SipServletRequest request) {
// We should keep the ordering of the servlets as it is
// in the sip.xml. Do not trust the ordering of the mapping
// getServletMapping return a HashMap but getServlet return
// a LinkedHashMap which support a insertion-ordering. So itertation
// should be on the servlets, not on the mapping (SSA1.0 chap. 11.1).
ArrayList<String> matchingServlets = new ArrayList<String>();
Map<String, org.jvnet.glassfish.comms.deployment.backend.Servlet> servlets = sipApplicationModel.getServlets();
Map<String, ServletMapping> mappings = sipApplicationModel.getServletMappings();
Iterator<String> itr = servlets.keySet().iterator();
while (itr.hasNext()) {
String servletName = itr.next();
ServletMapping mapping = mappings.get(servletName);
if ((mapping != null) &&
mapping.getPattern().getTopCondition().evaluate(request)) {
matchingServlets.add(servletName);
if (logger.isLoggable(Level.FINE)) {
logger.log(Level.FINE, "Matched Servlet " + servletName +
" with incoming request");
}
}
}
return matchingServlets;
}
/**
* Check if a given servlet name included within the same application. This
* method will be called from the application session or more correct from
* sip session setHandler() method.
*
* @param servletName
* @return boolean if the servlet name included within the same application.
*/
public boolean findServlet(String servletName) {
Map<String, org.jvnet.glassfish.comms.deployment.backend.Servlet> servlets = sipApplicationModel.getServlets();
return servlets.containsKey(servletName);
}
/**
* Loads all the <code>SipServlet</code>s that have a <i>load-on-startup</i>
* attribute greater or equal to 0. There is no guarantee on the load order
* of servlets with equal <i>load-on-startup</i> values.
*/
public void loadServletsMarkedOnStartup() throws ServletException {
TreeSet<org.jvnet.glassfish.comms.deployment.backend.Servlet> set = new TreeSet<org.jvnet.glassfish.comms.deployment.backend.Servlet>(new ServletStartupOrderComparator());
set.addAll(sipApplicationModel.getServlets().values());
for (Iterator it = set.iterator(); it.hasNext();) {
org.jvnet.glassfish.comms.deployment.backend.Servlet servlet =
(org.jvnet.glassfish.comms.deployment.backend.Servlet) it.next();
if (servlet.getLoadOnStartup() >= 0) {
Wrapper wrapper = getWrapper(servlet.getServletName());
try {
wrapper.allocate();
} catch (ServletException e) {
// NOTE: load errors (including a servlet that throws
// UnavailableException from the init() method) are NOT
// fatal to application startup
logger.log(Level.WARNING,
String.format(
"Servlet '%1$s' threw an exception during initialization.",
wrapper.getName()), StandardWrapper.getRootCause(e));
// Generate a servlet initialization failure notification.
Context context = (Context) container;
FmEventSender.servletInitializationFailed(context.getName(),
wrapper.getName(), e.getMessage());
throw e;
}
}
}
}
/**
* The method map a wrapper for a servlet.
*
* @param servlet
* Name The name of the servlet.
* @return A <code>Wrapper</code> associated with the servlet name.
*/
protected Wrapper getWrapper(String servletName) {
Wrapper wrapper = (Wrapper) wrappers.get(servletName);
if (wrapper == null) {
wrapper = initializeWrapper(servletName);
wrappers.put(servletName, wrapper);
}
return wrapper;
}
/**
* Add a wrapper to the map.
*
* @param ServletName
* @param wrapper
*/
public void addWrapper(String servletName, Wrapper wrapper) {
wrappers.put(servletName, wrapper);
}
/**
* The method allocate a servlet instance.
*
* @param handler
* @return A <code>Servlet</code> associated with handler.
*/
public javax.servlet.Servlet getHandler(String handler) {
javax.servlet.Servlet servlet = null;
Wrapper wrap = (Wrapper) wrappers.get(handler);
if (wrap != null) {
try {
servlet = wrap.allocate();
} catch (ServletException e) {
logger.log(Level.SEVERE, "Error allocating Servlet " + handler,
e);
}
}
return servlet;
}
/**
* The method return a <code>SipFactoryFacade</code>.
*
* @return A <code>SipFacotryFacade</code>
*/
public SipFactoryFacade getSipFactory() {
return m_sipFactory;
}
public void setContextId(String contextId) {
this.contextId = contextId;
}
public String getContextId() {
return this.contextId;
}
/**
* The method set a <code>SipFactoryFacade</code>.
*
* @param sipFactory
* A <code>SipFacotryFacade</code>.
*/
public void setSipFactory(SipFactoryFacade sipFactory) {
this.m_sipFactory = sipFactory;
}
public AuthModule getAuthModule() {
if (sipApplicationModel != null) {
if (authModule == null) {
authModule = new AuthModule(sipApplicationModel, appName, contextId);
}
}
return authModule;
}
/*
* public void registerListener(String servletName, String servletClass,
* Servlet instance) { if (instance instanceof TimerListener) {
* m_sipFactory.getSipApplicationListeners().setTimerListener((TimerListener)
* instance); } else if (instance instanceof SipApplicationSessionListener) {
* m_sipFactory.getSipApplicationListeners().addSipApplicationSessionListener(
* (SipApplicationSessionListener) instance); } else if (instance instanceof
* SipSessionListener) {
* m_sipFactory.getSipApplicationListeners().addSipSessionListener(
* (SipSessionListener) instance); } else if (instance instanceof
* SipErrorListener) {
* m_sipFactory.getSipApplicationListeners().addSipErrorListener((SipErrorListener)
* instance); } else if (instance instanceof SipSessionAttributeListener) {
* m_sipFactory.getSipApplicationListeners().addSipSessionAttributeListener(
* (SipSessionAttributeListener) instance); } }
*/
public void doPreInvoke(SipServletRequest request) {
StandardContext context = (StandardContext) getContainer();
Object[] instances = context.getApplicationEventListeners();
ServletRequestEvent event = null;
if ((instances != null) && (instances.length > 0)) {
event = new ServletRequestEvent(((StandardContext) container).getServletContext(),
request);
// create pre-service event
for (int i = 0; i < instances.length; i++) {
if (instances[i] == null) {
continue;
}
if (!(instances[i] instanceof ServletRequestListener)) {
continue;
}
ServletRequestListener listener = (ServletRequestListener) instances[i];
// START SJSAS 6329662
context.fireContainerEvent("beforeRequestInitialized", listener);
try {
listener.requestInitialized(event);
} catch (Throwable t) {
logger.log(Level.SEVERE,
"Failed calling requestInitialized()", t);
}
}
}
}
public void doPostInvoke(SipServletRequest request) {
StandardContext context = (StandardContext) getContainer();
Object[] instances = context.getApplicationEventListeners();
ServletRequestEvent event = new ServletRequestEvent(context.getServletContext(),
request);
if ((instances != null) && (instances.length > 0)) {
// create post-service event
for (int i = 0; i < instances.length; i++) {
if (instances[i] == null) {
continue;
}
if (!(instances[i] instanceof ServletRequestListener)) {
continue;
}
ServletRequestListener listener = (ServletRequestListener) instances[i];
// START SJSAS 6329662
context.fireContainerEvent("beforeRequestDestroyed", listener);
// END SJSAS 6329662
try {
listener.requestDestroyed(event);
} catch (Throwable t) {
logger.log(Level.SEVERE,
"Failed calling requestDestroyed()", t);
// START SJSAS 6329662
} finally {
context.fireContainerEvent("afterRequestDestroyed", listener);
// END SJSAS 6329662
}
}
}
}
public void logActiveCaches() {
if (m_SipSessionManager != null) {
m_SipSessionManager.logActiveObjects();
}
}
}