();
@@ -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/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/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