From 589947fed669602a28e53eb40a4cec84b65d894e Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 12 Aug 2012 17:43:44 +0800 Subject: [PATCH 1/9] add WebSessionStat support --- .../java/com/alibaba/druid/stat/DruidDataSourceStatManager.java | 2 +- src/main/resources/support/http/resources/js/weburi.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/druid/stat/DruidDataSourceStatManager.java b/src/main/java/com/alibaba/druid/stat/DruidDataSourceStatManager.java index ca8b1e7f45..9cc649ca12 100644 --- a/src/main/java/com/alibaba/druid/stat/DruidDataSourceStatManager.java +++ b/src/main/java/com/alibaba/druid/stat/DruidDataSourceStatManager.java @@ -234,7 +234,7 @@ public class DruidDataSourceStatManager implements DruidDataSourceStatManagerMBe public CompositeDataSupport getCompositeData(Object dataSource) throws JMException { CompositeType rowType = getDruidDataSourceCompositeType(); - Map map = DruidDataSourceUtils.getStatData(dataSource); + Map map = DruidDataSourceUtils.getStatDataForMBean(dataSource); return new CompositeDataSupport(rowType, map); } diff --git a/src/main/resources/support/http/resources/js/weburi.js b/src/main/resources/support/http/resources/js/weburi.js index acd16e8ae9..15cb9dfd9c 100644 --- a/src/main/resources/support/http/resources/js/weburi.js +++ b/src/main/resources/support/http/resources/js/weburi.js @@ -95,7 +95,7 @@ function ajaxResponseForDataSourceSqlStatInfo() { var sqlStat = sqlStatList[i]; var newRow = sqlStatTable.insertRow(-1); newRow.insertCell(-1).innerHTML = i+1; - newRow.insertCell(-1).innerHTML = subSqlString(sqlStat.URI, 25); + newRow.insertCell(-1).innerHTML = subSqlString(sqlStat.URI, 64); newRow.insertCell(-1).innerHTML = sqlStat.RequestCount; newRow.insertCell(-1).innerHTML = sqlStat.RunningCount; newRow.insertCell(-1).innerHTML = sqlStat.ConcurrentMax; From b679c084e42fc1ccc367a84ad01c6ac29d37f497 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 12 Aug 2012 19:41:07 +0800 Subject: [PATCH 2/9] add WebSessionStat support --- .../druid/support/JSONDruidStatService.java | 2 +- .../druid/support/http/WebStatFilter.java | 38 +++++- .../druid/support/http/stat/WebAppStat.java | 4 +- .../support/http/stat/WebRequestStat.java | 9 ++ .../support/http/stat/WebSessionStat.java | 117 ++++++++++++++++-- .../support/http/resources/js/websession.js | 4 + .../support/http/resources/websession.html | 4 + 7 files changed, 163 insertions(+), 15 deletions(-) diff --git a/src/main/java/com/alibaba/druid/support/JSONDruidStatService.java b/src/main/java/com/alibaba/druid/support/JSONDruidStatService.java index d88e0158e9..92a73bff94 100644 --- a/src/main/java/com/alibaba/druid/support/JSONDruidStatService.java +++ b/src/main/java/com/alibaba/druid/support/JSONDruidStatService.java @@ -159,7 +159,7 @@ public class JSONDruidStatService { Map dataMap = new LinkedHashMap(); dataMap.put("ResultCode", resultCode); dataMap.put("Content", content); - return JSON.toJSONString(dataMap); + return JSON.toJSONStringWithDateFormat(dataMap, "yyyy-MM-dd hh:mm:ss"); } } diff --git a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java index ba228da431..589b972d40 100644 --- a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java +++ b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java @@ -18,9 +18,14 @@ import com.alibaba.druid.support.http.stat.WebAppStatManager; import com.alibaba.druid.support.http.stat.WebRequestStat; import com.alibaba.druid.support.http.stat.WebSessionStat; import com.alibaba.druid.support.http.stat.WebURIStat; +import com.alibaba.druid.support.logging.Log; +import com.alibaba.druid.support.logging.LogFactory; +import com.alibaba.druid.util.DruidWebUtils; public class WebStatFilter implements Filter { + private final static Log LOG = LogFactory.getLog(WebStatFilter.class); + private WebAppStat webAppStat = null; private WebStatFilterContextListener statFilterContextListener = new WebStatFilterContextListener(); @@ -39,6 +44,7 @@ public class WebStatFilter implements Filter { } long startNano = System.nanoTime(); + long startMillis = System.currentTimeMillis(); WebRequestStat requestStat = new WebRequestStat(startNano); WebRequestStat.set(requestStat); @@ -48,6 +54,7 @@ public class WebStatFilter implements Filter { // 第一次访问时,sessionId为null,如果缺省sessionCreate=false,sessionStat就为null。 if (sessionStat != null) { sessionStat.beforeInvoke(); + sessionStat.setLastAccessTimeMillis(startMillis); } try { @@ -64,6 +71,7 @@ public class WebStatFilter implements Filter { } else { sessionStat = getSessionStat(httpRequest); if (sessionStat != null) { + sessionStat.setLastAccessTimeMillis(startMillis); sessionStat.reacord(nanoSpan); } } @@ -72,13 +80,35 @@ public class WebStatFilter implements Filter { } } - public WebSessionStat getSessionStat(HttpServletRequest httpRequest) { + public WebSessionStat getSessionStat(HttpServletRequest request) { WebSessionStat sessionStat = null; - String sessionId = getSessionId(httpRequest); + String sessionId = getSessionId(request); if (sessionId != null) { sessionStat = webAppStat.getSessionStat(sessionId); } + if (sessionStat != null) { + if (sessionStat.getCreateTimeMillis() == -1L) { + HttpSession session = request.getSession(false); + if (session != null) { + sessionStat.setCreateTimeMillis(session.getCreationTime()); + } else { + sessionStat.setCreateTimeMillis(System.currentTimeMillis()); + } + } + + String ip = DruidWebUtils.getRemoteAddr(request); + + int addressCount = sessionStat.getRemoteAddresses().size(); + if (addressCount < 10) { + sessionStat.addRemoteAddress(ip); + } else { + if (!sessionStat.getRemoteAddress().contains(ip)) { + LOG.error("sessoin ip change too many"); + } + } + } + return sessionStat; } @@ -174,6 +204,10 @@ public class WebStatFilter implements Filter { @Override public void executeAfter(String sql, long nanoSpan, Throwable error) { + WebRequestStat localStat = WebRequestStat.current(); + if (localStat != null) { + localStat.addJdbcExecuteNano(nanoSpan); + } } @Override diff --git a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java index f8101927cd..f9d9611525 100644 --- a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStat.java @@ -114,7 +114,9 @@ public class WebAppStat { WebSessionStat uriStat = sessionStatMap.get(sessionId); if (uriStat == null) { - sessionStatMap.putIfAbsent(sessionId, new WebSessionStat(sessionId)); + WebSessionStat newStat = new WebSessionStat(sessionId); + + sessionStatMap.putIfAbsent(sessionId, newStat); uriStat = sessionStatMap.get(sessionId); } diff --git a/src/main/java/com/alibaba/druid/support/http/stat/WebRequestStat.java b/src/main/java/com/alibaba/druid/support/http/stat/WebRequestStat.java index a9f67c9f67..17f89e313d 100644 --- a/src/main/java/com/alibaba/druid/support/http/stat/WebRequestStat.java +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebRequestStat.java @@ -9,6 +9,7 @@ public class WebRequestStat { private long jdbcExecuteCount; private long jdbcUpdateCount; private long jdbcFetchRowCount; + private long jdbcExecuteNano; public WebRequestStat(){ @@ -60,6 +61,14 @@ public class WebRequestStat { return jdbcExecuteCount; } + public long getJdbcExecuteNano() { + return jdbcExecuteNano; + } + + public void addJdbcExecuteNano(long nano) { + jdbcExecuteNano += nano; + } + public long getJdbcUpdateCount() { return jdbcUpdateCount; } diff --git a/src/main/java/com/alibaba/druid/support/http/stat/WebSessionStat.java b/src/main/java/com/alibaba/druid/support/http/stat/WebSessionStat.java index 665efc9462..8b0e76b558 100644 --- a/src/main/java/com/alibaba/druid/support/http/stat/WebSessionStat.java +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebSessionStat.java @@ -1,7 +1,10 @@ package com.alibaba.druid.support.http.stat; +import java.util.Date; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.Map; +import java.util.Set; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicLong; @@ -9,18 +12,24 @@ public class WebSessionStat { private final String sessionId; - private final AtomicInteger runningCount = new AtomicInteger(); - private final AtomicInteger concurrentMax = new AtomicInteger(); - private final AtomicLong requestCount = new AtomicLong(0); + private final AtomicInteger runningCount = new AtomicInteger(); + private final AtomicInteger concurrentMax = new AtomicInteger(); + private final AtomicLong requestCount = new AtomicLong(0); - private final AtomicLong jdbcFetchRowCount = new AtomicLong(); - private final AtomicLong jdbcUpdateCount = new AtomicLong(); - private final AtomicLong jdbcExecuteCount = new AtomicLong(); + private final AtomicLong jdbcFetchRowCount = new AtomicLong(); + private final AtomicLong jdbcUpdateCount = new AtomicLong(); + private final AtomicLong jdbcExecuteCount = new AtomicLong(); + private final AtomicLong jdbcExecuteTimeNano = new AtomicLong(); - private final AtomicLong jdbcCommitCount = new AtomicLong(); - private final AtomicLong jdbcRollbackCount = new AtomicLong(); + private final AtomicLong jdbcCommitCount = new AtomicLong(); + private final AtomicLong jdbcRollbackCount = new AtomicLong(); - private final AtomicLong requestTimeNano = new AtomicLong(); + private final AtomicLong requestTimeNano = new AtomicLong(); + + private long createTimeMillis = -1L; + private long lastAccessTimeMillis = -1L; + + private Set remoteAddresses = new HashSet(); public WebSessionStat(String sessionId){ super(); @@ -31,6 +40,62 @@ public class WebSessionStat { return sessionId; } + public long getCreateTimeMillis() { + return createTimeMillis; + } + + public Date getCreateTime() { + if (createTimeMillis == -1L) { + return null; + } + + return new Date(createTimeMillis); + } + + public void setCreateTimeMillis(long createTimeMillis) { + this.createTimeMillis = createTimeMillis; + } + + public long getLastAccessTimeMillis() { + return lastAccessTimeMillis; + } + + public Date getLastAccessTime() { + if (lastAccessTimeMillis == -1L) { + return null; + } + + return new Date(lastAccessTimeMillis); + } + + public Set getRemoteAddresses() { + return remoteAddresses; + } + + public String getRemoteAddress() { + if (remoteAddresses.size() == 0) { + return null; + } + + if (remoteAddresses.size() == 1) { + return remoteAddresses.iterator().next(); + } + + StringBuilder buf = new StringBuilder(); + for (String item : remoteAddresses) { + if (buf.length() != 0) { + buf.append(","); + } + buf.append(item); + } + + return buf.toString(); + } + + public void setLastAccessTimeMillis(long lastAccessTimeMillis) { + this.lastAccessTimeMillis = lastAccessTimeMillis; + } + public void beforeInvoke() { int running = runningCount.incrementAndGet(); @@ -66,9 +131,14 @@ public class WebSessionStat { this.addJdbcUpdateCount(requestStat.getJdbcUpdateCount()); this.addJdbcCommitCount(requestStat.getJdbcCommitCount()); this.addJdbcRollbackCount(requestStat.getJdbcRollbackCount()); + this.addJdbcExecuteTimeNano(requestStat.getJdbcExecuteNano()); } } + public void addRemoteAddress(String ip) { + this.remoteAddresses.add(ip); + } + public int getRunningCount() { return this.runningCount.get(); } @@ -81,6 +151,14 @@ public class WebSessionStat { return requestCount.get(); } + public long getRequestTimeNano() { + return requestTimeNano.get(); + } + + public long getRequestTimeMillis() { + return getRequestTimeNano() / (1000 * 1000); + } + public void addJdbcFetchRowCount(long delta) { this.jdbcFetchRowCount.addAndGet(delta); } @@ -109,6 +187,18 @@ public class WebSessionStat { return jdbcExecuteCount.get(); } + public long getJdbcExecuteTimeMillis() { + return getJdbcExecuteTimeNano() / (1000 * 1000); + } + + public long getJdbcExecuteTimeNano() { + return jdbcExecuteTimeNano.get(); + } + + public void addJdbcExecuteTimeNano(long nano) { + jdbcExecuteTimeNano.addAndGet(nano); + } + public void incrementJdbcCommitCount() { jdbcCommitCount.incrementAndGet(); } @@ -128,11 +218,11 @@ public class WebSessionStat { public long getJdbcRollbackCount() { return jdbcRollbackCount.get(); } - + public void addJdbcRollbackCount(long rollbackCount) { this.jdbcRollbackCount.addAndGet(rollbackCount); } - + public Map getStatData() { Map data = new LinkedHashMap(); @@ -140,11 +230,16 @@ public class WebSessionStat { data.put("RunningCount", this.getRunningCount()); data.put("ConcurrentMax", this.getConcurrentMax()); data.put("RequestCount", this.getRequestCount()); + data.put("RequestTimeMillisTotal", this.getRequestTimeMillis()); + data.put("CreateTime", this.getCreateTime()); + data.put("LastAccessTime", this.getLastAccessTime()); + data.put("RemoteAddress", this.getRemoteAddress()); data.put("JdbcCommitCount", this.getJdbcCommitCount()); data.put("JdbcRollbackCount", this.getJdbcRollbackCount()); data.put("JdbcExecuteCount", this.getJdbcExecuteCount()); + data.put("JdbcExecuteTimeMillis", this.getJdbcExecuteTimeMillis()); data.put("JdbcFetchRowCount", this.getJdbcFetchRowCount()); data.put("JdbcUpdateCount", this.getJdbcUpdateCount()); diff --git a/src/main/resources/support/http/resources/js/websession.js b/src/main/resources/support/http/resources/js/websession.js index 6e201222ea..c4d638a5a6 100644 --- a/src/main/resources/support/http/resources/js/websession.js +++ b/src/main/resources/support/http/resources/js/websession.js @@ -96,10 +96,14 @@ function ajaxResponseForDataSourceSqlStatInfo() { var newRow = sqlStatTable.insertRow(-1); newRow.insertCell(-1).innerHTML = i+1; newRow.insertCell(-1).innerHTML = sqlStat.SESSIONID; + newRow.insertCell(-1).innerHTML = sqlStat.CreateTime; + newRow.insertCell(-1).innerHTML = sqlStat.RemoteAddress; newRow.insertCell(-1).innerHTML = sqlStat.RequestCount; + newRow.insertCell(-1).innerHTML = sqlStat.RequestTimeMillisTotal; newRow.insertCell(-1).innerHTML = sqlStat.RunningCount; newRow.insertCell(-1).innerHTML = sqlStat.ConcurrentMax; newRow.insertCell(-1).innerHTML = sqlStat.JdbcExecuteCount; + newRow.insertCell(-1).innerHTML = sqlStat.JdbcExecuteTimeMillis; newRow.insertCell(-1).innerHTML = sqlStat.JdbcCommitCount; newRow.insertCell(-1).innerHTML = sqlStat.JdbcRollbackCount; newRow.insertCell(-1).innerHTML = sqlStat.JdbcFetchRowCount; diff --git a/src/main/resources/support/http/resources/websession.html b/src/main/resources/support/http/resources/websession.html index 148f0dfde8..3316533342 100644 --- a/src/main/resources/support/http/resources/websession.html +++ b/src/main/resources/support/http/resources/websession.html @@ -25,10 +25,14 @@ N-- SESSIONID + CreateTime + RemoteAddress Request Count + RequestTime Running Count Concurrent Max JdbcExecuteCount + JdbcExecuteTime JdbcCommitCount JdbcRollbackCount JdbcFetchRowCount From f26c98ace99e1c5681e92153c345ad83b19c9f5a Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 12 Aug 2012 19:41:12 +0800 Subject: [PATCH 3/9] add WebSessionStat support --- .../com/alibaba/druid/util/DruidWebUtils.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 src/main/java/com/alibaba/druid/util/DruidWebUtils.java diff --git a/src/main/java/com/alibaba/druid/util/DruidWebUtils.java b/src/main/java/com/alibaba/druid/util/DruidWebUtils.java new file mode 100644 index 0000000000..dc5bcc9b64 --- /dev/null +++ b/src/main/java/com/alibaba/druid/util/DruidWebUtils.java @@ -0,0 +1,21 @@ +package com.alibaba.druid.util; + +import javax.servlet.http.HttpServletRequest; + +public class DruidWebUtils { + + public static String getRemoteAddr(HttpServletRequest request) { + String ip = request.getHeader("x-forwarded-for"); + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + + return ip; + } +} From 98f91b177ade5be0035f3df3ec8fc7ff2be752cf Mon Sep 17 00:00:00 2001 From: Zhanming Qi Date: Sun, 12 Aug 2012 20:14:52 +0800 Subject: [PATCH 4/9] add WebStatFilter's exclusions, support /foo*, *.html, /bar/index.html --- .../druid/support/http/WebStatFilter.java | 2 +- .../alibaba/druid/util/AntPathMatcher.java | 394 ++++++++++++++++++ .../alibaba/druid/util/PatternMatcher.java | 24 ++ .../druid/util/ServletPathMatcher.java | 41 ++ .../com/alibaba/druid/util/StringUtils.java | 83 ++++ .../druid/util/ServletPathMathcherTest.java | 99 +++++ 6 files changed, 642 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/alibaba/druid/util/AntPathMatcher.java create mode 100644 src/main/java/com/alibaba/druid/util/PatternMatcher.java create mode 100644 src/main/java/com/alibaba/druid/util/ServletPathMatcher.java create mode 100644 src/test/java/com/alibaba/druid/util/ServletPathMathcherTest.java diff --git a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java index 589b972d40..7681bb159d 100644 --- a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java +++ b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java @@ -239,4 +239,4 @@ public class WebStatFilter implements Filter { } } } -} +} \ No newline at end of file diff --git a/src/main/java/com/alibaba/druid/util/AntPathMatcher.java b/src/main/java/com/alibaba/druid/util/AntPathMatcher.java new file mode 100644 index 0000000000..2629f91907 --- /dev/null +++ b/src/main/java/com/alibaba/druid/util/AntPathMatcher.java @@ -0,0 +1,394 @@ +package com.alibaba.druid.util; + +/** + *

+ * PathMatcher implementation for Ant-style path patterns. Examples are provided below. + *

+ *

+ * Part of this mapping code has been kindly borrowed from Apache Ant. + *

+ * The mapping matches URLs using the following rules:
+ *

    + *
  • ? matches one character
  • + *
  • * matches zero or more characters
  • + *
  • ** matches zero or more 'directories' in a path
  • + *
+ *

+ * Some examples:
+ *

    + *
  • com/t?st.jsp - matches com/test.jsp but also com/tast.jsp or + * com/txst.jsp
  • + *
  • com/*.jsp - matches all .jsp files in the com directory
  • + *
  • com/**/test.jsp - matches all test.jsp files underneath the com + * path
  • + *
  • com/alibaba/druid/**/*.jsp - matches all .jsp files underneath the + * org/apache/shiro path
  • + *
  • com/**/servlet/bla.jsp - matches com/alibaba/druid/servlet/bla.jsp but also + * com/alibaba/druid/testing/servlet/bla.jsp and com/servlet/bla.jsp
  • + *
+ *

+ * N.B.: This class was borrowed (with much appreciation) from the Shiro + * and the Spring Framework with modifications. We didn't want to reinvent + * the wheel of great work they've done, but also didn't want to force every Shiro user to depend on Spring + *

+ *

+ * As per the Apache 2.0 license, the original copyright notice and all author and copyright information have remained + * in tact. + *

+ * + * @since 16.07.2003 + */ +public class AntPathMatcher implements PatternMatcher { + + // TODO - complete JavaDoc + + /** + * Default path separator: "/" + */ + public static final String DEFAULT_PATH_SEPARATOR = "/"; + + private String pathSeparator = DEFAULT_PATH_SEPARATOR; + + /** + * Set the path separator to use for pattern parsing. Default is "/", as in Ant. + */ + public void setPathSeparator(String pathSeparator) { + this.pathSeparator = (pathSeparator != null ? pathSeparator : DEFAULT_PATH_SEPARATOR); + } + + public boolean isPattern(String path) { + return (path.indexOf('*') != -1 || path.indexOf('?') != -1); + } + + public boolean matches(String pattern, String source) { + return match(pattern, source); + } + + public boolean match(String pattern, String path) { + return doMatch(pattern, path, true); + } + + public boolean matchStart(String pattern, String path) { + return doMatch(pattern, path, false); + } + + /** + * Actually match the given path against the given pattern. + * + * @param pattern the pattern to match against + * @param path the path String to test + * @param fullMatch whether a full pattern match is required (else a pattern match as far as the given base path + * goes is sufficient) + * @return true if the supplied path matched, false if it didn't + */ + protected boolean doMatch(String pattern, String path, boolean fullMatch) { + if (path.startsWith(this.pathSeparator) != pattern.startsWith(this.pathSeparator)) { + return false; + } + + String[] pattDirs = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); + String[] pathDirs = StringUtils.tokenizeToStringArray(path, this.pathSeparator); + + int pattIdxStart = 0; + int pattIdxEnd = pattDirs.length - 1; + int pathIdxStart = 0; + int pathIdxEnd = pathDirs.length - 1; + + // Match all elements up to the first ** + while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { + String patDir = pattDirs[pattIdxStart]; + if ("**".equals(patDir)) { + break; + } + if (!matchStrings(patDir, pathDirs[pathIdxStart])) { + return false; + } + pattIdxStart++; + pathIdxStart++; + } + + if (pathIdxStart > pathIdxEnd) { + // Path is exhausted, only match if rest of pattern is * or **'s + if (pattIdxStart > pattIdxEnd) { + return (pattern.endsWith(this.pathSeparator) ? path.endsWith(this.pathSeparator) : !path.endsWith(this.pathSeparator)); + } + if (!fullMatch) { + return true; + } + if (pattIdxStart == pattIdxEnd && pattDirs[pattIdxStart].equals("*") && path.endsWith(this.pathSeparator)) { + return true; + } + for (int i = pattIdxStart; i <= pattIdxEnd; i++) { + if (!pattDirs[i].equals("**")) { + return false; + } + } + return true; + } else if (pattIdxStart > pattIdxEnd) { + // String not exhausted, but pattern is. Failure. + return false; + } else if (!fullMatch && "**".equals(pattDirs[pattIdxStart])) { + // Path start definitely matches due to "**" part in pattern. + return true; + } + + // up to last '**' + while (pattIdxStart <= pattIdxEnd && pathIdxStart <= pathIdxEnd) { + String patDir = pattDirs[pattIdxEnd]; + if (patDir.equals("**")) { + break; + } + if (!matchStrings(patDir, pathDirs[pathIdxEnd])) { + return false; + } + pattIdxEnd--; + pathIdxEnd--; + } + if (pathIdxStart > pathIdxEnd) { + // String is exhausted + for (int i = pattIdxStart; i <= pattIdxEnd; i++) { + if (!pattDirs[i].equals("**")) { + return false; + } + } + return true; + } + + while (pattIdxStart != pattIdxEnd && pathIdxStart <= pathIdxEnd) { + int patIdxTmp = -1; + for (int i = pattIdxStart + 1; i <= pattIdxEnd; i++) { + if (pattDirs[i].equals("**")) { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == pattIdxStart + 1) { + // '**/**' situation, so skip one + pattIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - pattIdxStart - 1); + int strLength = (pathIdxEnd - pathIdxStart + 1); + int foundIdx = -1; + + strLoop: for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + String subPat = (String) pattDirs[pattIdxStart + j + 1]; + String subStr = (String) pathDirs[pathIdxStart + i + j]; + if (!matchStrings(subPat, subStr)) { + continue strLoop; + } + } + foundIdx = pathIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + pattIdxStart = patIdxTmp; + pathIdxStart = foundIdx + patLength; + } + + for (int i = pattIdxStart; i <= pattIdxEnd; i++) { + if (!pattDirs[i].equals("**")) { + return false; + } + } + + return true; + } + + /** + * Tests whether or not a string matches against a pattern. The pattern may contain two special characters:
+ * '*' means zero or more characters
+ * '?' means one and only one character + * + * @param pattern pattern to match against. Must not be null. + * @param str string which must be matched against the pattern. Must not be null. + * @return true if the string matches against the pattern, or false otherwise. + */ + private boolean matchStrings(String pattern, String str) { + char[] patArr = pattern.toCharArray(); + char[] strArr = str.toCharArray(); + int patIdxStart = 0; + int patIdxEnd = patArr.length - 1; + int strIdxStart = 0; + int strIdxEnd = strArr.length - 1; + char ch; + + boolean containsStar = false; + for (char aPatArr : patArr) { + if (aPatArr == '*') { + containsStar = true; + break; + } + } + + if (!containsStar) { + // No '*'s, so we make a shortcut + if (patIdxEnd != strIdxEnd) { + return false; // Pattern and string do not have the same size + } + for (int i = 0; i <= patIdxEnd; i++) { + ch = patArr[i]; + if (ch != '?') { + if (ch != strArr[i]) { + return false;// Character mismatch + } + } + } + return true; // String matches against pattern + } + + if (patIdxEnd == 0) { + return true; // Pattern contains only '*', which matches anything + } + + // Process characters before first star + while ((ch = patArr[patIdxStart]) != '*' && strIdxStart <= strIdxEnd) { + if (ch != '?') { + if (ch != strArr[strIdxStart]) { + return false;// Character mismatch + } + } + patIdxStart++; + strIdxStart++; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (patArr[i] != '*') { + return false; + } + } + return true; + } + + // Process characters after last star + while ((ch = patArr[patIdxEnd]) != '*' && strIdxStart <= strIdxEnd) { + if (ch != '?') { + if (ch != strArr[strIdxEnd]) { + return false;// Character mismatch + } + } + patIdxEnd--; + strIdxEnd--; + } + if (strIdxStart > strIdxEnd) { + // All characters in the string are used. Check if only '*'s are + // left in the pattern. If so, we succeeded. Otherwise failure. + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (patArr[i] != '*') { + return false; + } + } + return true; + } + + // process pattern between stars. padIdxStart and patIdxEnd point + // always to a '*'. + while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) { + int patIdxTmp = -1; + for (int i = patIdxStart + 1; i <= patIdxEnd; i++) { + if (patArr[i] == '*') { + patIdxTmp = i; + break; + } + } + if (patIdxTmp == patIdxStart + 1) { + // Two stars next to each other, skip the first one. + patIdxStart++; + continue; + } + // Find the pattern between padIdxStart & padIdxTmp in str between + // strIdxStart & strIdxEnd + int patLength = (patIdxTmp - patIdxStart - 1); + int strLength = (strIdxEnd - strIdxStart + 1); + int foundIdx = -1; + strLoop: for (int i = 0; i <= strLength - patLength; i++) { + for (int j = 0; j < patLength; j++) { + ch = patArr[patIdxStart + j + 1]; + if (ch != '?') { + if (ch != strArr[strIdxStart + i + j]) { + continue strLoop; + } + } + } + + foundIdx = strIdxStart + i; + break; + } + + if (foundIdx == -1) { + return false; + } + + patIdxStart = patIdxTmp; + strIdxStart = foundIdx + patLength; + } + + // All characters in the string are used. Check if only '*'s are left + // in the pattern. If so, we succeeded. Otherwise failure. + for (int i = patIdxStart; i <= patIdxEnd; i++) { + if (patArr[i] != '*') { + return false; + } + } + + return true; + } + + /** + * Given a pattern and a full path, determine the pattern-mapped part. + *

+ * For example: + *

    + *
  • '/docs/cvs/commit.html' and '/docs/cvs/commit.html -> ''
  • + *
  • '/docs/*' and '/docs/cvs/commit -> 'cvs/commit'
  • + *
  • '/docs/cvs/*.html' and '/docs/cvs/commit.html -> 'commit.html'
  • + *
  • '/docs/**' and '/docs/cvs/commit -> 'cvs/commit'
  • + *
  • '/docs/**\/*.html' and '/docs/cvs/commit.html -> 'cvs/commit.html'
  • + *
  • '/*.html' and '/docs/cvs/commit.html -> 'docs/cvs/commit.html'
  • + *
  • '*.html' and '/docs/cvs/commit.html -> '/docs/cvs/commit.html'
  • + *
  • '*' and '/docs/cvs/commit.html -> '/docs/cvs/commit.html'
  • + *
+ *

+ * Assumes that {@link #match} returns true for 'pattern' and 'path', but + * does not enforce this. + */ + public String extractPathWithinPattern(String pattern, String path) { + String[] patternParts = StringUtils.tokenizeToStringArray(pattern, this.pathSeparator); + String[] pathParts = StringUtils.tokenizeToStringArray(path, this.pathSeparator); + + StringBuilder buffer = new StringBuilder(); + + // Add any path parts that have a wildcarded pattern part. + int puts = 0; + for (int i = 0; i < patternParts.length; i++) { + String patternPart = patternParts[i]; + if ((patternPart.indexOf('*') > -1 || patternPart.indexOf('?') > -1) && pathParts.length >= i + 1) { + if (puts > 0 || (i == 0 && !pattern.startsWith(this.pathSeparator))) { + buffer.append(this.pathSeparator); + } + buffer.append(pathParts[i]); + puts++; + } + } + + // Append any trailing path parts. + for (int i = patternParts.length; i < pathParts.length; i++) { + if (puts > 0 || i > 0) { + buffer.append(this.pathSeparator); + } + buffer.append(pathParts[i]); + } + + return buffer.toString(); + } + +} diff --git a/src/main/java/com/alibaba/druid/util/PatternMatcher.java b/src/main/java/com/alibaba/druid/util/PatternMatcher.java new file mode 100644 index 0000000000..9b49be34ff --- /dev/null +++ b/src/main/java/com/alibaba/druid/util/PatternMatcher.java @@ -0,0 +1,24 @@ +package com.alibaba.druid.util; + +/** + * Interface for components that can match source strings against a specified pattern string. + *

+ * Different implementations can support different pattern types, for example, Ant style path expressions, or regular + * expressions, or other types of text based patterns. + * + * @see com.alibaba.druid.util.AntPathMatcher AntPathMatcher + * @since 0.2.6 + */ +public interface PatternMatcher { + + /** + * Returns true if the given source matches the specified pattern, + * false otherwise. + * + * @param pattern the pattern to match against + * @param source the source to match + * @return true if the given source matches the specified pattern, + * false otherwise. + */ + boolean matches(String pattern, String source); +} diff --git a/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java b/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java new file mode 100644 index 0000000000..6395e38aec --- /dev/null +++ b/src/main/java/com/alibaba/druid/util/ServletPathMatcher.java @@ -0,0 +1,41 @@ +package com.alibaba.druid.util; + +public class ServletPathMatcher implements PatternMatcher { + + /** + *

+ * three type: endsWithMatch(eg. /xxx*=/xxx/xyz), startsWithMatch(eg. *.xxx=abc.xxx), equals(eg. /xxx=/xxx)。 + *

+ * Notice: *xxx* will match *xxxyyyy. endsWithMatch first. + */ + @Override + public boolean matches(String pattern, String source) { + if (pattern == null || source == null) { + return false; + } + pattern = pattern.trim(); + source = source.trim(); + if (pattern.endsWith("*")) { + // pattern: /druid* source:/druid/index.html + int length = pattern.length() - 1; + if (source.length() >= length) { + if (pattern.substring(0, length).equals(source.substring(0, length))) { + return true; + } + } + } else if (pattern.startsWith("*")) { + // pattern: *.html source:/xx/xx.html + int length = pattern.length() - 1; + if (source.length() >= length && source.endsWith(pattern.substring(1))) { + return true; + } + } else { + // pattern: /druid/index.html source:/druid/index.html + if (pattern.equals(source)) { + return true; + } + } + return false; + } + +} diff --git a/src/main/java/com/alibaba/druid/util/StringUtils.java b/src/main/java/com/alibaba/druid/util/StringUtils.java index 01ce45e10c..1b9f0a8108 100644 --- a/src/main/java/com/alibaba/druid/util/StringUtils.java +++ b/src/main/java/com/alibaba/druid/util/StringUtils.java @@ -1,7 +1,11 @@ package com.alibaba.druid.util; +import java.util.ArrayList; +import java.util.Collection; import java.util.LinkedHashMap; +import java.util.List; import java.util.Map; +import java.util.StringTokenizer; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; @@ -97,6 +101,85 @@ public class StringUtils { return parameters; } + /** + * Tokenize the given String into a String array via a StringTokenizer. Trims tokens and omits empty tokens. + *

+ * The given delimiters string is supposed to consist of any number of delimiter characters. Each of those + * characters can be used to separate tokens. A delimiter is always a single character; for multi-character + * delimiters, consider using delimitedListToStringArray + *

+ *

+ * Copied from the Spring Framework while retaining all license, copyright and author information. + * + * @param str the String to tokenize + * @param delimiters the delimiter characters, assembled as String (each of those characters is individually + * considered as delimiter). + * @return an array of the tokens + * @see java.util.StringTokenizer + * @see java.lang.String#trim() + */ + public static String[] tokenizeToStringArray(String str, String delimiters) { + return tokenizeToStringArray(str, delimiters, true, true); + } + + /** + * Tokenize the given String into a String array via a StringTokenizer. + *

+ * The given delimiters string is supposed to consist of any number of delimiter characters. Each of those + * characters can be used to separate tokens. A delimiter is always a single character; for multi-character + * delimiters, consider using delimitedListToStringArray + *

+ *

+ * Copied from the Spring Framework while retaining all license, copyright and author information. + * + * @param str the String to tokenize + * @param delimiters the delimiter characters, assembled as String (each of those characters is individually + * considered as delimiter) + * @param trimTokens trim the tokens via String's trim + * @param ignoreEmptyTokens omit empty tokens from the result array (only applies to tokens that are empty after + * trimming; StringTokenizer will not consider subsequent delimiters as token in the first place). + * @return an array of the tokens (null if the input String was null) + * @see java.util.StringTokenizer + * @see java.lang.String#trim() + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static String[] tokenizeToStringArray(String str, String delimiters, boolean trimTokens, + boolean ignoreEmptyTokens) { + + if (str == null) { + return null; + } + StringTokenizer st = new StringTokenizer(str, delimiters); + List tokens = new ArrayList(); + while (st.hasMoreTokens()) { + String token = st.nextToken(); + if (trimTokens) { + token = token.trim(); + } + if (!ignoreEmptyTokens || token.length() > 0) { + tokens.add(token); + } + } + return toStringArray(tokens); + } + + /** + * Copy the given Collection into a String array. The Collection must contain String elements only. + *

+ *

+ * Copied from the Spring Framework while retaining all license, copyright and author information. + * + * @param collection the Collection to copy + * @return the String array (null if the passed-in Collection was null) + */ + @SuppressWarnings({ "unchecked", "rawtypes" }) + public static String[] toStringArray(Collection collection) { + if (collection == null) { + return null; + } + return (String[]) collection.toArray(new String[collection.size()]); + } + public static void main(String args[]) { System.out.println(JSON.toJSONString(getParameters("test?t=1&f=").get("t"))); } diff --git a/src/test/java/com/alibaba/druid/util/ServletPathMathcherTest.java b/src/test/java/com/alibaba/druid/util/ServletPathMathcherTest.java new file mode 100644 index 0000000000..c1f0e68544 --- /dev/null +++ b/src/test/java/com/alibaba/druid/util/ServletPathMathcherTest.java @@ -0,0 +1,99 @@ +package com.alibaba.druid.util; + +import static org.hamcrest.CoreMatchers.equalTo; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + + +public class ServletPathMathcherTest { + + /** + * PatternMatcher used in determining which paths to react to for a given request. + */ + protected PatternMatcher pathMatcher = new ServletPathMatcher(); + + @Test + public void testStartsWithPattern() { + String[] bogusPatterns = { "/druid*", "/druid*/what*", "*/druid*" }; + String[] bogusSources = { "/druid", "/druid/index.html", "/druid*/what/xyu" }; + boolean result = false; + for (int i = 0; i < bogusSources.length; i++) { + for (int j = 0; j < bogusPatterns.length; j++) { + String bogusSource = bogusSources[i]; + String bogusPattern = bogusPatterns[j]; + if (pathMatcher.matches(bogusPattern, bogusSource)) { + result = true; + } + if (result == true) { + break; + } + } + assertThat(true, equalTo(result)); + result = false; + } + } + + @Test + public void testEndsWithPattern() { + String[] bogusPatterns = { "*.html", "*.ico", "*.css" }; + String[] bogusSources = { "/index.html", "/favicon.ico", "/druid.css" }; + boolean result = false; + for (int i = 0; i < bogusSources.length; i++) { + for (int j = 0; j < bogusPatterns.length; j++) { + String bogusSource = bogusSources[i]; + String bogusPattern = bogusPatterns[j]; + if (pathMatcher.matches(bogusPattern, bogusSource)) { + result = true; + } + if (result == true) { + break; + } + } + assertThat(true, equalTo(result)); + result = false; + } + } + + @Test + public void testEqualsPattern() { + String[] bogusPatterns = { "/index.html", "/favicon.ico", "/xyz" }; + String[] bogusSources = { "/index.html", "/favicon.ico", "/xyz" }; + boolean result = false; + for (int i = 0; i < bogusSources.length; i++) { + for (int j = 0; j < bogusPatterns.length; j++) { + String bogusSource = bogusSources[i]; + String bogusPattern = bogusPatterns[j]; + if (pathMatcher.matches(bogusPattern, bogusSource)) { + result = true; + } + if (result == true) { + break; + } + } + assertThat(true, equalTo(result)); + result = false; + } + } + + @Test + public void testPatternPriority() { + String[] bogusPatterns = { "*html*", "/favicon.ico*", "*html" }; + String[] bogusSources = { "*html/ok?", "/favicon.ico/ok?", "/index.html" }; + boolean result = false; + for (int i = 0; i < bogusSources.length; i++) { + for (int j = 0; j < bogusPatterns.length; j++) { + String bogusSource = bogusSources[i]; + String bogusPattern = bogusPatterns[j]; + if (pathMatcher.matches(bogusPattern, bogusSource)) { + result = true; + } + if (result == true) { + break; + } + } + assertThat(true, equalTo(result)); + result = false; + } + } +} From c7e33b6697247ecd940f04d8988a962ddc7fe6f9 Mon Sep 17 00:00:00 2001 From: Zhanming Qi Date: Sun, 12 Aug 2012 20:19:02 +0800 Subject: [PATCH 5/9] add WebStatFilter's exclusions, support /foo*, *.html, /bar/index.html --- .../druid/support/http/WebStatFilter.java | 27 ++++++++++++++++--- 1 file changed, 23 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java index 7681bb159d..9d8799eefc 100644 --- a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java +++ b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java @@ -1,6 +1,9 @@ package com.alibaba.druid.support.http; import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import javax.servlet.Filter; import javax.servlet.FilterChain; @@ -21,6 +24,8 @@ import com.alibaba.druid.support.http.stat.WebURIStat; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; import com.alibaba.druid.util.DruidWebUtils; +import com.alibaba.druid.util.PatternMatcher; +import com.alibaba.druid.util.ServletPathMatcher; public class WebStatFilter implements Filter { @@ -28,7 +33,13 @@ public class WebStatFilter implements Filter { private WebAppStat webAppStat = null; private WebStatFilterContextListener statFilterContextListener = new WebStatFilterContextListener(); + /** + * PatternMatcher used in determining which paths to react to for a given request. + */ + protected PatternMatcher pathMatcher = new ServletPathMatcher(); + private final String EXCLUSIONS = "exclusions"; + private Set excludesPattern; private boolean createSession = false; @Override @@ -38,9 +49,13 @@ public class WebStatFilter implements Filter { final String requestURI = getRequestURI(httpRequest); - if (isExclusion(requestURI)) { - chain.doFilter(request, response); - return; + if (excludesPattern != null) { + for (String pattern : excludesPattern) { + if (pathMatcher.matches(pattern, requestURI)) { + chain.doFilter(request, response); + return; + } + } } long startNano = System.nanoTime(); @@ -133,6 +148,10 @@ public class WebStatFilter implements Filter { @Override public void init(FilterConfig config) throws ServletException { + String exclusions = config.getInitParameter(EXCLUSIONS); + if (exclusions != null && exclusions.trim().length() != 0) { + excludesPattern = new HashSet(Arrays.asList(exclusions.split("\\s*,\\s*"))); + } config.getServletContext().getContextPath(); StatFilterContext.getInstance().addContextListener(statFilterContextListener); @@ -239,4 +258,4 @@ public class WebStatFilter implements Filter { } } } -} \ No newline at end of file +} From 01f6ac4012632da4ae3e238b9fabbce038cd0401 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 12 Aug 2012 20:56:28 +0800 Subject: [PATCH 6/9] improve Utils Performance --- .../support/http/stat/WebAppStatUtils.java | 16 +++++++- .../druid/util/DruidDataSourceUtils.java | 37 +++++++++++++++++++ .../alibaba/druid/util/JdbcSqlStatUtils.java | 5 +++ 3 files changed, 56 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStatUtils.java b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStatUtils.java index 50e9fdb9b9..33b583ad59 100644 --- a/src/main/java/com/alibaba/druid/support/http/stat/WebAppStatUtils.java +++ b/src/main/java/com/alibaba/druid/support/http/stat/WebAppStatUtils.java @@ -13,6 +13,10 @@ public class WebAppStatUtils { @SuppressWarnings("unchecked") public static Map getStatData(Object webStat) { + if (webStat.getClass() == WebAppStat.class) { + return ((WebAppStat) webStat).getStatData(); + } + try { Method method = webStat.getClass().getMethod("getStatData"); Object obj = method.invoke(webStat); @@ -22,9 +26,13 @@ public class WebAppStatUtils { return null; } } - + @SuppressWarnings("unchecked") public static List> getURIStatDataList(Object webStat) { + if (webStat.getClass() == WebAppStat.class) { + return ((WebAppStat) webStat).getURIStatDataList(); + } + try { Method method = webStat.getClass().getMethod("getURIStatDataList"); Object obj = method.invoke(webStat); @@ -34,9 +42,13 @@ public class WebAppStatUtils { return null; } } - + @SuppressWarnings("unchecked") public static List> getSessionStatDataList(Object webStat) { + if (webStat.getClass() == WebAppStat.class) { + return ((WebAppStat) webStat).getSessionStatDataList(); + } + try { Method method = webStat.getClass().getMethod("getSessionStatDataList"); Object obj = method.invoke(webStat); diff --git a/src/main/java/com/alibaba/druid/util/DruidDataSourceUtils.java b/src/main/java/com/alibaba/druid/util/DruidDataSourceUtils.java index 49dc8d1741..b19070b1fe 100644 --- a/src/main/java/com/alibaba/druid/util/DruidDataSourceUtils.java +++ b/src/main/java/com/alibaba/druid/util/DruidDataSourceUtils.java @@ -6,6 +6,7 @@ import java.util.Map; import javax.management.ObjectName; +import com.alibaba.druid.pool.DruidDataSource; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; @@ -14,6 +15,10 @@ public class DruidDataSourceUtils { private final static Log LOG = LogFactory.getLog(DruidDataSourceUtils.class); public static String getUrl(Object druidDatasource) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).getUrl(); + } + try { Method method = druidDatasource.getClass().getMethod("getUrl"); Object obj = method.invoke(druidDatasource); @@ -25,6 +30,10 @@ public class DruidDataSourceUtils { } public static ObjectName getObjectName(Object druidDatasource) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).getObjectName(); + } + try { Method method = druidDatasource.getClass().getMethod("getObjectName"); Object obj = method.invoke(druidDatasource); @@ -36,6 +45,10 @@ public class DruidDataSourceUtils { } public static Object getSqlStat(Object druidDatasource, int sqlId) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).getSqlStat(sqlId); + } + try { Method method = druidDatasource.getClass().getMethod("getSqlStat", int.class); Object obj = method.invoke(druidDatasource, sqlId); @@ -47,6 +60,10 @@ public class DruidDataSourceUtils { } public static boolean isRemoveAbandoned(Object druidDatasource) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).isRemoveAbandoned(); + } + try { Method method = druidDatasource.getClass().getMethod("isRemoveAbandoned"); Object obj = method.invoke(druidDatasource); @@ -59,6 +76,10 @@ public class DruidDataSourceUtils { @SuppressWarnings("unchecked") public static Map getStatDataForMBean(Object druidDatasource) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).getStatDataForMBean(); + } + try { Method method = druidDatasource.getClass().getMethod("getStatDataForMBean"); Object obj = method.invoke(druidDatasource); @@ -71,6 +92,10 @@ public class DruidDataSourceUtils { @SuppressWarnings("unchecked") public static Map getStatData(Object druidDatasource) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).getStatData(); + } + try { Method method = druidDatasource.getClass().getMethod("getStatData"); Object obj = method.invoke(druidDatasource); @@ -83,6 +108,10 @@ public class DruidDataSourceUtils { @SuppressWarnings({ "rawtypes" }) public static Map getSqlStatMap(Object druidDatasource) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).getSqlStatMap(); + } + try { Method method = druidDatasource.getClass().getMethod("getSqlStatMap"); Object obj = method.invoke(druidDatasource); @@ -95,6 +124,10 @@ public class DruidDataSourceUtils { @SuppressWarnings("unchecked") public static List> getPoolingConnectionInfo(Object druidDatasource) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).getPoolingConnectionInfo(); + } + try { Method method = druidDatasource.getClass().getMethod("getPoolingConnectionInfo"); Object obj = method.invoke(druidDatasource); @@ -107,6 +140,10 @@ public class DruidDataSourceUtils { @SuppressWarnings("unchecked") public static List getActiveConnectionStackTrace(Object druidDatasource) { + if (druidDatasource.getClass() == DruidDataSource.class) { + return ((DruidDataSource) druidDatasource).getActiveConnectionStackTrace(); + } + try { Method method = druidDatasource.getClass().getMethod("getActiveConnectionStackTrace"); Object obj = method.invoke(druidDatasource); diff --git a/src/main/java/com/alibaba/druid/util/JdbcSqlStatUtils.java b/src/main/java/com/alibaba/druid/util/JdbcSqlStatUtils.java index 7540df8f61..248cc9f3c1 100644 --- a/src/main/java/com/alibaba/druid/util/JdbcSqlStatUtils.java +++ b/src/main/java/com/alibaba/druid/util/JdbcSqlStatUtils.java @@ -3,6 +3,7 @@ package com.alibaba.druid.util; import java.lang.reflect.Method; import java.util.Map; +import com.alibaba.druid.stat.JdbcSqlStat; import com.alibaba.druid.support.logging.Log; import com.alibaba.druid.support.logging.LogFactory; @@ -13,6 +14,10 @@ public class JdbcSqlStatUtils { @SuppressWarnings("unchecked") public static Map getData(Object jdbcSqlStat) { try { + if (jdbcSqlStat.getClass() == JdbcSqlStat.class) { + return ((JdbcSqlStat) jdbcSqlStat).getData(); + } + Method method = jdbcSqlStat.getClass().getMethod("getData"); Object obj = method.invoke(jdbcSqlStat); return (Map) obj; From ac3f8cef2b6f9abd944d74f162f21bc1b2219ba7 Mon Sep 17 00:00:00 2001 From: wenshao Date: Sun, 12 Aug 2012 21:51:01 +0800 Subject: [PATCH 7/9] WebStatFilter support exclustions --- .../druid/support/http/WebStatFilter.java | 31 +++++++++++++------ .../com/alibaba/druid/util/DruidWebUtils.java | 23 ++++++++++++++ 2 files changed, 45 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java index 9d8799eefc..4bd0ba6110 100644 --- a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java +++ b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java @@ -42,6 +42,8 @@ public class WebStatFilter implements Filter { private Set excludesPattern; private boolean createSession = false; + private String contextPath; + @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { @@ -49,13 +51,9 @@ public class WebStatFilter implements Filter { final String requestURI = getRequestURI(httpRequest); - if (excludesPattern != null) { - for (String pattern : excludesPattern) { - if (pathMatcher.matches(pattern, requestURI)) { - chain.doFilter(request, response); - return; - } - } + if (isExclusion(requestURI)) { + chain.doFilter(request, response); + return; } long startNano = System.nanoTime(); @@ -138,7 +136,21 @@ public class WebStatFilter implements Filter { return sessionId; } - public boolean isExclusion(String uri) { + public boolean isExclusion(String requestURI) { + if (excludesPattern == null) { + return false; + } + + if (contextPath != null && requestURI.startsWith(contextPath)) { + requestURI = requestURI.substring(contextPath.length()); + } + + for (String pattern : excludesPattern) { + if (pathMatcher.matches(pattern, requestURI)) { + return true; + } + } + return false; } @@ -156,7 +168,8 @@ public class WebStatFilter implements Filter { StatFilterContext.getInstance().addContextListener(statFilterContextListener); - webAppStat = new WebAppStat(); + this.contextPath = DruidWebUtils.getContextPath(config.getServletContext()); + webAppStat = new WebAppStat(contextPath); WebAppStatManager.getInstance().addWebAppStatSet(webAppStat); } diff --git a/src/main/java/com/alibaba/druid/util/DruidWebUtils.java b/src/main/java/com/alibaba/druid/util/DruidWebUtils.java index dc5bcc9b64..c43a699f83 100644 --- a/src/main/java/com/alibaba/druid/util/DruidWebUtils.java +++ b/src/main/java/com/alibaba/druid/util/DruidWebUtils.java @@ -1,5 +1,6 @@ package com.alibaba.druid.util; +import javax.servlet.ServletContext; import javax.servlet.http.HttpServletRequest; public class DruidWebUtils { @@ -18,4 +19,26 @@ public class DruidWebUtils { return ip; } + + private static String getContextPath_2_5(ServletContext context) { + String contextPath = context.getContextPath(); + + if (contextPath == null || contextPath.length() == 0) { + contextPath = "/"; + } + + return contextPath; + } + + public static String getContextPath(ServletContext context) { + if (context.getMajorVersion() == 2 && context.getMinorVersion() < 5) { + return null; + } + + try { + return getContextPath_2_5(context); + } catch (NoSuchMethodError error) { + return null; + } + } } From c1199823a2f7b9770dfea02ec230f373d0adae8b Mon Sep 17 00:00:00 2001 From: Zhanming Qi Date: Sun, 12 Aug 2012 22:26:09 +0800 Subject: [PATCH 8/9] fix isExclusion method. --- .../java/com/alibaba/druid/support/http/WebStatFilter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java index 4bd0ba6110..ef308ef53f 100644 --- a/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java +++ b/src/main/java/com/alibaba/druid/support/http/WebStatFilter.java @@ -143,6 +143,9 @@ public class WebStatFilter implements Filter { if (contextPath != null && requestURI.startsWith(contextPath)) { requestURI = requestURI.substring(contextPath.length()); + if(!requestURI.startsWith("/")) { + requestURI = "/" + requestURI; + } } for (String pattern : excludesPattern) { From 41120f6ecba7bdf8e3127dd1be04069158250dd9 Mon Sep 17 00:00:00 2001 From: Zhanming Qi Date: Sun, 12 Aug 2012 22:27:00 +0800 Subject: [PATCH 9/9] remove jetty dependency. --- pom.xml | 91 ++------------ .../bvt/support/http/StatViewServletTest.java | 116 ------------------ 2 files changed, 7 insertions(+), 200 deletions(-) delete mode 100644 src/test/java/com/alibaba/druid/bvt/support/http/StatViewServletTest.java diff --git a/pom.xml b/pom.xml index 3dc75fe958..53c9a26e4d 100644 --- a/pom.xml +++ b/pom.xml @@ -15,7 +15,6 @@ true 4.10 - 8.1.5.v20120716 @@ -217,6 +216,13 @@ + + javax.servlet + servlet-api + 2.5 + provided + true + commons-logging commons-logging @@ -402,88 +408,5 @@ 4.1.4.Final test - - - - org.eclipse.jetty.orbit - javax.servlet - 3.0.0.v201112011016 - provided - true - - - org.eclipse.jetty.aggregate - jetty-aggregate-project - ${jetty.version} - test - pom - - - org.eclipse.jetty.aggregate - jetty-server - ${jetty.version} - test - - - org.eclipse.jetty.aggregate - jetty-client - ${jetty.version} - test - - - org.eclipse.jetty.aggregate - jetty-servlet - ${jetty.version} - test - - - org.eclipse.jetty.aggregate - jetty-webapp - ${jetty.version} - test - - - org.eclipse.jetty.aggregate - jetty-websocket - ${jetty.version} - test - - - org.eclipse.jetty.aggregate - jetty-plus - ${jetty.version} - test - - - org.eclipse.jetty.aggregate - jetty-all - ${jetty.version} - test - - - org.eclipse.jetty.aggregate - jetty-all-server - ${jetty.version} - test - - - - httpunit - httpunit - 1.7 - test - - - javax.servlet - servlet-api - - - - - rhino - rhino - 1.5R4.1 - test - diff --git a/src/test/java/com/alibaba/druid/bvt/support/http/StatViewServletTest.java b/src/test/java/com/alibaba/druid/bvt/support/http/StatViewServletTest.java deleted file mode 100644 index 1b8bc2f22f..0000000000 --- a/src/test/java/com/alibaba/druid/bvt/support/http/StatViewServletTest.java +++ /dev/null @@ -1,116 +0,0 @@ -package com.alibaba.druid.bvt.support.http; - -import static org.hamcrest.CoreMatchers.equalTo; -import static org.junit.Assert.assertThat; - -import java.net.ServerSocket; -import java.util.Map; - -import org.eclipse.jetty.server.Handler; -import org.eclipse.jetty.server.Server; -import org.eclipse.jetty.server.handler.DefaultHandler; -import org.eclipse.jetty.server.handler.HandlerList; -import org.eclipse.jetty.server.handler.ResourceHandler; -import org.eclipse.jetty.server.nio.SelectChannelConnector; -import org.eclipse.jetty.servlet.ServletContextHandler; -import org.eclipse.jetty.servlet.ServletHolder; -import org.eclipse.jetty.util.resource.Resource; -import org.eclipse.jetty.xml.XmlConfiguration; -import org.junit.After; -import org.junit.AfterClass; -import org.junit.Before; -import org.junit.BeforeClass; -import org.junit.Test; - -import com.alibaba.druid.VERSION; -import com.alibaba.druid.support.http.StatViewServlet; -import com.alibaba.fastjson.JSON; -import com.alibaba.fastjson.JSONObject; -import com.meterware.httpunit.WebConversation; -import com.meterware.httpunit.WebResponse; - -/** - * Test StatViewServlet' json test. - * - * @author Zhanming - */ -public class StatViewServletTest { - - private final static String contextPath = "/"; - private final static String servletPatternPrefix = "/druid"; - private Server server; - private int port; - - @BeforeClass - public static void setUpBeforeClass() throws Exception {} - - @AfterClass - public static void tearDownAfterClass() throws Exception {} - - @Before - public void setUp() throws Exception { - // get a port - ServerSocket s = new ServerSocket(0); - port = s.getLocalPort(); - s.close(); - - server = new Server(); - SelectChannelConnector connector = new SelectChannelConnector(); - connector.setPort(port); - server.addConnector(connector); - ResourceHandler resourceHandler = new ResourceHandler(); - resourceHandler.setWelcomeFiles(new String[] { "index.html" }); - resourceHandler.setResourceBase("."); - - ServletContextHandler contextHandler = new ServletContextHandler(); - contextHandler.setContextPath(contextPath); - // http://localhost:8080/druid/* - contextHandler.addServlet(new ServletHolder(new StatViewServlet()), servletPatternPrefix + "/*"); - - HandlerList handlers = new HandlerList(); - handlers.setHandlers(new Handler[] { resourceHandler, contextHandler, new DefaultHandler() }); - server.setHandler(handlers); - - server.start(); - // in test case, should not use join method. - // server.join(); - } - - @After - public void tearDown() throws Exception { - server.stop(); - } - - @Test - public void test_basic() throws Exception { - WebConversation wc = new WebConversation(); - String url = "http://localhost:" + port + contextPath + servletPatternPrefix + "/basic.json"; - WebResponse wr = wc.getResponse(url); - String jsonString = wr.getText(); - JSONObject json = JSON.parseObject(jsonString); - - int resultCode = json.getInteger("ResultCode"); - assertThat(1, equalTo(resultCode)); - - Map dataMap = json.getJSONObject("Content"); - assertThat(VERSION.getVersionNumber(), equalTo(dataMap.get("Version"))); - } -// -// public void test_sql() throws Exception { -// -// } - - /** - * Config an embedding Jetty Server with XML file. It's the same as setUp(). - * - * @param args - * @throws Exception - */ - public static void main(String[] args) throws Exception { - Resource statViewServerXml = Resource.newSystemResource("jetty/StatViewServer.xml"); - XmlConfiguration configuration = new XmlConfiguration(statViewServerXml.getInputStream()); - Server server = (Server) configuration.configure(); - server.start(); - server.join(); - } -}