/*
* CurrentActivityAction.java
*
* Version: $Revision: 3833 $
*
* Date: $Date: 2009-06-02 01:57:28 +0000 (Tue, 02 Jun 2009) $
*
* Copyright (c) 2002, Hewlett-Packard Company and Massachusetts
* Institute of Technology. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of the Hewlett-Packard Company nor the name of the
* Massachusetts Institute of Technology nor the names of their
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
package org.dspace.app.xmlui.aspect.administrative;
import java.util.ArrayList;
import java.util.Enumeration;
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.Session;
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 int MAX_EVENTS = 250;
/** The HTTP header that contains the real IP for this request, this is used when DSpace is placed behind a load balancer */
public static String IP_HEADER = "X-Forwarded-For";
/** 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");
if (ConfigurationManager.getProperty("xmlui.controlpanel.activity.ipheader") != null)
IP_HEADER = ConfigurationManager.getProperty("xmlui.controlpanel.activity.ipheader");
}
/**
* 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()
{
if (epersonID == -1)
return true;
else
return false;
}
/**
* Is this event from a bot?
* @return
*/
public boolean isBot()
{
if (userAgent == null)
return false;
String ua = userAgent.toLowerCase();
if (ua.contains("google/") ||
ua.contains("msnbot/") ||
ua.contains("googlebot/") ||
ua.contains("webcrawler/") ||
ua.contains("inktomi") ||
ua.contains("teoma") ||
ua.contains("bot"))
return true;
else
return false;
}
/**
* 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";
// BOTS
if (userAgent.toLowerCase().contains("google/"))
return "Google (bot)";
if (userAgent.toLowerCase().contains("msnbot/"))
return "MSN (bot)";
if (userAgent.toLowerCase().contains("googlebot/"))
return "Google (bot)";
if (userAgent.toLowerCase().contains("webcrawler/"))
return "WebCrawler (bot)";
if (userAgent.toLowerCase().contains("inktomi"))
return "Inktomi (bot)";
if (userAgent.toLowerCase().contains("teoma"))
return "Teoma (bot)";
if (userAgent.toLowerCase().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";
}
}
}