3

I have a library foo.jar that contains an svnversion.properties file (just looks like svnversion=12345M or whatever) and the following static method in a class SVNVersion:

public static SVNVersion fromString(String s) { ... }
public static SVNVersion fromResources(Class<?> cl) {
    ResourceBundle svnversionResource = ResourceBundle.getBundle(
        "svnversion", Locale.getDefault(), cl.getClassLoader());
    if (svnversionResource.containsKey("svnversion"))
    {
        String svnv = svnversionResource.getString("svnversion");
        return fromString(svnv);
    }
    else
    {
        return null;
    }
}

I also have a library bar.jar that contains an svnversion.properties file as well (let's say it contains svnversion=789).

However, when I run the following within a class SomeClassInBarJar that's in bar.jar:

SVNVersion vfoo = SVNVersion.fromResources(SVNVersion.class);
SVNVersion vbar = SVNVersion.fromResources(SomeClassInBarJar.class);

and I print the result, I see 789 twice. Clearly I am not doing this right. How do I get the right svnversion.properties file in the root of the jar file containing a given class? (assuming it's there)


edit: I just tried

InputStream is = cl.getResourceAsStream("/svnversion.properties");

and it has the same problem. I seem to be able only to get access to the main jar file's /svnversion.properties and not the library's /svnversion.properties.

Jason S
  • 184,598
  • 164
  • 608
  • 970

1 Answers1

2

You obviously cannot use this approach, as whatever svnversion.properties file comes will always be used the classloader. It is the same behaviour you would see for classes: if two classes with the same name are on the classpath, it’s whatever comes first that is used.

A (convoluted) approach would be to find out what jar a class belongs to, and then retrieve svnversion.properties in that jar:

public static JarFile getJarFile(Class<?> cl) {
    URL classUrl = cl.getResource(cl.getSimpleName() + ".class");
    if (classUrl != null) {
        try {
            URLConnection conn = classUrl.openConnection();
            if (conn instanceof JarURLConnection) {
                JarURLConnection connection = (JarURLConnection) conn;
                return connection.getJarFile();
            }
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }
    return null;
}

public static SVNVersion fromResources(Class<?> cl) {
    JarFile jarFile = getJarFile(cl);
    ZipEntry entry = jarFile.getEntry("svnversion.properties");
    Properties props = new Properties();
    try {
        props.load(jarFile.getInputStream(entry));
    } catch (IOException e) {
        throw new RuntimeException(e);
    }

    if (props.containsKey("svnversion")) {
        String svnv = props.getProperty("svnversion");
        return fromString(svnv);
    } else {
        return null;
    }
}

That’s why, IMHO, you’d probably be better off storing the svn version number in the class themselves as final static variables (and using svn $Revision$ keyword).

Sébastien Le Callonnec
  • 26,254
  • 8
  • 67
  • 80
  • svn keyword substitution e.g. $Revision$ applies only to individual files; I want the result of running svnversion.exe on my entire checkout. Anyway the question is not just about svn versions but about getting resources from library jars vs. application jars. Thanks for the other suggestions. – Jason S Jan 14 '11 at 18:56
  • 1
    I did get that, but clearly you’re misunderstanding the way classloading works. Unless you load the different jars in different classloaders, you’ll never manage to achieve what you want. – Sébastien Le Callonnec Jan 14 '11 at 18:59
  • "but clearly you’re misunderstanding the way classloading works." -- I'm not misunderstanding classloading at all. What I was misunderstanding was the way getResource() and getResourceStream() and getResourceBundle() worked in conjunction with classloaders. – Jason S Jan 14 '11 at 19:29