开发者

Is there a way to get, at runtime, the version of Guava in use?

I did a quick grep of the Guava source and documentation, and neither seem to have any mention of versions. I was wondering if there was a way Guava's version information can be obtained at runtime.

This version information doesn't have to be accessible via any getter, if no such thing actually exists; if it's stashed in a field somewhere that doesn't get GC'd while Guava is loaded, that would be sufficient.

Is this version information available anywhere at runtime?


I have a very specific use for this. A big part of my work is analysing Java heap dumps to identify and fix places in the code that cause exorbitant memory usage. For this task, I use fasthat, a heavily-modified version of jhat with special features useful to my work.

One of those features is to display the contents of containers. I've already implemented this for the likes of ArrayList, HashMap, ConcurrentHashMap, etc. (I implement type printers on de开发者_开发技巧mand, based on what I encounter in our heap dumps.) Currently, I'm implementing a type printer for Guava's CustomConcurrentHashMap.

Since the layout of structures can change between versions, my code tweaks its unpacking behaviour based on what version is in use. For example, at work, we used to use JRuby 1.2, but recently switched to JRuby 1.6, so I have type printers for both of those versions, and it selects the version based on the version information it finds in the heap dump.

So, that's the point of the second paragraph of the question: if the version information is anywhere in the heap dump, that's all I need.

And before anyone asks: heap dump data is not "live", so you cannot simply call toString or the like. You really have to walk the data structures to extract the bits out, and you really do have to use implementation details to the nth degree. ;-)


If you want to get the version of a maven built class, you can start with the class, find the jar it comes from and read the meta information which maven adds (such as the version)

A simpler way to do this, if the version is in the path, is to look at your classpath, find guava and the version you are using from the file name.

For a heap dump, the class path is in a System property.


This is more of a workaround, but I guess you could do this if there is no simpler way of accessing the Guava version:

In most Guava releases, classes / fields / methods are added / removed. You could try to look for them in the heap dump, and, depending on their existence, determine the Guava version.

Something like:

/**
 * A {@link Predicate} that checks whether a class exists in the given {@link HeapDump}
 */
public class ClassExistsPredicate implements Predicate<String> {

    private final HeapDump heapDump;

    public ClassExistsPredicate(HeapDump heapDump) {
        this.heapDump = heapDump;
    }

    @Override
    public boolean apply(String fullyQualifiedClassName) {
        // checks whether the given class exists in the heap dump
        return true;
    }

}


public enum GuavaVersion {

    R01,

    R02 {
        @Override
        Set<String> getAddedClasses() {
            return ImmutableSet.of("com.google.common.base.Foo");
        }
    },

    R03 {
        @Override
        Set<String> getAddedClasses() {
            return ImmutableSet.of("com.google.common.collect.ForwardingFooIterator");
        }
    },

    R04 {
        @Override
        Set<String> getAddedClasses() {
            return ImmutableSet.of("com.google.common.collect.FooFoo2");
        }
    };

    /**
     * @return a {@link Set} of added class names that uniquely identify this version from the preceding one (not
     *         necessarily <b>all</b> classes!)
     */
    Set<String> getAddedClasses() {
        return ImmutableSet.of();
    }

    public static GuavaVersion getGuavaVersionFor(HeapDump heapDump) {
        ClassExistsPredicate classExists = new ClassExistsPredicate(heapDump);

        for (GuavaVersion version : Lists.reverse(Arrays.asList(GuavaVersion.values()))) {
            if (Iterables.all(version.getAddedClasses(), classExists)) {
                return version;
            }
        }

        throw new RuntimeException("Unable to determine Guava version...");
    }

}

Obviously, you should cache the Guava version number, since the computation might be slow... The approach could be extended to consider added methods / fields.

This approach could work with other projects, too.


You can retrieve the version from Guava's JAR manifest.

public static String getGuavaVersion() {
    try {
        File file = new File(Charsets.class.getProtectionDomain().getCodeSource().getLocation().toURI());
        try (JarFile jar = new JarFile(file)) {
            return jar.getManifest().getMainAttributes().getValue("Bundle-Version");
        }
    } catch (Exception ex) {
        throw new RuntimeException("Unable to get the version of Guava", ex);
    }
}

Unfortunately, this works only for Guava 11+. Guava 10 and older were not OSGI bundles yet.

Another option is to retrieve the version from pom.properties. This works for old versions of Guava too: https://gist.github.com/seanizer/8de050427f3f199cf8f085b2a3a2473e

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜