1

I have a call to a HashMap's containsKey method that I expect to return true. It returns true if I compile my program to .class files and run it like that, but if I build a JAR then the same call returns false for reasons I cannot comprehend.

I've debugged both the .class and the JAR versions (the JAR using a remote connection as described in http://www.eclipsezone.com/eclipse/forums/t53459.html ) and in both cases the HashMap appears to contain the Key I'm trying to check.

The HashMap uses URI objects as keys. Here are the contents of the variables as shown in each debug session:

When run as a .class file

HashMap Key: java.net.URI = file:/E:/SSD%20App%20Libraries/Google%20Drive/Programming/Bet%20Matching/Java%20Sim/target/classes/simfiles/paytables/

URIToCheck: java.net.URI = file:///E:/SSD%20App%20Libraries/Google%20Drive/Programming/Bet%20Matching/Java%20Sim/target/classes/simfiles/paytables/

Result: GameTreeItemsMap.containsKey(URIToCheck) is true

When run as a JAR

HashMap Key: java.net.URI = jar:file:/E:/SSD%20App%20Libraries/Google%20Drive/Programming/Bet%20Matching/Java%20Sim/out/artifacts/JavaSim_jar/Java%20Sim.jar!/simfiles/paytables/

URIToCheck: java.net.URI = jar:file:///E:/SSD%2520App%2520Libraries/Google%2520Drive/Programming/Bet%2520Matching/Java%2520Sim/out/artifacts/JavaSim_jar/Java%2520Sim.jar!/simfiles/paytables/

Result: GameTreeItemsMap.containsKey(URIToCheck) is false

I would expect the method to return true in both cases. Do URIs somehow behave differently inside a JAR? What's going on?

Thanks in advance for any help!

Edit 1 As was pointed out to me, the URIToCheck in the JAR case is being double encoded (%2520 instead of %20). Here's the code that generates the URIToCheck. I use the walkFileTree method.

        Files.walkFileTree(paytableHomePath, new SimpleFileVisitor<Path>(){
            @Override
            public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) {

                Path parentPath = dir.getParent();

                URI parentURIToCheck = parentPath.toUri();

                boolean testContain = GameTreeItemsMap.containsKey(parentURIToCheck);

                return FileVisitResult.CONTINUE;
            }

In the JAR case, the URI parentURIToCheck is double encoded (with %5250 for a space where there should be %20) and in the .class case this does not happen. Any idea why?

  • 1
    Looks like you are encoding the URI twice in the JAR case: http://stackoverflow.com/questions/16084935/a-html-space-is-showing-as-2520-instead-of-20 – user3707125 Jul 02 '15 at 01:43
  • Please edit your question to include the code which creates the URI instances. – VGR Jul 02 '15 at 01:52
  • Oh, thanks a lot! I can't believe I missed that. I've updated my original post to show how the URI is generated, but I'm still confused as to why double encoding occurs in the JAR case but not in the .class case. –  Jul 02 '15 at 21:34
  • Is there any data I could provide on the Path variables that might help? –  Jul 02 '15 at 22:02
  • Pointless title. You need to look for bugs that are actually possible. It isn't possible for HashMap to behave differently according to whether the app is in a JAR file, but it is possible that you didn't put the same data *into* it for some reason associated with that. And the chances of a JDK bug versus a bug in your own newly written code are infinitesimal. Don't waste time chasing chimera. – user207421 Jul 02 '15 at 22:15

1 Answers1

1

This has nothing to do with HashMap. It appears you've found a bug in Java's Zip File System Provider, namely that it converts a Path to a doubly-encoded URI.

I can't find an existing bug for it, so I've submitted one. (There is this related bug, whose fix I suspect is the cause of this one.) Update: This is Java bug 8131067.

Here's the program I wrote to demonstrate the problem, in Java 1.8.0_45-b14. Pass a .jar file with one or more spaces in its path as the first command line argument.

import java.util.Map;
import java.util.Collections;
import java.net.URI;
import java.io.IOException;
import java.nio.file.FileSystem;
import java.nio.file.FileSystems;
import java.nio.file.Path;
import java.nio.file.Paths;

public class JarPathTest {
    public static void main(String[] args)
    throws IOException {

        Path zip = Paths.get(args[0]);

        URI zipURI = URI.create("jar:" + zip.toUri());
        System.out.println(zipURI);

        Map<String, String> env = Collections.emptyMap();

        try (FileSystem fs = FileSystems.newFileSystem(zipURI, env)) {
            Path root = fs.getPath("/");
            System.out.println(root.toUri());
        }
    }
}

You can work around it by treating the decoded URI as if it's still percent encoded:

parentURIToCheck = URI.create(
    parentURIToCheck.getScheme() + ":" +
    parentURIToCheck.getSchemeSpecificPart());
VGR
  • 40,506
  • 4
  • 48
  • 63
  • Thank you for the answer! Can I ask for a link to the bug report you submitted so I can track its progress? –  Jul 07 '15 at 15:49
  • Also - the workaround you posted doesn't work in itself for me. I also run the application from the .class files, where there are no double-encoding issues with the URIs, so adding those lines messes that up. The best I've found is to do something like: `String[] splitURI = uri.toString().split("!"); if (splitURI.length == 2) { // assumes we are running from a JAR and adjusts URI appropriately }` Because of how the JAR URIs have a '!' in them. Do you think there is a better test I could use? –  Jul 07 '15 at 15:55
  • 1
    A final comment with something that may be of use. It is only the part before the `!` that is double-encoded, and not the part after it. (This isn't visible in my example because there is no space after the ! in the example I showed). For example, here is what happens if there is one: `jar:file:///E:/SSD%2520App%2520Libraries/Google%2520Drive/Programming/Bet%2520Matching/Java%2520Sim/out/artifacts/JavaSim_jar/Java%2520Sim.jar!/simfiles/paytables/video%20poker` –  Jul 07 '15 at 16:25
  • As long as the URI is a `jar:` URI, I think looking for the first `!` is reliable. I would use `split("!", 2)` to be safe. I'll certainly post the URL of the bug, once I know it; knowing Oracle's usual speed of processing bug reports, I expect it to take days if not weeks to show up. – VGR Jul 07 '15 at 17:55