Author: rjung
Date: Tue Aug 3 17:09:55 2010
New Revision: 981967
URL: http://svn.apache.org/viewvc?rev=981967&view=rev
Log:
Allow glob patterns in the jarsToSkip configuration and add
some debug logging to the jar scanner.
Still need to localize debug log messages.
ecj Jar in catalina.properties now uses a glob,
did not yet add globs to all the commons jars
in the default configuration.
Added:
tomcat/trunk/java/org/apache/tomcat/util/file/
tomcat/trunk/java/org/apache/tomcat/util/file/Matcher.java (with props)
Modified:
tomcat/trunk/conf/catalina.properties
tomcat/trunk/java/org/apache/tomcat/util/scan/StandardJarScanner.java
tomcat/trunk/webapps/docs/changelog.xml
Modified: tomcat/trunk/conf/catalina.properties
URL:
http://svn.apache.org/viewvc/tomcat/trunk/conf/catalina.properties?rev=981967&r1=981966&r2=981967&view=diff
==============================================================================
--- tomcat/trunk/conf/catalina.properties (original)
+++ tomcat/trunk/conf/catalina.properties Tue Aug 3 17:09:55 2010
@@ -89,7 +89,7 @@ tomcat.util.scan.DefaultJarScanner.jarsT
bootstrap.jar,commons-daemon.jar,tomcat-juli.jar,\
annotations-api.jar,el-api.jar,jsp-api.jar,servlet-api.jar,\
catalina.jar,catalina-ant.jar,catalina-ha.jar,catalina-tribes.jar,\
-jasper.jar,jasper-el.jar,ecj-3.6.jar,\
+jasper.jar,jasper-el.jar,ecj-*.jar,\
tomcat-api.jar,tomcat-util.jar,tomcat-coyote.jar,tomcat-dbcp.jar,\
tomcat-i18n-en.jar,tomcat-i18n-es.jar,tomcat-i18n-fr.jar,tomcat-i18n-ja.jar,\
commons-beanutils.jar,commons-collections.jar,commons-dbcp.jar,\
Added: tomcat/trunk/java/org/apache/tomcat/util/file/Matcher.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/file/Matcher.java?rev=981967&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/file/Matcher.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/util/file/Matcher.java Tue Aug 3
17:09:55 2010
@@ -0,0 +1,571 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+package org.apache.tomcat.util.file;
+
+import java.io.File;
+import java.util.Locale;
+import java.util.Set;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+/**
+ * <p>This is a utility class used by selectors and DirectoryScanner. The
+ * functionality more properly belongs just to selectors, but unfortunately
+ * DirectoryScanner exposed these as protected methods. Thus we have to
+ * support any subclasses of DirectoryScanner that may access these methods.
+ * </p>
+ * <p>This is a Singleton.</p>
+ */
+public final class Matcher {
+
+ /**
+ * The pattern that matches an arbitrary number of directories.
+ */
+ public static final String DEEP_TREE_MATCH = "**";
+
+ private static final Matcher instance = new Matcher();
+ private static final String OS_NAME =
+ System.getProperty("os.name").toLowerCase(Locale.ENGLISH);
+ private static final String PATH_SEP =
+ System.getProperty("path.separator");
+ private static final boolean ON_NETWARE = isNetware();
+ private static final boolean ON_DOS = isDos();
+
+ /**
+ * Private Constructor
+ */
+ private Matcher() {
+ }
+
+ /**
+ * Retrieves the instance of the Singleton.
+ * @return singleton instance
+ */
+ public static Matcher getInstance() {
+ return instance;
+ }
+
+ /**
+ * Tests whether or not a given path matches any pattern in the given set.
+ *
+ * If you need to call this method multiple times with the same
+ * pattern you should rather use TokenizedPath
+ *
+ * @see TokenizedPath
+ *
+ * @param patternSet The pattern set to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ *
+ * @return <code>true</code> if any pattern in the set matches against the
string,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean matchPath(Set<String[]> patternSet, String str) {
+ boolean result;
+ for (String[] patternTokens: patternSet) {
+ if (matchPath(patternTokens, tokenizePathAsArray(str), true)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * If you need to call this method multiple times with the same
+ * pattern you should rather use TokenizedPath
+ *
+ * @see TokenizedPath
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ *
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean matchPath(String pattern, String str) {
+ String[] patDirs = tokenizePathAsArray(pattern);
+ return matchPath(patDirs, tokenizePathAsArray(str), true);
+ }
+
+ /**
+ * Tests whether or not a given path matches a given pattern.
+ *
+ * If you need to call this method multiple times with the same
+ * pattern you should rather use TokenizedPattern
+ *
+ * @see TokenizedPattern
+ *
+ * @param pattern The pattern to match against. Must not be
+ * <code>null</code>.
+ * @param str The path to match, as a String. Must not be
+ * <code>null</code>.
+ * @param isCaseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ * @return <code>true</code> if the pattern matches against the string,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean matchPath(String pattern, String str,
+ boolean isCaseSensitive) {
+ String[] patDirs = tokenizePathAsArray(pattern);
+ return matchPath(patDirs, tokenizePathAsArray(str), isCaseSensitive);
+ }
+
+ /**
+ * Core implementation of matchPath. It is isolated so that it
+ * can be called from TokenizedPattern.
+ */
+ static boolean matchPath(String[] tokenizedPattern, String[] strDirs,
+ boolean isCaseSensitive) {
+ int patIdxStart = 0;
+ int patIdxEnd = tokenizedPattern.length - 1;
+ int strIdxStart = 0;
+ int strIdxEnd = strDirs.length - 1;
+
+ // up to first '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = tokenizedPattern[patIdxStart];
+ if (patDir.equals(DEEP_TREE_MATCH)) {
+ break;
+ }
+ if (!match(patDir, strDirs[strIdxStart], isCaseSensitive)) {
+ return false;
+ }
+ patIdxStart++;
+ strIdxStart++;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) {
+ return false;
+ }
+ }
+ return true;
+ } else {
+ if (patIdxStart > patIdxEnd) {
+ // String not exhausted, but pattern is. Failure.
+ return false;
+ }
+ }
+
+ // up to last '**'
+ while (patIdxStart <= patIdxEnd && strIdxStart <= strIdxEnd) {
+ String patDir = tokenizedPattern[patIdxEnd];
+ if (patDir.equals(DEEP_TREE_MATCH)) {
+ break;
+ }
+ if (!match(patDir, strDirs[strIdxEnd], isCaseSensitive)) {
+ return false;
+ }
+ patIdxEnd--;
+ strIdxEnd--;
+ }
+ if (strIdxStart > strIdxEnd) {
+ // String is exhausted
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ while (patIdxStart != patIdxEnd && strIdxStart <= strIdxEnd) {
+ int patIdxTmp = -1;
+ for (int i = patIdxStart + 1; i <= patIdxEnd; i++) {
+ if (tokenizedPattern[i].equals(DEEP_TREE_MATCH)) {
+ patIdxTmp = i;
+ break;
+ }
+ }
+ if (patIdxTmp == patIdxStart + 1) {
+ // '**/**' situation, so skip 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++) {
+ String subPat = tokenizedPattern[patIdxStart +
j + 1];
+ String subStr = strDirs[strIdxStart + i + j];
+ if (!match(subPat, subStr, isCaseSensitive)) {
+ continue strLoop;
+ }
+ }
+
+ foundIdx = strIdxStart + i;
+ break;
+ }
+
+ if (foundIdx == -1) {
+ return false;
+ }
+
+ patIdxStart = patIdxTmp;
+ strIdxStart = foundIdx + patLength;
+ }
+
+ for (int i = patIdxStart; i <= patIdxEnd; i++) {
+ if (!tokenizedPattern[i].equals(DEEP_TREE_MATCH)) {
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against.
+ * Must not be <code>null</code>.
+ * @param str The string which must be matched against the pattern.
+ * Must not be <code>null</code>.
+ *
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean match(String pattern, String str) {
+ return match(pattern, str, true);
+ }
+
+ /**
+ * Tests whether or not a string matches against a pattern.
+ * The pattern may contain two special characters:<br>
+ * '*' means zero or more characters<br>
+ * '?' means one and only one character
+ *
+ * @param pattern The pattern to match against.
+ * Must not be <code>null</code>.
+ * @param str The string which must be matched against the pattern.
+ * Must not be <code>null</code>.
+ * @param caseSensitive Whether or not matching should be performed
+ * case sensitively.
+ *
+ *
+ * @return <code>true</code> if the string matches against the pattern,
+ * or <code>false</code> otherwise.
+ */
+ public static boolean match(String pattern, String str,
+ boolean caseSensitive) {
+ 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 (int i = 0; i < patArr.length; i++) {
+ if (patArr[i] == '*') {
+ 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 (different(caseSensitive, 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 (true) {
+ ch = patArr[patIdxStart];
+ if (ch == '*' || strIdxStart > strIdxEnd) {
+ break;
+ }
+ if (ch != '?') {
+ if (different(caseSensitive, 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.
+ return allStars(patArr, patIdxStart, patIdxEnd);
+ }
+
+ // Process characters after last star
+ while (true) {
+ ch = patArr[patIdxEnd];
+ if (ch == '*' || strIdxStart > strIdxEnd) {
+ break;
+ }
+ if (ch != '?') {
+ if (different(caseSensitive, 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.
+ return allStars(patArr, patIdxStart, patIdxEnd);
+ }
+
+ // 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 (different(caseSensitive, 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.
+ return allStars(patArr, patIdxStart, patIdxEnd);
+ }
+
+ private static boolean allStars(char[] chars, int start, int end) {
+ for (int i = start; i <= end; ++i) {
+ if (chars[i] != '*') {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean different(
+ boolean caseSensitive, char ch, char other) {
+ return caseSensitive
+ ? ch != other
+ : Character.toUpperCase(ch) != Character.toUpperCase(other);
+ }
+
+ /**
+ * Breaks a path up into a Vector of path elements, tokenizing on
+ * <code>File.separator</code>.
+ *
+ * @param path Path to tokenize. Must not be <code>null</code>.
+ *
+ * @return a String array of path elements from the tokenized path
+ */
+ public static String[] tokenizePathAsArray(String path) {
+ String root = null;
+ if (isAbsolutePath(path)) {
+ String[] s = dissect(path);
+ root = s[0];
+ path = s[1];
+ }
+ char sep = File.separatorChar;
+ int start = 0;
+ int len = path.length();
+ int count = 0;
+ for (int pos = 0; pos < len; pos++) {
+ if (path.charAt(pos) == sep) {
+ if (pos != start) {
+ count++;
+ }
+ start = pos + 1;
+ }
+ }
+ if (len != start) {
+ count++;
+ }
+ String[] l = new String[count + ((root == null) ? 0 : 1)];
+
+ if (root != null) {
+ l[0] = root;
+ count = 1;
+ } else {
+ count = 0;
+ }
+ start = 0;
+ for (int pos = 0; pos < len; pos++) {
+ if (path.charAt(pos) == sep) {
+ if (pos != start) {
+ String tok = path.substring(start, pos);
+ l[count++] = tok;
+ }
+ start = pos + 1;
+ }
+ }
+ if (len != start) {
+ String tok = path.substring(start);
+ l[count/*++*/] = tok;
+ }
+ return l;
+ }
+
+ /**
+ * Dissect the specified absolute path.
+ * @param path the path to dissect (must be absolute).
+ * @return String[] {root, remaining path}.
+ * @throws java.lang.NullPointerException if path is null.
+ */
+ private static String[] dissect(String path) {
+ char sep = File.separatorChar;
+ path = path.replace('/', sep).replace('\\', sep);
+
+ String root = null;
+ int colon = path.indexOf(':');
+ if (colon > 0 && (ON_DOS || ON_NETWARE)) {
+
+ int next = colon + 1;
+ root = path.substring(0, next);
+ char[] ca = path.toCharArray();
+ root += sep;
+ //remove the initial separator; the root has it.
+ next = (ca[next] == sep) ? next + 1 : next;
+
+ StringBuffer sbPath = new StringBuffer();
+ // Eliminate consecutive slashes after the drive spec:
+ for (int i = next; i < ca.length; i++) {
+ if (ca[i] != sep || ca[i - 1] != sep) {
+ sbPath.append(ca[i]);
+ }
+ }
+ path = sbPath.toString();
+ } else if (path.length() > 1 && path.charAt(1) == sep) {
+ // UNC drive
+ int nextsep = path.indexOf(sep, 2);
+ nextsep = path.indexOf(sep, nextsep + 1);
+ root = (nextsep > 2) ? path.substring(0, nextsep + 1) : path;
+ path = path.substring(root.length());
+ } else {
+ root = File.separator;
+ path = path.substring(1);
+ }
+ return new String[] {root, path};
+ }
+
+ /**
+ * Verifies that the specified filename represents an absolute path.
+ * Differs from new java.io.File("filename").isAbsolute() in that a path
+ * beginning with a double file separator--signifying a Windows UNC--must
+ * at minimum match "\\a\b" to be considered an absolute path.
+ * @param filename the filename to be checked.
+ * @return true if the filename represents an absolute path.
+ * @throws java.lang.NullPointerException if filename is null.
+ */
+ private static boolean isAbsolutePath(String filename) {
+ int len = filename.length();
+ if (len == 0) {
+ return false;
+ }
+ char sep = File.separatorChar;
+ filename = filename.replace('/', sep).replace('\\', sep);
+ char c = filename.charAt(0);
+ if (!(ON_DOS || ON_NETWARE)) {
+ return (c == sep);
+ }
+ if (c == sep) {
+ // CheckStyle:MagicNumber OFF
+ if (!(ON_DOS && len > 4 && filename.charAt(1) == sep)) {
+ return false;
+ }
+ // CheckStyle:MagicNumber ON
+ int nextsep = filename.indexOf(sep, 2);
+ return nextsep > 2 && nextsep + 1 < len;
+ }
+ int colon = filename.indexOf(':');
+ return (Character.isLetter(c) && colon == 1
+ && filename.length() > 2 && filename.charAt(2) == sep)
+ || (ON_NETWARE && colon > 0);
+ }
+
+ /**
+ * Determines if our OS is Netware.
+ *
+ * @return true if we run on Netware
+ */
+ private static boolean isNetware() {
+ return OS_NAME.indexOf("netware") > -1;
+ }
+
+ /**
+ * Determines if our OS is DOS.
+ *
+ * @return true if we run on DOS
+ */
+ private static boolean isDos() {
+ return PATH_SEP.equals(";") && !isNetware();
+ }
+}
Propchange: tomcat/trunk/java/org/apache/tomcat/util/file/Matcher.java
------------------------------------------------------------------------------
svn:eol-style = native
Propchange: tomcat/trunk/java/org/apache/tomcat/util/file/Matcher.java
------------------------------------------------------------------------------
svn:keywords = Author Date Id Revision
Modified: tomcat/trunk/java/org/apache/tomcat/util/scan/StandardJarScanner.java
URL:
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/util/scan/StandardJarScanner.java?rev=981967&r1=981966&r2=981967&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/util/scan/StandardJarScanner.java
(original)
+++ tomcat/trunk/java/org/apache/tomcat/util/scan/StandardJarScanner.java Tue
Aug 3 17:09:55 2010
@@ -35,6 +35,7 @@ import org.apache.juli.logging.Log;
import org.apache.juli.logging.LogFactory;
import org.apache.tomcat.JarScanner;
import org.apache.tomcat.JarScannerCallback;
+import org.apache.tomcat.util.file.Matcher;
import org.apache.tomcat.util.res.StringManager;
/**
@@ -134,6 +135,13 @@ public class StandardJarScanner implemen
} else {
ignoredJars = jarsToSkip;
}
+ Set<String[]> ignoredJarsTokens = new HashSet<String[]>();
+ for (String pattern: ignoredJars) {
+ if (log.isDebugEnabled()) {
+ log.debug("Tokenizing " + pattern);
+ }
+ ignoredJarsTokens.add(Matcher.tokenizePathAsArray(pattern));
+ }
// Scan WEB-INF/lib
Set<String> dirList = context.getResourcePaths(Constants.WEB_INF_LIB);
@@ -141,10 +149,16 @@ public class StandardJarScanner implemen
Iterator<String> it = dirList.iterator();
while (it.hasNext()) {
String path = it.next();
+ if (log.isDebugEnabled()) {
+ log.debug("Matching path '" + path + "'");
+ }
if (path.endsWith(Constants.JAR_EXT) &&
- !ignoredJars.contains(
- path.substring(path.lastIndexOf('/')+1))) {
+ !Matcher.matchPath(ignoredJarsTokens,
+ path.substring(path.lastIndexOf('/')+1))) {
// Need to scan this JAR
+ if (log.isDebugEnabled()) {
+ log.debug("Scanning jar " + path);
+ }
URL url = null;
try {
url = context.getResource(path);
@@ -152,6 +166,10 @@ public class StandardJarScanner implemen
} catch (IOException e) {
log.warn(sm.getString("jarScan.webinflibFail", url),
e);
}
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Didn't scan jar " + path);
+ }
}
}
}
@@ -174,15 +192,26 @@ public class StandardJarScanner implemen
// Skip JARs with known not to be interesting and JARs
// in WEB-INF/lib we have already scanned
- if (!(ignoredJars.contains(jarName) ||
+ if (log.isDebugEnabled()) {
+ log.debug("Matching jar '" + jarName + "'");
+ }
+ if (jarName != null &&
+ !(Matcher.matchPath(ignoredJarsTokens, jarName) ||
urls[i].toString().contains(
Constants.WEB_INF_LIB + jarName))) {
+ if (log.isDebugEnabled()) {
+ log.debug("Scanning jar " + jarName);
+ }
try {
process(callback, urls[i]);
} catch (IOException ioe) {
log.warn(sm.getString(
"jarScan.classloaderFail",urls[i]),
ioe);
}
+ } else {
+ if (log.isDebugEnabled()) {
+ log.debug("Didn't scan jar " + jarName);
+ }
}
}
}
Modified: tomcat/trunk/webapps/docs/changelog.xml
URL:
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=981967&r1=981966&r2=981967&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Tue Aug 3 17:09:55 2010
@@ -42,6 +42,10 @@
Fix repgrssion that prevented running with a security manager enabled.
(markt)
</fix>
+ <update>
+ Allow glob patterns in the <code>jarsToSkip</code> configuration and
add
+ some debug logging to the jar scanner. (rjung)
+ </update>
</changelog>
</subsection>
<subsection name="Web applications">
---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]