FileDocCategorySizeDatePackage
WebContainerListener.javaAPI DocGlassfish v2 API13439Fri May 04 22:24:18 BST 2007com.sun.enterprise.admin.monitor.callflow

WebContainerListener.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. 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.
 */

/*
 * WebContainerListener.java
 * $Id: WebContainerListener.java,v 1.14 2007/05/05 05:24:18 tcfujii Exp $
 * $Date: 2007/05/05 05:24:18 $
 * $Revision: 1.14 $
 */

package com.sun.enterprise.admin.monitor.callflow;

import org.apache.catalina.Wrapper;
import org.apache.catalina.InstanceEvent;
import org.apache.catalina.InstanceListener;
import org.apache.catalina.ContainerEvent;
import org.apache.catalina.ContainerListener;
import org.apache.catalina.core.StandardContext;
import org.apache.coyote.Adapter;
import org.apache.jasper.Constants;
import org.apache.jasper.servlet.JspServlet; 

import javax.servlet.Filter;
import javax.servlet.Servlet;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;

import com.sun.enterprise.Switch;
/*
 * WebContainerListener: Listens to web events and pushes to the callflow 
 * infrastructure.
 * @author Harpreet Singh
 * @author Ram Jeyaraman
 */
public class WebContainerListener
        implements InstanceListener, ContainerListener {
    
    private static final String JSP_SERVLET = JspServlet.class.getName();
    /**
     * Thread local object.
     *
     * This motivation for this object is to ensure that the natural call flow
     * sequence is maintained. Refer to the javadoc description of Agent class.
     * The natural call flow sequence expects startTime() to be called after
     * all the addRequestInfo() calls are completed.
     *
     * In the case of the web container, we need to explicitly issue the first
     * startTime() call, just before the beforeFilter or beforeService
     * operation. However, for nested local servlet calls, called by the first
     * Servlet, there is no need to explicitly issue the startTime() call,
     * since the web container provides the beforeDispatch() event.
     * Unfortunately, the beforeDispatch() event is not available for the
     * first Servlet call, it is only available for nested local servlet
     * invocations; so we use this thread local object as a work around.
     *
     * So, this thread local object exists, primarily to support the explicit
     * issuance of the first startTime() call and its corollary endTime() call,
     * during the invocation of the first Servlet or filter in the invocation
     * path.
     */
    private static final ThreadLocal<AgentImpl.FlowStack<Boolean>> threadLocal =
        new ThreadLocal() {
            protected AgentImpl.FlowStack<Boolean> initialValue() {
                return new AgentImpl.FlowStack<Boolean>();
            }
        };
    
       
        private static final ThreadLocal<Boolean> requestStartTls =
          new ThreadLocal() {
            protected Boolean initialValue(){
                return Boolean.FALSE;
            }
        };
        
    private Agent callFlowAgent;
    
    public WebContainerListener() {
        this.callFlowAgent = Switch.getSwitch().getCallFlowAgent();
    }
    
    public void instanceEvent(InstanceEvent event) {

        if (!callFlowAgent.isEnabled()) {
            return;
        }
        
        if (!requestStartTls.get ())
            return;        

        if (event.getType().equals(InstanceEvent.BEFORE_SERVICE_EVENT)) {
            AgentImpl.FlowStack<Boolean> flowStack = threadLocal.get();
            if (flowStack.size() == 0) {
                // Explicitly issue first startTime() only for the first
                // Servlet or filter call, in the invocation path.
                processBeforeDispatchEvent(event);
            }
            flowStack.push(Boolean.TRUE);
            processBeforeServiceEvent(event);
        } else if (event.getType().equals(InstanceEvent.AFTER_SERVICE_EVENT)) {
            processAfterServiceEvent(event);
            AgentImpl.FlowStack<Boolean> flowStack = threadLocal.get();
            flowStack.pop();
            if (flowStack.size() == 0) {
                // Explicitly issue the matching endTime() for the first
                // Servlet or filter call, in the invocation path.
                processAfterDispatchEvent(event);
            }
        } else if (event.getType().equals(InstanceEvent.BEFORE_FILTER_EVENT)) {
            AgentImpl.FlowStack<Boolean> flowStack = threadLocal.get();
            if (flowStack.size() == 0) {
                // Explicitly issue first startTime() only for the first
                // Servlet or filter call, in the invocation path.
                processBeforeDispatchEvent(event);
            }
            flowStack.push(Boolean.TRUE);
            processBeforeFilterEvent(event);
        } else if (event.getType().equals(InstanceEvent.AFTER_FILTER_EVENT)) {
            processAfterFilterEvent(event);
            AgentImpl.FlowStack<Boolean> flowStack = threadLocal.get();
            flowStack.pop();
            if (flowStack.size() == 0) {
                // Explicitly issue the matching endTime() for the first
                // Servlet or filter call, in the invocation path.
                processAfterDispatchEvent(event);
            }
        } else if (event.getType().
                equals(InstanceEvent.BEFORE_DISPATCH_EVENT)) {
            processBeforeDispatchEvent(event);
        } else if (event.getType().equals(InstanceEvent.AFTER_DISPATCH_EVENT)) {
            processAfterDispatchEvent(event);
        }
    }
    
    private void processBeforeServiceEvent(InstanceEvent event) {        

        Servlet servlet = event.getServlet();
        ServletConfig servletConfig = servlet.getServletConfig();
        String servletName = "UNKNOWN";
        if (servletConfig != null) {
            servletName = servletConfig.getServletName();
        }
        
        HttpServletRequest req = (HttpServletRequest) event.getRequest();
        String methodName = req.getRequestURI() + ":";
        if (!servlet.getClass().getName().equalsIgnoreCase(JSP_SERVLET)){
               methodName += servlet.getClass().getName() + ".service";
        } else {
            // JSP
            methodName += extractJSPName(req) +":" + JSP_SERVLET + ".service";
        }
        String callerPrincipal = req.getRemoteUser();
        if (callerPrincipal == null) {
            if (req.getUserPrincipal() != null) {
                callerPrincipal = req.getUserPrincipal().getName();
            } else {
                callerPrincipal = "anonymous";
            }
        }

        Wrapper wrapper = event.getWrapper();
        String applicationName =
                ((StandardContext)wrapper.getParent()).getJ2EEApplication();
        if ((applicationName == null) || (applicationName.equals("null"))) {
            applicationName = "URI:" + req.getRequestURI();
        }
        String moduleName = wrapper.getParent().getName();
         
        callFlowAgent.webMethodStart(
                methodName, applicationName, moduleName,
                servletName, ComponentType.SERVLET,
                callerPrincipal);
    }
    
    private void processAfterServiceEvent(InstanceEvent event) {
        Throwable exception = event.getException();
        callFlowAgent.webMethodEnd(exception);        
    }
    
    private void processBeforeFilterEvent(InstanceEvent event) {

        Filter filter = event.getFilter();
        
        HttpServletRequest req = (HttpServletRequest) event.getRequest();
        String methodName = req.getRequestURI() + ":" +
                            filter.getClass().getName() + ".doFilter";
        String callerPrincipal = req.getRemoteUser();
        if (callerPrincipal == null) {
            if (req.getUserPrincipal() != null) {
                callerPrincipal = req.getUserPrincipal().getName();
            } else {
                callerPrincipal = "anonymous";
            }
        }
        
        Wrapper wrapper = event.getWrapper();
        String applicationName =
                ((StandardContext)wrapper.getParent()).getJ2EEApplication();
        if ((applicationName == null) || (applicationName.equals("null"))) {
            applicationName = "URI:" + req.getRequestURI();
        }
        String moduleName = wrapper.getParent().getName();
                
        callFlowAgent.webMethodStart(
                methodName, applicationName, moduleName,
                filter.getClass().getName(),
                ComponentType.SERVLET_FILTER, callerPrincipal);
    }
    
    private void processAfterFilterEvent(InstanceEvent event) {
        Throwable exception = event.getException();
        callFlowAgent.webMethodEnd(exception);         
    }
        
    private void processBeforeDispatchEvent(InstanceEvent event) {
        callFlowAgent.startTime(ContainerTypeOrApplicationType.WEB_CONTAINER);
    }
    
    private void processAfterDispatchEvent(InstanceEvent event) {
        callFlowAgent.endTime();
    }
    
    /**
     * Receive event from the Grizzly HTTP Connector. 
     * The ContainerEvent.getData() will return the current 
     * <code>RequestInfo</code>, which contains request information.
     * @param event An instance of ContainerEvent.
     */
    public void containerEvent(ContainerEvent event) { 

        if (!callFlowAgent.isEnabled()) {
            return;
        }

        Object obj = event.getData();
        if (!(obj instanceof org.apache.coyote.RequestInfo)) {
            return;
        }
        org.apache.coyote.RequestInfo 
                requestInfo = (org.apache.coyote.RequestInfo) obj;
        if (Adapter.CONNECTION_PROCESSING_STARTED.equals(event.getType())) {
           requestStartTls.set (Boolean.TRUE);            
           callFlowAgent.requestStart(RequestType.REMOTE_WEB);    
           callFlowAgent.addRequestInfo(
                            RequestInfo.CALLER_IP_ADDRESS,
                            requestInfo.getRemoteAddr());
        }  else if (Adapter.REQUEST_PROCESSING_COMPLETED.
                    equals(event.getType())) {
           callFlowAgent.requestEnd();
        }
    }
    
    private String extractJSPName  (HttpServletRequest request){
  
       String jspUri = null;

       String jspFile = (String) request.getAttribute(Constants.JSP_FILE);
       if (jspFile != null) {
           // JSP is specified via <jsp-file> in <servlet> declaration
           jspUri = jspFile;
       } else {
           /*
            * Check to see if the requested JSP has been the target of a
            * RequestDispatcher.include()
            */
           jspUri = (String) request.getAttribute(Constants.INC_SERVLET_PATH);
           if (jspUri != null) {
               /*
                * Requested JSP has been target of
                * RequestDispatcher.include(). Its path is assembled from the
                * relevant javax.servlet.include.* request attributes
                */
               String pathInfo = (String) request.getAttribute(
                                   "javax.servlet.include.path_info");
               if (pathInfo != null) {
                   jspUri += pathInfo;
               }
           } else {
               /*
                * Requested JSP has not been the target of a
                * RequestDispatcher.include(). Reconstruct its path from the
                * request's getServletPath() and getPathInfo()
                */
               jspUri = request.getServletPath();
               String pathInfo = request.getPathInfo();
               if (pathInfo != null) {
                   jspUri += pathInfo;
               }
           }
       }
     return jspUri;
    }
}