001: /*
002: * (C) Copyright 2000 - 2003 Nabh Information Systems, Inc.
003: *
004: * This program is free software; you can redistribute it and/or
005: * modify it under the terms of the GNU General Public License
006: * as published by the Free Software Foundation; either version 2
007: * of the License, or (at your option) any later version.
008: *
009: * This program is distributed in the hope that it will be useful,
010: * but WITHOUT ANY WARRANTY; without even the implied warranty of
011: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
012: * GNU General Public License for more details.
013: *
014: * You should have received a copy of the GNU General Public License
015: * along with this program; if not, write to the Free Software
016: * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
017: *
018: */
019:
020: package com.nabhinc.portal.monitor;
021:
022: import java.io.IOException;
023: import java.util.HashMap;
024: import java.util.Timer;
025: import java.util.TimerTask;
026:
027: import javax.servlet.Filter;
028: import javax.servlet.FilterChain;
029: import javax.servlet.FilterConfig;
030: import javax.servlet.ServletException;
031: import javax.servlet.ServletRequest;
032: import javax.servlet.ServletResponse;
033: import javax.servlet.http.HttpServletRequest;
034:
035: import org.apache.commons.logging.Log;
036: import org.apache.commons.logging.LogFactory;
037:
038: import com.nabhinc.core.Defaults;
039: import com.nabhinc.util.StringUtil;
040: import com.nabhinc.util.db.DBUtil;
041:
042: /**
043: * This servlet filter intercepts HTTP requests and logs
044: * timestamp, request URI, referer, user agent, client name,
045: * client IP, user ID, and response time, and response code.
046: * Stringbeans provides
047: * portlets that generate reports and charts based on this data.
048: * statistics and alerts related to portal usage and performance.
049: * <p>
050: * The filter can be set up to send alerts in case response time
051: * exceeds user configured threshold value and/or response code
052: * indicates internal errors.
053: *
054: * @author Padmanabh Dabke
055: * (c) 2004 Nabh Information Systems, Inc. All Rights Reserved.
056: *
057: */
058: public class RequestMonitor extends TimerTask implements Filter {
059:
060: private String[] rmIgnoredIP = new String[0];
061: private String[] rmIgnoredUserAgents = new String[0];
062: private HashMap rmVisitorMap = new HashMap();
063: private Timer rmTimer = new Timer();
064: private long rmResetPeriod = 0;
065: private boolean rmMonitorUniqueSession = false;
066: private String rmInsertSQL = "INSERT INTO SB_MONITOR_LOGS (logtime, sessionid,remotehost,remoteaddress,useragent,referer,requesturl,browser,os) VALUES (?,?,?,?,?,?,?,?,?)";
067: private String rmUpdateCounterSQL = "UPDATE SB_MONITOR_COUNTER SET moncount=moncount+1 WHERE (montype=? AND monvar= ?) OR (montype=? AND monvar=?)";
068: private Log rmLogger = null;
069:
070: /* (non-Javadoc)
071: * @see javax.servlet.Filter#init(javax.servlet.FilterConfig)
072: */
073: public void init(FilterConfig config) throws ServletException {
074:
075: rmLogger = LogFactory.getLog(this .getClass());
076:
077: String ignoreIPs = config.getInitParameter("ignoreIPs");
078: if (ignoreIPs != null) {
079: rmIgnoredIP = StringUtil.split(ignoreIPs, ",");
080: }
081: String ignoreUAs = config.getInitParameter("ignoreUserAgents");
082: if (ignoreUAs != null) {
083: rmIgnoredUserAgents = StringUtil.split(ignoreUAs, ",");
084: }
085:
086: String monitorUniqueSession = config
087: .getInitParameter("monitorUniqueSession");
088: if ("true".equals(monitorUniqueSession))
089: rmMonitorUniqueSession = true;
090:
091: String cachedIPResetPeriod = config
092: .getInitParameter("cachedIPResetPeriod");
093:
094: if (cachedIPResetPeriod != null) {
095: long resetMinutes = Long.parseLong(cachedIPResetPeriod);
096: if (resetMinutes > 0)
097: rmResetPeriod = resetMinutes * 60 * 1000L;
098: }
099:
100: if (rmResetPeriod > 0)
101: rmTimer.scheduleAtFixedRate(this , rmResetPeriod,
102: rmResetPeriod);
103: else
104: rmTimer.cancel();
105:
106: String insertSql = config
107: .getInitParameter("insertMonitorLogSQL");
108: if (insertSql != null) {
109: rmInsertSQL = insertSql;
110: }
111:
112: String updateSql = config
113: .getInitParameter("updateMonitorCounterSQL");
114: if (updateSql != null) {
115: rmUpdateCounterSQL = updateSql;
116: }
117: }
118:
119: /* (non-Javadoc)
120: * @see javax.servlet.Filter#doFilter(javax.servlet.ServletRequest, javax.servlet.ServletResponse, javax.servlet.FilterChain)
121: */
122: public void doFilter(ServletRequest rq, ServletResponse res,
123: FilterChain fChain) throws IOException, ServletException {
124:
125: HttpServletRequest req = (HttpServletRequest) rq;
126: String clientIP = req.getRemoteAddr();
127:
128: if (rmResetPeriod > 0) {
129: boolean alreadyIn = true;
130: synchronized (this ) {
131: if (rmVisitorMap.get(clientIP) == null) {
132: rmVisitorMap.put(clientIP, "");
133: alreadyIn = false;
134: }
135: }
136: if (alreadyIn) {
137: fChain.doFilter(rq, res);
138: return;
139: }
140: }
141:
142: String sessionID = req.getSession().getId();
143: if (rmMonitorUniqueSession) {
144: String userSession = (String) req.getSession()
145: .getAttribute("com.nabhinc.session_monitor");
146: if (userSession == null)
147: req.getSession().setAttribute(
148: "com.nabhinc.session_monitor", "true");
149: else {
150: fChain.doFilter(rq, res);
151: return;
152: }
153: }
154:
155: String browserName = null;
156: String osName = null;
157: //String updateCounterSQL = "UPDATE SB_MONITOR_COUNTER SET moncount=moncount+1 WHERE (type=? AND var= ?) OR (type=? AND var=?)";
158:
159: StringBuffer sbReqURL = req.getRequestURL();
160: String reqURL = null;
161: String clientName = req.getRemoteHost();
162: //String userName = req.getRemoteUser();
163: //long lastModified = req.getDateHeader("If-Modified-Since");
164: String referer = req.getHeader("referer");
165:
166: if (referer != null) {
167: int index = referer.indexOf("?");
168: if (index > -1)
169: referer = referer.substring(0, index);
170:
171: if (referer.length() > 255)
172: referer = referer.substring(0, 255);
173: }
174:
175: if (sbReqURL != null) {
176: if (sbReqURL.length() > 255)
177: reqURL = sbReqURL.substring(0, 255);
178: else
179: reqURL = sbReqURL.toString();
180: }
181: String userAgent = req.getHeader("user-agent");
182:
183: // Check if we should ignore this user agent.
184: if (userAgent == null
185: || ignoreThisRequest(clientIP, userAgent = userAgent
186: .toLowerCase())) {
187: fChain.doFilter(rq, res);
188: return;
189: }
190:
191: if (userAgent.length() > 100)
192: userAgent = userAgent.substring(0, 100);
193:
194: try {
195: if (browserName == null) {
196: if (userAgent
197: .indexOf(UserAgentConstants.MSIE_BROWSER_TYPE) != -1)
198: browserName = UserAgentConstants.MSIE_BROWSER;
199: else if (userAgent
200: .indexOf(UserAgentConstants.LYNX_BROWSER_TYPE) != -1)
201: browserName = UserAgentConstants.LYNX_BROWSER;
202: else if (userAgent
203: .indexOf(UserAgentConstants.OPERA_BROWSER_TYPE) != -1)
204: browserName = UserAgentConstants.OPERA_BROWSER;
205: else if (userAgent
206: .indexOf(UserAgentConstants.WEBTV_BROWSER_TYPE) != -1)
207: browserName = UserAgentConstants.WEBTV_BROWSER;
208: else if (userAgent
209: .indexOf(UserAgentConstants.KONQUEROR_BROWSER_TYPE) != -1)
210: browserName = UserAgentConstants.KONQUEROR_BROWSER;
211: else if (userAgent
212: .indexOf(UserAgentConstants.FIREFOX_BROWSER_TYPE) != -1)
213: browserName = UserAgentConstants.FIREFOX_BROWSER;
214: else
215: for (int i = 0; i < UserAgentConstants.NETSCAPE_BROWSER_TYPES.length; i++)
216: if (userAgent
217: .indexOf(UserAgentConstants.NETSCAPE_BROWSER_TYPES[i]) != -1)
218: browserName = UserAgentConstants.NETSCAPE_BROWSER;
219:
220: }
221:
222: if (browserName == null) {
223: if (userAgent
224: .indexOf(UserAgentConstants.YAHOO_SEARCH_ENGINE_TYPE) != -1)
225: browserName = UserAgentConstants.YAHOO_SEARCH_ENGINE;
226: else if (userAgent
227: .indexOf(UserAgentConstants.ALTAVISTA_SEARCH_ENGINE_TYPE) != -1)
228: browserName = UserAgentConstants.ALTAVISTA_SEARCH_ENGINE;
229: else if (userAgent
230: .indexOf(UserAgentConstants.GOOGLE_SEARCH_ENGINE_TYPE) != -1)
231: browserName = UserAgentConstants.GOOGLE_SEARCH_ENGINE;
232: else if (userAgent
233: .indexOf(UserAgentConstants.LYCOS_SEARCH_ENGINE_TYPE) != -1)
234: browserName = UserAgentConstants.LYCOS_SEARCH_ENGINE;
235: else if (userAgent
236: .indexOf(UserAgentConstants.MSN_SEARCH_ENGINE_TYPE) != -1)
237: browserName = UserAgentConstants.MSN_SEARCH_ENGINE;
238: else
239: for (int i = 0; i < UserAgentConstants.SEARCH_ENGINES_TYPES.length; i++)
240: if (userAgent
241: .indexOf(UserAgentConstants.SEARCH_ENGINES_TYPES[i]) != -1)
242: browserName = UserAgentConstants.SEARCH_ENGINE;
243: }
244:
245: if (browserName == null)
246: browserName = UserAgentConstants.UNKNOWN_BROWSER;
247:
248: // OS
249: if (userAgent.indexOf(UserAgentConstants.WINDOWS_OS_TYPE) != -1)
250: osName = UserAgentConstants.WINDOWS_OS;
251: else if (userAgent
252: .indexOf(UserAgentConstants.LINUX_OS_TYPE) != -1)
253: osName = UserAgentConstants.LINUX_OS;
254: else if (userAgent
255: .indexOf(UserAgentConstants.FREEBSD_OS_TYPE) != -1)
256: osName = UserAgentConstants.FREEBSD_OS;
257: else if (userAgent.indexOf(UserAgentConstants.SUN_OS_TYPE) != -1)
258: osName = UserAgentConstants.SUN_OS;
259: else if (userAgent.indexOf(UserAgentConstants.IRIX_OS_TYPE) != -1)
260: osName = UserAgentConstants.IRIX_OS;
261: else if (userAgent.indexOf(UserAgentConstants.BE_OS_TYPE) != -1)
262: osName = UserAgentConstants.BE_OS;
263: else if (userAgent.indexOf(UserAgentConstants.OS2_OS_TYPE) != -1)
264: osName = UserAgentConstants.OS2_OS;
265: else if (userAgent.indexOf(UserAgentConstants.AIX_OS_TYPE) != -1)
266: osName = UserAgentConstants.AIX_OS;
267: else if (userAgent.indexOf(UserAgentConstants.MAC_OS_TYPE) != -1
268: || userAgent
269: .indexOf(UserAgentConstants.PPC_OS_TYPE) != -1)
270: osName = UserAgentConstants.MAC_OS;
271: else
272: osName = UserAgentConstants.UNKNOWN_OS;
273:
274: DBUtil.execute(Defaults.getDataSourceName(), rmInsertSQL,
275: new Object[] {
276: new java.sql.Timestamp(System
277: .currentTimeMillis()), sessionID,
278: clientName, clientIP, userAgent, referer,
279: reqURL, browserName, osName });
280:
281: DBUtil.execute(Defaults.getDataSourceName(),
282: rmUpdateCounterSQL, new Object[] {
283: UserAgentConstants.BROWSER, browserName,
284: UserAgentConstants.OS, osName });
285:
286: } catch (NullPointerException ex) {
287: // NullPointer exception is thrown in the first call to request monitor
288: // when PortalServlet is not auto-loaded. This happens because the default
289: // data source has not yet been set. To avoid checking for a null in every
290: // request, the exception is catched and ignored here.
291: } catch (Throwable ex) {
292: rmLogger.error("RequestMonitor.doFilter()", ex);
293: }
294:
295: fChain.doFilter(rq, res);
296:
297: }
298:
299: /* (non-Javadoc)
300: * @see javax.servlet.Filter#destroy()
301: */
302: public void destroy() {
303: rmTimer.cancel();
304:
305: }
306:
307: private boolean ignoreThisRequest(String ipAddress, String userAgent) {
308: for (int i = 0; i < rmIgnoredIP.length; i++) {
309: if (rmIgnoredIP[i].equals(ipAddress)) {
310: return true;
311: }
312: }
313: for (int i = 0; i < rmIgnoredUserAgents.length; i++) {
314: if (userAgent.indexOf(rmIgnoredUserAgents[i]) > -1) {
315: return true;
316: }
317: }
318:
319: return false;
320: }
321:
322: public synchronized void run() {
323: rmVisitorMap.clear();
324: }
325: }
|