/** * The contents of this file are subject to the license and copyright * detailed in the LICENSE and NOTICE files at the root of the source * tree and available online at * * http://www.dspace.org/license/ */ package org.dspace.app.xmlui.aspect.administrative; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Queue; import javax.servlet.http.HttpSession; import org.apache.avalon.framework.parameters.Parameters; import org.apache.cocoon.acting.AbstractAction; import org.apache.cocoon.environment.ObjectModelHelper; import org.apache.cocoon.environment.Redirector; import org.apache.cocoon.environment.Request; import org.apache.cocoon.environment.SourceResolver; import org.dspace.app.xmlui.utils.ContextUtil; import org.dspace.core.ConfigurationManager; import org.dspace.core.Context; import org.dspace.eperson.EPerson; /** * * This action simply records pipeline events that it sees, keeping track of the users and * pages they are viewing. Later the control panel's activity viewer can access this data to * get a realtime snap shot of current activity of the repository. * * @author Scott Phillips */ public class CurrentActivityAction extends AbstractAction { /** The maximum number of events that are recorded */ public static final int MAX_EVENTS; /** The HTTP header that contains the real IP for this request, this is used when DSpace is placed behind a load balancer */ public static final String IP_HEADER; /** The ordered list of events, by access time */ private static Queue<Event> events = new LinkedList<Event>(); /** record events that are from anynmous users */ private static boolean recordAnonymousEvents = true; /** record events from automatic bots */ private static boolean recordBotEvents = false; /** * Allow the DSpace.cfg to override our activity max and ip header. */ static { // If the dspace.cfg has a max event count then use it. if (ConfigurationManager.getProperty("xmlui.controlpanel.activity.max") != null) { MAX_EVENTS = ConfigurationManager.getIntProperty("xmlui.controlpanel.activity.max"); } else { MAX_EVENTS = 250; } if (ConfigurationManager.getProperty("xmlui.controlpanel.activity.ipheader") != null) { IP_HEADER = ConfigurationManager.getProperty("xmlui.controlpanel.activity.ipheader"); } else { IP_HEADER = "X-Forwarded-For"; } } /** * Record this current event. */ public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception { Request request = ObjectModelHelper.getRequest(objectModel); Context context = ContextUtil.obtainContext(objectModel); // Ensure only one thread is manipulating the events queue at a time. synchronized (events) { // Create and store our events Event event = new Event(context,request); // Check if we should record the event boolean record = true; if (!recordAnonymousEvents && event.isAnonymous()) { record = false; } if (!recordBotEvents && event.isBot()) { record = false; } if (record) { events.add(event); } // Remove the oldest element from the list if we are over our max // number of elements. while (events.size() > MAX_EVENTS) { events.poll(); } } return null; } /** * @return a list of all current events. */ public static List<Event> getEvents() { List<Event> list = new ArrayList<Event>(); // Make sure only one thread is manipulating the events queue at a time. synchronized (events) { list.addAll(events); } return list; } public static boolean getRecordAnonymousEvents() { return recordAnonymousEvents; } public static void setRecordAnonymousEvents(boolean record) { recordAnonymousEvents = record; } public static boolean getRecordBotEvents() { return recordBotEvents; } public static void setRecordBotEvents(boolean record) { recordBotEvents = record; } /** * An object that represents an activity event. */ public static class Event { /** The Servlet session */ private String sessionID; /** The eperson ID */ private int epersonID = -1; /** The url being viewed */ private String url; /** A timestap of this event */ private long timestamp; /** The user-agent for the request */ private String userAgent; /** The ip address of the requester */ private String ip; /** * Construct a new activity event, grabing various bits of data about the request from the context and request. */ public Event(Context context, Request request) { if (context != null) { EPerson eperson = context.getCurrentUser(); if (eperson != null) { epersonID = eperson.getID(); } } if (request != null) { url = request.getSitemapURI(); HttpSession session = request.getSession(true); if (session != null) { sessionID = session.getId(); } userAgent = request.getHeader("User-Agent"); ip = request.getHeader(IP_HEADER); if (ip == null) { ip = request.getRemoteAddr(); } } // The current time timestamp = System.currentTimeMillis(); } public String getSessionID() { return sessionID; } public int getEPersonID() { return epersonID; } public String getURL() { return url; } public long getTimeStamp() { return timestamp; } public String getUserAgent() { return userAgent; } public String getIP() { return ip; } /** * Is this event anonymous? * @return */ public boolean isAnonymous() { return (epersonID == -1); } /** * Is this event from a bot? * @return */ public boolean isBot() { if (userAgent == null) { return false; } String ua = userAgent.toLowerCase(); return (ua.contains("google/") || ua.contains("msnbot/") || ua.contains("googlebot/") || ua.contains("webcrawler/") || ua.contains("inktomi") || ua.contains("teoma") || ua.contains("bot")); } /** * Return the activity viewer's best guess as to what browser or bot was initiating the request. * * @return A short name for the browser or bot. */ public String getDectectedBrowser() { if (userAgent == null) { return "No browser provided"; } String userAgentLower = userAgent.toLowerCase(); // BOTS if (userAgentLower.contains("google/")) { return "Google (bot)"; } if (userAgentLower.contains("msnbot/")) { return "MSN (bot)"; } if (userAgentLower.contains("googlebot/")) { return "Google (bot)"; } if (userAgentLower.contains("webcrawler/")) { return "WebCrawler (bot)"; } if (userAgentLower.contains("inktomi")) { return "Inktomi (bot)"; } if (userAgentLower.contains("teoma")) { return "Teoma (bot)"; } if (userAgentLower.contains("bot")) { return "Unknown (bot)"; } // Normal Browsers if (userAgent.contains("Lotus-Notes/")) { return "Lotus-Notes"; } if (userAgent.contains("Opera")) { return "Opera"; } if (userAgent.contains("Safari/")) { if (userAgent.contains("Chrome")) { return "Chrome"; } return "Safari"; } if (userAgent.contains("Konqueror/")) { return "Konqueror"; } // Internet explorer browsers if (userAgent.contains("MSIE")) { if (userAgent.contains("MSIE 9")) { return "MSIE 9"; } if (userAgent.contains("MSIE 8")) { return "MSIE 8"; } if (userAgent.contains("MSIE 7")) { return "MSIE 7"; } if (userAgent.contains("MSIE 6")) { return "MSIE 6"; } if (userAgent.contains("MSIE 5")) { return "MSIE 5"; } // Can't fine the version number return "MSIE"; } // Gecko based browsers if (userAgent.contains("Gecko/")) { if (userAgent.contains("Camio/")) { return "Gecko/Camino"; } if (userAgent.contains("Chimera/")) { return "Gecko/Chimera"; } if (userAgent.contains("Firebird/")) { return "Gecko/Firebird"; } if (userAgent.contains("Phoenix/")) { return "Gecko/Phoenix"; } if (userAgent.contains("Galeon")) { return "Gecko/Galeon"; } if (userAgent.contains("Firefox/1")) { return "Firefox 1.x"; } if (userAgent.contains("Firefox/2")) { return "Firefox 2.x"; } if (userAgent.contains("Firefox/3")) { return "Firefox 3.x"; } if (userAgent.contains("Firefox/4")) { return "Firefox 4.x"; } if (userAgent.contains("Firefox/")) { return "Firefox"; // can't find the exact version } if (userAgent.contains("Netscape/")) { return "Netscape"; } // Can't find the exact distribution return "Gecko"; } // Generic browser types (lots of things report these names) if (userAgent.contains("KHTML/")) { return "KHTML"; } if (userAgent.contains("Netscape/")) { return "Netscape"; } if (userAgent.contains("Mozilla/")) { return "Mozilla"; // Almost everything says they are mozilla. } // if you get all the way to the end and still nothing, return unknown. return "Unknown"; } } }