/* * Copyright 2007 Zhang, Zheng <oldbig@gmail.com> * * This file is part of ZOJ. * * ZOJ is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either revision 3 of the License, or (at your option) any later revision. * * ZOJ is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along with ZOJ. if not, see * <http://www.gnu.org/licenses/>. */ package cn.edu.zju.acm.onlinejudge.util; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.sql.Types; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import javax.servlet.http.HttpServletRequest; import org.apache.struts.action.Action; import cn.edu.zju.acm.onlinejudge.bean.UserProfile; import cn.edu.zju.acm.onlinejudge.bean.request.LogCriteria; import cn.edu.zju.acm.onlinejudge.persistence.PersistenceException; import cn.edu.zju.acm.onlinejudge.persistence.sql.Database; public class PerformanceManager { private static final String ACTION_PACKAGE = "cn.edu.zju.acm.onlinejudge.action."; private static final int TIME_LIMIT = 60000; private static final int SIZE_LIMIT = 50; private static PerformanceManager instance = null; private List<AccessLog> finished = new ArrayList<AccessLog>(); private Map<Long, AccessLog> waiting = new HashMap<Long, AccessLog>(); private long id = 0; private long lastSave = 0; private Runner runner = new Runner(); public PerformanceManager() { runner.start(); } public synchronized long actionStart(Action action, HttpServletRequest request, UserProfile user) { String actionName = action.getClass().getName(); if (actionName.startsWith(ACTION_PACKAGE)) { actionName = actionName.substring(ACTION_PACKAGE.length()); } AccessLog log = new AccessLog(); log.setTimestamp(new Date()); String url = request.getServletPath() + (request.getQueryString() == null ? "" : "?" + request.getQueryString()) + " " + request.getMethod(); log.setUrl(url); log.setAction(actionName); if (user != null) { log.setUserId(user.getId()); log.setHandle(user.getHandle()); } log.setIp(request.getRemoteHost()); id++; waiting.put(id, log); return id; } public synchronized void actionEnd(long id) { AccessLog log = waiting.get(id); if (log == null) { return; } log.setAccessTime(System.currentTimeMillis() - log.getTimestamp().getTime()); waiting.remove(id); addAccessLog(log); } public synchronized void addAccessLog(AccessLog log) { finished.add(log); if (finished.size() >= SIZE_LIMIT) { saveFinished(); } } public synchronized void saveFinished() { lastSave = System.currentTimeMillis(); if (finished.size() == 0) { return; } try { saveAccessLog(finished); } catch (PersistenceException e) { e.printStackTrace(); } // TODO save; finished.clear(); } public synchronized void saveAccessLog(List<AccessLog> logs) throws PersistenceException { Connection conn = null; PreparedStatement ps = null; try { conn = Database.createConnection(); ps = conn.prepareStatement("INSERT INTO access_log (user_profile_id, handle, action, url, ip, timestamp, access_time) " + "VALUES(?,?,?,?,?,?,?)"); for (AccessLog log : logs) { if (log.getHandle() == null) { ps.setNull(1, Types.BIGINT); ps.setNull(2, Types.VARCHAR); } else { ps.setLong(1, log.getUserId()); ps.setString(2, log.getHandle()); } ps.setString(3, log.getAction()); ps.setString(4, log.getUrl()); ps.setString(5, log.getIp()); ps.setTimestamp(6, new Timestamp(log.getTimestamp().getTime())); ps.setLong(7, log.getAccessTime()); ps.addBatch(); } ps.executeBatch(); } catch (SQLException e) { throw new PersistenceException("Failed to save logs.", e); } finally { Database.dispose(ps); Database.dispose(conn); } } public PreparedStatement buildQuery(LogCriteria criteria, String orderBy, Connection conn) throws SQLException { StringBuilder sb = new StringBuilder(); if ("action".equals(orderBy)) { orderBy = "action"; } else if ("count".equals(orderBy)) { orderBy = "count DESC"; } else if ("max".equals(orderBy)) { orderBy = "max DESC"; } else if ("min".equals(orderBy)) { orderBy = "min ASC"; } else { orderBy = "avg DESC"; } if (criteria.getTimeStart() != null) { sb.append(" AND timestamp>=?"); } if (criteria.getTimeEnd() != null) { sb.append(" AND timestamp<=?"); } if (criteria.getIp() != null && criteria.getIp().trim().length() > 0) { sb.append(" AND ip='" + criteria.getIp() + "'"); } if (criteria.getUserId() != null) { sb.append(" AND user_profile_id=" + criteria.getUserId()); } if (criteria.getHandle() != null && criteria.getHandle().trim().length() > 0) { sb.append(" AND handle='" + criteria.getHandle() + "'"); } String where = sb.length() == 0 ? "" : "WHERE " + sb.toString().substring(4); String sql = "SELECT action, count(*) count, max(access_time) max, min(access_time) min, avg(access_time) avg " + "FROM access_log " + where + " GROUP BY action ORDER BY " + orderBy; PreparedStatement ps = conn.prepareStatement(sql); int index = 1; if (criteria.getTimeStart() != null) { ps.setTimestamp(index, new Timestamp(criteria.getTimeStart().getTime())); index++; } if (criteria.getTimeEnd() != null) { ps.setTimestamp(index, new Timestamp(criteria.getTimeEnd().getTime())); index++; } return ps; } public PreparedStatement buildQuery(LogCriteria criteria, int offset, int count, String orderBy, Connection conn) throws SQLException { StringBuilder sb = new StringBuilder(); if ("accessTimeDesc".equals(orderBy)) { orderBy = "access_time DESC, timestamp DESC"; } else if ("accessTimeAsc".equals(orderBy)) { orderBy = "access_time ASC, timestamp DESC"; } else if ("timestampAsc".equals(orderBy)) { orderBy = "timestamp ASC"; } else { orderBy = "timestamp DESC"; } if (criteria.getTimeStart() != null) { sb.append(" AND timestamp>=?"); } if (criteria.getTimeEnd() != null) { sb.append(" AND timestamp<=?"); } if (criteria.getAction() != null && criteria.getAction().trim().length() > 0) { sb.append(" AND action='" + criteria.getAction() + "'"); } if (criteria.getIp() != null && criteria.getIp().trim().length() > 0) { sb.append(" AND ip='" + criteria.getIp() + "'"); } if (criteria.getUserId() != null) { sb.append(" AND user_profile_id=" + criteria.getUserId()); } if (criteria.getHandle() != null && criteria.getHandle().trim().length() > 0) { sb.append(" AND handle='" + criteria.getHandle() + "'"); } String where = sb.length() == 0 ? "" : "WHERE " + sb.toString().substring(4); String sql = "SELECT * FROM access_log " + where + " ORDER BY " + orderBy + " LIMIT " + offset + "," + count; PreparedStatement ps = conn.prepareStatement(sql); int index = 1; if (criteria.getTimeStart() != null) { ps.setTimestamp(index, new Timestamp(criteria.getTimeStart().getTime())); index++; } if (criteria.getTimeEnd() != null) { ps.setTimestamp(index, new Timestamp(criteria.getTimeEnd().getTime())); index++; } return ps; } public synchronized List<ActionLog> getActionDashboard(LogCriteria criteria, String orderBy) throws PersistenceException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = Database.createConnection(); ps = buildQuery(criteria, orderBy, conn); rs = ps.executeQuery(); List<ActionLog> logs = new ArrayList<ActionLog>(); while (rs.next()) { ActionLog log = new ActionLog(); log.setAction(rs.getString("action")); log.setAvgAccessTime(rs.getLong("avg")); log.setMinAccessTime(rs.getLong("min")); log.setMaxAccessTime(rs.getLong("max")); log.setCount(rs.getLong("count")); logs.add(log); } return logs; } catch (SQLException e) { throw new PersistenceException("Failed to search logs.", e); } finally { Database.dispose(ps); Database.dispose(conn); } } public synchronized List<AccessLog> searchLogs(LogCriteria criteria, int offset, int count, String orderBy) throws PersistenceException { Connection conn = null; PreparedStatement ps = null; ResultSet rs = null; try { conn = Database.createConnection(); ps = buildQuery(criteria, offset, count, orderBy, conn); rs = ps.executeQuery(); List<AccessLog> logs = new ArrayList<AccessLog>(); while (rs.next()) { AccessLog log = new AccessLog(); log.setId(rs.getLong("access_log_id")); if (rs.getObject("user_profile_id") != null) { log.setUserId(rs.getLong("user_profile_id")); } if (rs.getObject("handle") != null) { log.setHandle(rs.getString("handle")); } log.setUrl(rs.getString("url")); log.setAction(rs.getString("action")); log.setIp(rs.getString("ip")); log.setTimestamp(new Date(rs.getTimestamp("timestamp").getTime())); log.setAccessTime(rs.getLong("access_time")); logs.add(log); } return logs; } catch (SQLException e) { throw new PersistenceException("Failed to search logs.", e); } finally { Database.dispose(ps); Database.dispose(conn); } } public static PerformanceManager getInstance() { if (instance == null) { synchronized (StatisticsManager.class) { if (instance == null) { instance = new PerformanceManager(); } } } return instance; } private class Runner extends Thread { public void run() { for (;;) { try { if (System.currentTimeMillis() - lastSave > TIME_LIMIT - 1000) { saveFinished(); } } catch (Exception e) { e.printStackTrace(); } try { Thread.sleep(TIME_LIMIT); } catch (Exception e) { e.printStackTrace(); } } } } }