/** * Copyright 2008 The University of North Carolina at Chapel Hill * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package edu.unc.lib.dl.ui.util; import java.net.URI; import java.net.URISyntaxException; import java.util.ArrayList; import java.util.List; import javax.annotation.PreDestroy; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.apache.http.NameValuePair; import org.apache.http.client.config.RequestConfig; import org.apache.http.client.methods.CloseableHttpResponse; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.utils.URIBuilder; import org.apache.http.conn.HttpClientConnectionManager; import org.apache.http.impl.client.CloseableHttpClient; import org.apache.http.impl.client.HttpClients; import org.apache.http.impl.conn.PoolingHttpClientConnectionManager; import org.apache.http.message.BasicNameValuePair; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Utility for performing asynchronous analytics tracking events when unable to use the javascript api * * @author bbpennel * @date Apr 21, 2014 */ public class AnalyticsTrackerUtil { private static final Logger log = LoggerFactory.getLogger(AnalyticsTrackerUtil.class); // Made up CID to use if the request does not include one, such as from a API request private static final String DEFAULT_CID = "35009a79-1a05-49d7-b876-2b884d0f825b"; // Google analytics measurement API url private final String GA_URL = "https://www.google-analytics.com/collect"; // Google analytics tracking id private String gaTrackingID; private final HttpClientConnectionManager httpManager; private final CloseableHttpClient httpClient; public AnalyticsTrackerUtil() { // Use a threaded manager with timeouts httpManager = new PoolingHttpClientConnectionManager(); RequestConfig requestConfig = RequestConfig.custom() .setConnectTimeout(2000) .build(); httpClient = HttpClients.custom() .setConnectionManager(httpManager) .setDefaultRequestConfig(requestConfig) .build(); } @PreDestroy public void destroy() { httpManager.shutdown(); } public void setGaTrackingID(String trackingID) { this.gaTrackingID = trackingID; } public void trackEvent(AnalyticsUserData userData, String category, String action, String label, Integer value) { // Use a default customer ID if none was provided, since it is required if (userData == null) return; // Perform the analytics tracking event asynchronously Thread trackerThread = new Thread(new EventTrackerRunnable(userData, category, action, label, value)); trackerThread.start(); } public static class AnalyticsUserData { public String uip; public String cid; public AnalyticsUserData(HttpServletRequest request) { // Get the user's IP address, either from proxy headers or request uip = request.getHeader("X-Forwarded-For"); if (uip == null || uip.length() == 0 || "unknown".equalsIgnoreCase(uip)) { uip = request.getHeader("Proxy-Client-IP"); } if (uip == null || uip.length() == 0 || "unknown".equalsIgnoreCase(uip)) { uip = request.getHeader("WL-Proxy-Client-IP"); } if (uip == null || uip.length() == 0 || "unknown".equalsIgnoreCase(uip)) { uip = request.getHeader("HTTP_CLIENT_IP"); } if (uip == null || uip.length() == 0 || "unknown".equalsIgnoreCase(uip)) { uip = request.getHeader("HTTP_X_FORWARDED_FOR"); } if (uip == null || uip.length() == 0 || "unknown".equalsIgnoreCase(uip)) { uip = request.getRemoteAddr(); } // Store the CID from _ga cookie if it is present Cookie cookies[] = request.getCookies(); if (cookies != null) { for (Cookie cookie : cookies) { if ("_ga".equals(cookie.getName())) { String[] parts = cookie.getValue().split("\\."); if (parts.length == 4) { cid = parts[2] + "." + parts[3]; } break; } } } if (cid == null) { cid = DEFAULT_CID; } } } private class EventTrackerRunnable implements Runnable { private final AnalyticsUserData userData; private final String category; private final String action; private final String label; private final Integer value; public EventTrackerRunnable(AnalyticsUserData userData, String category, String action, String label, Integer value) { this.category = category; this.action = action; this.label = label; this.value = value; this.userData = userData; } @Override public void run() { if (log.isDebugEnabled()) log.debug("Tracking user {} with event {} in category {} with label {}", new String[] { userData.cid, action, category, label }); URIBuilder builder; try { builder = new URIBuilder(GA_URL); } catch (URISyntaxException e) { log.warn("Failed to build URI for tracker", e); return; } List<NameValuePair> params = new ArrayList<NameValuePair>(); params.add(new BasicNameValuePair("v", "1")); params.add(new BasicNameValuePair("tid", gaTrackingID)); params.add(new BasicNameValuePair("cid", userData.cid)); params.add(new BasicNameValuePair("t", "event")); params.add(new BasicNameValuePair("uip", userData.uip)); params.add(new BasicNameValuePair("an", "cdr")); params.add(new BasicNameValuePair("de", "UTF-8")); params.add(new BasicNameValuePair("ul", "en-us")); log.debug("Tracking user {} with event {} in category {} with label {}", new String[] { userData.cid, action, category, label }); log.debug("Tracking:{} {} {} {}", new Object[] { GA_URL, gaTrackingID, userData.cid, userData.uip}); if (category != null) { params.add(new BasicNameValuePair("ec", category)); } if (action != null) { params.add(new BasicNameValuePair("ea", action)); } if (label != null) { params.add(new BasicNameValuePair("el", label)); } if (value != null) { params.add(new BasicNameValuePair("ev", value.toString())); } builder.addParameters(params); HttpGet method; try { URI url = builder.build(); method = new HttpGet(url); method.addHeader("Accept", "*/*"); } catch (URISyntaxException e) { log.warn("Failed to build tracking url", e); return; } try (CloseableHttpResponse resp = httpClient.execute(method)) { } catch (Exception e) { log.warn("Failed to issue tracking event for cid {}", e, userData.cid); } } } }