/*
* HistoryFilter.java
*
* Brazil project web application toolkit,
* export version: 2.3
* Copyright (c) 2000-2004 Sun Microsystems, Inc.
*
* Sun Public License Notice
*
* The contents of this file are subject to the Sun Public License Version
* 1.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is included as the file "license.terms",
* and also available at http://www.sun.com/
*
* The Original Code is from:
* Brazil project web application toolkit release 2.3.
* The Initial Developer of the Original Code is: cstevens.
* Portions created by cstevens are Copyright (C) Sun Microsystems, Inc.
* All Rights Reserved.
*
* Contributor(s): cstevens, suhler.
*
* Version: 2.2
* Created by cstevens on 00/04/12
* Last modified by suhler on 04/11/30 15:19:39
*
* Version Histories:
*
* 2.2 04/11/30-15:19:39 (suhler)
* fixed sccs version string
*
* 2.1 02/10/01-16:37:12 (suhler)
* version change
*
* 1.6 00/12/11-13:31:35 (suhler)
* add class=props for automatic property extraction
*
* 1.5 00/05/22-14:06:42 (suhler)
* doc updates
*
* 1.4 00/05/01-14:54:03 (suhler)
* fix doc bug
*
* 1.3 00/04/24-13:02:50 (cstevens)
* doc
*
* 1.2 00/04/12-15:58:52 (cstevens)
* imports
*
* 1.2 00/04/12-11:26:13 (Codemgr)
* SunPro Code Manager data about conflicts, renames, etc...
* Name history : 1 0 proxy/HistoryFilter.java
*
* 1.1 00/04/12-11:26:12 (cstevens)
* date and time created 00/04/12 11:26:12 by cstevens
*
*/
package sunlabs.brazil.proxy;
import sunlabs.brazil.filter.Filter;
import sunlabs.brazil.server.Request;
import sunlabs.brazil.server.Server;
import sunlabs.brazil.session.SessionManager;
import sunlabs.brazil.util.http.HttpUtil;
import sunlabs.brazil.util.http.MimeHeaders;
import sunlabs.brazil.util.regexp.Regexp;
import java.io.Serializable;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.Properties;
/**
* The HistoryFilter
is both a Handler
and a
* Filter
that keeps a record of all pages visited by a
* given session.
*
* The HistoryFilter
can be used to
* make a user's session "mobile" as follows: A user's history
* is normally stored with the browser being used, on the user's machine.
* If the user runs a different browser or goes to a different machine, the
* user's history will not be there. Instead, the user can access the web
* via a proxy that keeps track of their history. No matter which browser
* the user chooses or machine the user is at, a server running with the
* HistoryFilter
will automatically remember and be able to
* present the user's history.
*
* The history is kept with respect to a Session ID. *
* This filter uses the following configuration properties:
prefix
* session
* nosession
* admin
* HistoryFilter
* to store the history information for the current Session in the
* request properties
*
* filter
* Regexp
pattern to match
* against the "Content-Type" of the result. Setting this also implies
* that the HistoryFilter
will be invoked as a
* Filter
and not a Handler
. The default
* value is "", which indicates that the "Content-Type" is not
* examined and that this HistoryFilter
will be invoked
* as a Handler
.
*
* It is an error if the filter
is specified but malformed.
*
* @param server
* The HTTP server.
*
* @param prefix
* The configuration property prefix.
*
* @return true
if this filter initialized successfully,
* false
otherwise.
*/
public boolean
init(Server server, String prefix)
{
this.prefix = prefix;
Properties props = server.props;
urlPrefix = props.getProperty(prefix + URL_PREFIX, urlPrefix);
session = props.getProperty(prefix + SESSION, session);
nosession = props.getProperty(prefix + NOSESSION, nosession);
admin = props.getProperty(prefix + ADMIN, admin);
if (admin.length() == 0) {
admin = null;
}
try {
String tmp = props.getProperty(prefix + FILTER, "");
if (tmp.length() > 0) {
filter = new Regexp(tmp);
}
} catch (IllegalArgumentException e) {
server.log(Server.LOG_DIAGNOSTIC, prefix, e.getMessage());
return false;
}
return true;
}
/**
* If the admin
prefix is seen, store the history
* information associated with the session in the request properties.
*
* If invoked as a Handler
and the URL matches the
* prefix
, records this page's address in the history.
*
* @param request
* The Request
object that represents the HTTP
* request.
*
* @return false
, indicating that this respond
* method ran purely for its side effects.
*/
public boolean
respond(Request request)
{
if ((admin != null) && request.url.startsWith(admin)) {
StringBuffer sb = new StringBuffer();
Properties props = request.props;
Hashtable pages = getPages(request);
Enumeration keys = pages.keys();
for (int i = 0; keys.hasMoreElements(); i++) {
String url = (String) keys.nextElement();
PageInfo pi = (PageInfo) pages.get(url);
sb.append(i).append(' ');
String pfx = prefix + i;
props.put(pfx + ".url", pi.url);
props.put(pfx + ".first", HttpUtil.formatTime(pi.first));
props.put(pfx + ".last", HttpUtil.formatTime(pi.last));
props.put(pfx + ".count", Integer.toString(pi.count));
}
props.put(prefix + "pages", sb.toString());
} else if ((filter == null) && request.url.startsWith(urlPrefix)) {
/*
* If no Content-Type filter is present, it means that the
* HistoryFilter is being used as a Handler.
*/
recordUrl(request);
}
return false;
}
/**
* Called when invoked as a Filter
. If the URL matches the
* prefix
and the returned "Content-Type" matches the
* filter
, records this page's address in the history.
*
* @param request
* The in-progress HTTP request.
*
* @param headers
* The MIME headers from the result.
*
* @return false
indicating that this Filter
* does not want to modify the content.
*/
public boolean
shouldFilter(Request request, MimeHeaders headers)
{
try {
String type = headers.get("Content-Type");
if (request.url.startsWith(urlPrefix)
&& (filter.match(type) != null)) {
recordUrl(request);
}
} catch (Exception e) {
/* Ignore:
* No content-type
* Missing/malformed filter.
*/
}
return false;
}
/**
* Returns the original content, since this filter does not change
* content. Won't actually be invoked.
*/
public byte[]
filter(Request request, MimeHeaders headers, byte[] content)
{
return content;
}
private void
recordUrl(Request request)
{
Hashtable pages = getPages(request);
PageInfo pi = (PageInfo) pages.get(request.url);
if (pi == null) {
pi = new PageInfo(request.url);
pages.put(request.url, pi);
}
pi.count++;
pi.last = System.currentTimeMillis();
}
private Hashtable
getPages(Request request)
{
String id = request.props.getProperty(session, nosession);
return (Hashtable) SessionManager.getSession(id,
HistoryFilter.class, Hashtable.class);
}
/**
* Keep information about a visited URL
*/
static class PageInfo
implements Serializable
{
String url;
int count;
long first;
long last;
public
PageInfo(String url)
{
this.url = url;
first = System.currentTimeMillis();
}
}
}