package org.alleypress.servlet.filter; import java.io.IOException; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.HashMap; import java.util.Iterator; import java.util.Map; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; /** * Collect info on which browsers are accessing your application and record it to a * database. You will need to initialze the following parameters: *
 		<init-param>
			<param-name>driver</param-name>
			<param-value>org.gjt.mm.mysql.Driver</param-value>
		</init-param>
 		<init-param>
			<param-name>url</param-name>
			<param-value>jdbc:mysql://localhost:3306/stats</param-value>
		</init-param>
 		<init-param>
			<param-name>username</param-name>
			<param-value>user</param-value>
		</init-param>
 		<init-param>
			<param-name>password</param-name>
			<param-value>pass</param-value>
		</init-param>
	
* * Right now this is really oriented towards a MySql database, but it could * enhanced to be confiurable for any database by adding some parameters. * It doesn't use a connection pool becuase the writes should be fairly infrequent * Here's the table that needs to be setup: *
  
  CREATE TABLE browser (
  agent varchar(255) NOT NULL default '',
  total int(10) unsigned NOT NULL default '0',
  last_update timestamp(14) NOT NULL,
  app_id int(10) unsigned NOT NULL default '0',
  PRIMARY KEY  (`agent`)
  ) TYPE=InnoDB'
  
* * @author Tom Crofton * @version 1.3 * @email croftontom@yahoo.com * @created Jan 27, 2006 * @last Feb 3, 2006 * */ public class BrowserInfo implements Filter { private class MyInt { private int val=1; public void incr() {val++;} public Integer getInt() {return new Integer(val);} public int getVal() {return val;}; } private static final int UPDATE_TIME=1000*60*15; //15 minutes private static final int UPDATE_COUNT=100; //100 requests private Map agents = new HashMap(); private String driverClass = null; private String connectUrl = null; private String username = null; private String password = null; private int appId = 0; private long updateTime = 0; private int updateCount = 0; /* * Initialize the driver,url,username, and password params. Also try connecting to * the database to make sure the settings are good. * * @see javax.servlet.Filter#init(javax.servlet.FilterConfig) * */ public void init(FilterConfig config) throws ServletException { //grab the parameters driverClass=config.getInitParameter("driver"); connectUrl=config.getInitParameter("url"); username=config.getInitParameter("username"); password=config.getInitParameter("password"); if (connectUrl==null) throw new ServletException("parameter 'url' not specified"); if (username==null) throw new ServletException("parameter 'username' not specified"); if (password==null) throw new ServletException("parameter 'password' not specified"); try { appId=Integer.parseInt(config.getInitParameter("appId")); } catch (Exception e) { throw new ServletException("parameter 'appId' is missing or invalid"); } if (driverClass==null) throw new ServletException("parameter 'driver' not specified"); try { Class.forName(driverClass); } catch (ClassNotFoundException e) { throw new ServletException("could not load db driver "+driverClass); } //establish a database connection or throw exception try { Connection conn = DriverManager.getConnection(connectUrl, username, password); ResultSet rs = conn.createStatement().executeQuery("select count(*) from browser"); rs.next(); conn.close(); } catch (SQLException e) { throw new ServletException(e); } } /* * Takes the User-Agent header from the request and maintiains a Map of agents and * counts. After the buffer is full or enough time has passed, on the next request * the data is written out to the database. * * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain) */ public void doFilter(ServletRequest req, ServletResponse resp, FilterChain chain) throws IOException, ServletException { HttpServletRequest request=(HttpServletRequest)req; String ag=request.getHeader("User-Agent"); if (ag!=null) { if (agents.containsKey(ag)) { ((MyInt)agents.get(ag)).incr(); } else { agents.put(ag,new MyInt()); } updateCount++; if (timeToUpdate()) { try { writeCounts(); resetCounts(); } catch (SQLException e) { throw new ServletException(e); } } } chain.doFilter(request,resp); } private void resetCounts() { updateCount=0; updateTime=System.currentTimeMillis(); agents.clear(); } private boolean timeToUpdate() { return (updateCount>UPDATE_COUNT || System.currentTimeMillis()>(updateTime+UPDATE_TIME) ); } /* (non-Javadoc) * @see javax.servlet.Filter#destroy() */ public void destroy() { try { writeCounts(); } catch (SQLException e) { //kinda forced to abandon this e.printStackTrace(); } } private synchronized void writeCounts() throws SQLException { if (agents.isEmpty()) return; //nothing to do Connection conn = DriverManager.getConnection(connectUrl, username, password); PreparedStatement psSelect= conn.prepareStatement("select total from browser where agent=? and app_id=?"); PreparedStatement psIns = conn.prepareStatement("insert into browser (agent,total,last_update,app_id) values (?,?,current_timestamp(),?)"); PreparedStatement psUpdate = conn.prepareStatement("update browser set total=?,last_update=current_timestamp() where agent=? and app_id=?"); psSelect.setInt(2,appId); psIns.setInt(3,appId); psUpdate.setInt(3,appId); Iterator it=agents.keySet().iterator(); while (it.hasNext()) { String agentName=(String)it.next(); psSelect.setString(1,agentName); ResultSet rs=psSelect.executeQuery(); if (rs.next()) { int ttl=rs.getInt("total"); ttl=ttl+ ((MyInt)agents.get(agentName)).getVal(); psUpdate.setInt(1,ttl); psUpdate.setString(2,agentName); psUpdate.executeUpdate(); } else { psIns.setString(1,agentName); psIns.setInt(2,((MyInt)agents.get(agentName)).getVal()); psIns.executeUpdate(); } } conn.close(); } }