Index: stripes/src/net/sourceforge/stripes/util/ResolverUtil.java
===================================================================
--- stripes/src/net/sourceforge/stripes/util/ResolverUtil.java	(revision 1156)
+++ stripes/src/net/sourceforge/stripes/util/ResolverUtil.java	(working copy)
@@ -15,10 +15,11 @@
 package net.sourceforge.stripes.util;
 
 import java.io.File;
-import java.io.FileInputStream;
 import java.io.IOException;
 import java.lang.annotation.Annotation;
 import java.net.URL;
+import java.net.MalformedURLException;
+import java.net.URISyntaxException;
 import java.util.Enumeration;
 import java.util.HashSet;
 import java.util.Set;
@@ -213,30 +214,43 @@
             return this;
         }
 
-        while (urls.hasMoreElements()) {
-            String urlPath = urls.nextElement().getFile();
-            urlPath = StringUtil.urlDecode(urlPath);
+        try {
+            while (urls.hasMoreElements()) {
+                URL currentUrl = urls.nextElement();
 
-            // If it's a file in a directory, trim the stupid file: spec
-            if ( urlPath.startsWith("file:") ) {
-                urlPath = urlPath.substring(5);
-            }
+                if ( currentUrl.getProtocol().equals("file") ) {
+                    // It's a file URL, which means it's a directory
+                    loadImplementationsInDirectory(test, packageName, new File(currentUrl.toURI()));
+                }
+                else {
+                    // Otherwise, we're looking at a directory in a jar file.
+                    // Ultimately, we want a URL to the jar file.
 
-            // Else it's in a JAR, grab the path to the jar
-            if (urlPath.indexOf('!') > 0) {
-                urlPath = urlPath.substring(0, urlPath.indexOf('!'));
-            }
+                    if ( currentUrl.getProtocol().equals("jar")) {
+                        // Jar URLs are weird.  We want the file URL that's
+                        // embedded in the jar URL.  This just strips the
+                        // jar: off the URL, and the next bit takes the
+                        // internal path.
+                        currentUrl = new URL(currentUrl.toString()
+                                .replaceFirst("^jar:", ""));
+                    }
 
-            log.info("Scanning for classes in [", urlPath, "] matching criteria: ", test);
-            File file = new File(urlPath);
-            if ( file.isDirectory() ) {
-                loadImplementationsInDirectory(test, packageName, file);
+                    // Strip /package/name from the end of the url along with potential
+                    // delimiters
+                    currentUrl = new URL(currentUrl.toString()
+                            .replaceFirst("!?/" + packageName + "/?$", ""));
+
+                    log.info("Scanning for classes in [", currentUrl, "] matching criteria: ", test);
+                    loadImplementationsInJar(test, packageName, currentUrl);
+                }
             }
-            else {
-                loadImplementationsInJar(test, packageName, file);
-            }
         }
-        
+        catch (MalformedURLException e) {
+            log.warn(e, "Could not parse malformed URL");
+        } catch (URISyntaxException e) {
+            log.warn(e, "Could not parse malformed URI");
+        }
+
         return this;
     }
 
@@ -285,13 +299,13 @@
      *
      * @param test a Test used to filter the classes that are discovered
      * @param parent the parent package under which classes must be in order to be considered
-     * @param jarfile the jar file to be examined for classes
+     * @param jarfile the URL of the jar file to be examined for classes
      */
-    private void loadImplementationsInJar(Test test, String parent, File jarfile) {
+    private void loadImplementationsInJar(Test test, String parent, URL jarfile) {
 
         try {
             JarEntry entry;
-            JarInputStream jarStream = new JarInputStream(new FileInputStream(jarfile));
+            JarInputStream jarStream = new JarInputStream(jarfile.openStream());
 
             while ( (entry = jarStream.getNextJarEntry() ) != null) {
                 String name = entry.getName();
@@ -330,4 +344,4 @@
                      t.getClass().getName(), " with message: ", t.getMessage());
         }
     }
-}
\ No newline at end of file
+}
Index: tests/src/net/sourceforge/stripes/util/ResolverUtilTest.java
===================================================================
--- tests/src/net/sourceforge/stripes/util/ResolverUtilTest.java	(revision 1156)
+++ tests/src/net/sourceforge/stripes/util/ResolverUtilTest.java	(working copy)
@@ -1,5 +1,6 @@
 package net.sourceforge.stripes.util;
 
+import net.sourceforge.stripes.extensions.MyIntegerTypeConverter;
 import net.sourceforge.stripes.validation.BooleanTypeConverter;
 import net.sourceforge.stripes.validation.DateTypeConverter;
 import net.sourceforge.stripes.validation.LocalizableError;
@@ -26,13 +27,16 @@
         resolver.findImplementations(TypeConverter.class, "net");
         Set<Class<? extends TypeConverter<?>>> impls = resolver.getClasses();
 
-        // Check on a few random converters
+        // Check on a few random converters in a jar
         Assert.assertTrue(impls.contains(BooleanTypeConverter.class),
                           "BooleanTypeConverter went missing.");
         Assert.assertTrue(impls.contains(DateTypeConverter.class),
                           "DateTypeConverter went missing.");
         Assert.assertTrue(impls.contains(BooleanTypeConverter.class),
                           "ShortTypeConverter went missing.");
+        // Check on a random converter in a class directory
+        Assert.assertTrue(impls.contains(MyIntegerTypeConverter.class),
+                          "MyIntegerTypeConverter went missing");
 
         Assert.assertTrue(impls.size() >= 10,
                           "Did not find all the built in TypeConverters.");
@@ -46,13 +50,15 @@
         resolver.findImplementations(TypeConverter.class, "net.sourceforge.stripes.validation");
         Set<Class<? extends TypeConverter<?>>> impls = resolver.getClasses();
 
-        // Check on a few random converters
+        // Check on a few random converters in a jar
         Assert.assertTrue(impls.contains(BooleanTypeConverter.class),
                           "BooleanTypeConverter went missing.");
         Assert.assertTrue(impls.contains(DateTypeConverter.class),
                           "DateTypeConverter went missing.");
         Assert.assertTrue(impls.contains(BooleanTypeConverter.class),
                           "ShortTypeConverter went missing.");
+        Assert.assertFalse(impls.contains(MyIntegerTypeConverter.class),
+                          "MyIntegerTypeConverter was found and should not have been");
 
         Assert.assertTrue(impls.size() >= 10,
                           "Did not find all the built in TypeConverters.");
@@ -73,7 +79,7 @@
                           "SimpleError itself should have been found.");
     }
 
-    /** Test interface used with the testFindZeroImplementatios() method. */
+    /** Test interface used with the testFindZeroImplementations() method. */
     private static interface ZeroImplementations {}
 
     @Test(groups="fast")

