001    /* 
002     * Copyright 2008-2009 the original author or authors.
003     * The contents of this file are subject to the Mozilla Public License
004     * Version 1.1 (the "License"); you may not use this file except in
005     * compliance with the License. You may obtain a copy of the License at
006     * http://www.mozilla.org/MPL/
007     *
008     * Software distributed under the License is distributed on an "AS IS"
009     * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
010     * License for the specific language governing rights and limitations
011     * under the License.
012     */
013     
014    package com.mtgi.analytics.servlet;
015    
016    import java.util.Collection;
017    
018    import javax.servlet.ServletContext;
019    import javax.servlet.ServletContextEvent;
020    import javax.servlet.ServletContextListener;
021    import javax.servlet.ServletRequest;
022    import javax.servlet.ServletRequestEvent;
023    import javax.servlet.ServletRequestListener;
024    import javax.servlet.http.HttpServletRequest;
025    
026    import org.apache.commons.logging.Log;
027    import org.apache.commons.logging.LogFactory;
028    import org.springframework.web.context.WebApplicationContext;
029    import org.springframework.web.context.support.WebApplicationContextUtils;
030    
031    import com.mtgi.analytics.BehaviorEvent;
032    
033    /**
034     * Logs behavior tracking events for incoming servlet requests.  This listener is activated by the
035     * <code>bt:http-requests</code> Spring XML tag.  It is an alternative to {@link BehaviorTrackingFilter}
036     * registration in <code>web.xml</code>.  BehaviorTrackingFilter is more flexible, but is slightly more
037     * complex to configure.  At most one of these methods (bt:http-requests or BehaviorTrackingFilter) should
038     * be used in a given application, otherwise errors will be thrown during application startup.
039     */
040    public class BehaviorTrackingListener implements ServletRequestListener, ServletContextListener {
041    
042            private static final Log log = LogFactory.getLog(BehaviorTrackingListener.class);
043            
044            private static final String ATT_EVENTS = BehaviorTrackingListener.class.getName() + ".events";
045    
046            private ServletRequestBehaviorTrackingAdapter[] adapters = null;
047            
048            @SuppressWarnings("unchecked")
049            public void contextInitialized(ServletContextEvent event) {
050                    ServletContext context = event.getServletContext();
051                    WebApplicationContext spring = WebApplicationContextUtils.getWebApplicationContext(context);
052                    if (spring != null) {
053                            Collection<ServletRequestBehaviorTrackingAdapter> beans = spring.getBeansOfType(ServletRequestBehaviorTrackingAdapter.class, false, false).values();
054                            if (!beans.isEmpty()) {
055                                    if (BehaviorTrackingFilter.isFiltered(context))
056                                            throw new IllegalStateException("You have configured both BehaviorTrackingFilters and BehaviorTrackingListeners in the same web application.  Only one of these methods may be used in a single application.");
057                                    adapters = beans.toArray(new ServletRequestBehaviorTrackingAdapter[beans.size()]);
058                                    log.info("BehaviorTracking for HTTP servlet requests started");
059                            }
060                    }
061            }
062    
063            public void contextDestroyed(ServletContextEvent event) {
064                    if (adapters != null) {
065                            log.info("BehaviorTracking for HTTP servlet requests stopped");
066                            adapters = null;
067                    }
068            }
069    
070            public void requestInitialized(ServletRequestEvent event) {
071                    if (adapters != null) {
072                            BehaviorEvent[] events = new BehaviorEvent[adapters.length];
073                            ServletRequest request = event.getServletRequest();
074                            request.setAttribute(ATT_EVENTS, events);
075                            
076                            for (int i = 0; i < adapters.length; ++i)
077                                    try {
078                                            events[i] = adapters[i].start(request);
079                                    } catch (Exception e) {
080                                            log.error("Error starting http event", e);
081                                    }
082                    }
083            }
084    
085            public void requestDestroyed(ServletRequestEvent event) {
086                    if (adapters != null) {
087                            ServletRequest request = event.getServletRequest();
088                            BehaviorEvent[] events = (BehaviorEvent[])request.getAttribute(ATT_EVENTS);
089                            if (events == null) {
090                                    log.error("no behavior events stored in the current request (" + ((HttpServletRequest)request).getRequestURI());
091                            } else {
092                                    request.removeAttribute(ATT_EVENTS);
093                                    for (int i = 0; i < adapters.length; ++i)
094                                            try {
095                                                    adapters[i].stop(events[i]);
096                                            } catch (Exception e) {
097                                                    log.error("Error stopping http event", e);
098                                            }
099                            }
100                    }
101            }
102    
103    }