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.Enumeration;
017    import java.util.regex.Pattern;
018    
019    import javax.servlet.ServletRequest;
020    import javax.servlet.http.HttpServletRequest;
021    
022    import org.springframework.util.StringUtils;
023    
024    import com.mtgi.analytics.BehaviorEvent;
025    import com.mtgi.analytics.BehaviorTrackingManager;
026    import com.mtgi.analytics.EventDataElement;
027    
028    /**
029     * <p>Facilitates behavior tracking for servlet requests via calls to 
030     * {@link #start(ServletRequest)} and {@link #stop(BehaviorEvent)}.  The relative
031     * verbosity of event logging is configured in the {@link #ServletRequestBehaviorTrackingAdapter(String, BehaviorTrackingManager, String[]) constructor}.</p>
032     * 
033     * <p>This class abstracts the details of tracking servlet requests, so that
034     * it can be reused by delegation in both listeners and filters.</p>
035     */
036    public class ServletRequestBehaviorTrackingAdapter {
037            
038            private String eventType;
039            private BehaviorTrackingManager manager;
040            private String[] parameters;
041            private Pattern[] uriPatterns;
042            
043            public ServletRequestBehaviorTrackingAdapter(String eventType, BehaviorTrackingManager manager, String[] parameters, Pattern[] uriPatterns) {
044                    this.eventType = StringUtils.hasText(eventType) ? eventType: "http-request";
045                    this.manager = manager;
046                    this.parameters = parameters;
047                    this.uriPatterns = uriPatterns;
048            }
049    
050            public BehaviorEvent start(ServletRequest request) {
051                    
052                    HttpServletRequest req = (HttpServletRequest)request;
053                    if (!match(req))
054                            return null;
055    
056                    //use the request path as an event name, excluding proto, host, and query string.
057                    String eventName = req.getRequestURI();
058                    BehaviorEvent event = manager.createEvent(eventType, eventName);
059    
060                    //log relevant request data and parameters to the event.
061                    EventDataElement data = event.addData();
062                    data.add("uri", eventName);
063                    data.add("protocol", req.getProtocol());
064                    data.add("method", req.getMethod());
065                    data.add("remote-address", req.getRemoteAddr());
066                    data.add("remote-host", req.getRemoteHost());
067                    
068                    EventDataElement parameters = data.addElement("parameters");
069                    if (this.parameters != null) {
070                            //include only configured parameters
071                            for (String name : this.parameters) {
072                                    String[] values = request.getParameterValues(name);
073                                    if (values != null)
074                                            addParameter(parameters, name, values);
075                            }
076                    } else {
077                            //include all parameters
078                            for (Enumeration<?> params = request.getParameterNames(); params.hasMoreElements(); ) {
079                                    String name = (String)params.nextElement();
080                                    String[] values = request.getParameterValues(name);
081                                    addParameter(parameters, name, values);
082                            }
083                    }
084                    
085                    manager.start(event);
086                    return event;
087            }
088            
089            public void stop(BehaviorEvent event) {
090                    if (event != null) //event may be null if match() returned false at the start of the request.
091                            manager.stop(event);
092            }
093            
094            protected boolean match(HttpServletRequest request) {
095                    if (uriPatterns == null)
096                            return true;
097                    for (Pattern p : uriPatterns)
098                            if (p.matcher(request.getRequestURI()).matches())
099                                    return true;
100                    return false;
101            }
102            
103            private static final void addParameter(EventDataElement parameters, String name, String[] values) {
104                    EventDataElement param = parameters.addElement("param");
105                    param.add("name", name);
106                    for (String v : values)
107                            param.addElement("value").setText(v);
108            }
109            
110    }