views:

412

answers:

2

Hi all,

I am not in front of an IDE right now, just looking at the API specs ...

CodeSource src = MyClass.class.getProtectionDomain().getCodeSource();
if (src != null) {
  URL jar = src.getLocation();

I want to determine which jar file a class is from, is this the way to do it?

Walter

+3  A: 

Yes. It works for all classes except classes loaded by bootstrap classloader. The other way to determine is:

Class klass = String.class;
URL location = klass.getResource('/'+klass.getName().replace('.', '/')+".class");

As notnoop pointed out getProtectionDomain().getCodeSource().getLocation() method returns the location of the class file itself. For example:

jar:file:/jdk/jre/lib/rt.jar!/java/lang/String.class
file:/projects/classes/pkg/MyClass$1.class

The klass.getResource() method returns the location of the jar file or CLASSPATH

file:/Users/home/java/libs/ejb3-persistence-1.0.2.GA.jar
file:/projects/classes
Chandra Patni
This makes assumptions abut the mapping from class name to class file. Will it work properly for anonymous classes? Nested classes?
Thorbjørn Ravn Andersen
This points to the url of the class not the jar itself. The url needs to be parsed to find the jar file.
notnoop
@notnoop. I have clarified the answer.
Chandra Patni
+3  A: 

Checkout the LiveInjector.findPathJar() from Lombok Patcher LiveInjector.java. Note that it special cases where the file doesn't actually live in a jar, and you might want to change that.

/**
 * If the provided class has been loaded from a jar file that is on the local file system, will find the absolute path to that jar file.
 * 
 * @param context The jar file that contained the class file that represents this class will be found. Specify {@code null} to let {@code LiveInjector}
 *                find its own jar.
 * @throws IllegalStateException If the specified class was loaded from a directory or in some other way (such as via HTTP, from a database, or some
 *                               other custom classloading device).
 */
public static String findPathJar(Class<?> context) throws IllegalStateException {
    if (context == null) context = LiveInjector.class;
    String rawName = context.getName();
    String classFileName;
    /* rawName is something like package.name.ContainingClass$ClassName. We need to turn this into ContainingClass$ClassName.class. */ {
        int idx = rawName.lastIndexOf('.');
        classFileName = (idx == -1 ? rawName : rawName.substring(idx+1)) + ".class";
    }

    String uri = context.getResource(classFileName).toString();
    if (uri.startsWith("file:")) throw new IllegalStateException("This class has been loaded from a directory and not from a jar file.");
    if (!uri.startsWith("jar:file:")) {
        int idx = uri.indexOf(':');
        String protocol = idx == -1 ? "(unknown)" : uri.substring(0, idx);
        throw new IllegalStateException("This class has been loaded remotely via the " + protocol +
                " protocol. Only loading from a jar on the local file system is supported.");
    }

    int idx = uri.indexOf('!');
    //As far as I know, the if statement below can't ever trigger, so it's more of a sanity check thing.
    if (idx == -1) throw new IllegalStateException("You appear to have loaded this class from a local jar file, but I can't make sense of the URL!");

    try {
        String fileName = URLDecoder.decode(uri.substring("jar:file:".length(), idx), Charset.defaultCharset().name());
        return new File(fileName).getAbsolutePath();
    } catch (UnsupportedEncodingException e) {
        throw new InternalError("default charset doesn't exist. Your VM is borked.");
    }
}
notnoop
This seems overly complicated to get something very simple. I just sat down and tried what I found earlier, and it appears to work. Just wanted some validation.
Well. Your code doesn't handle files in bootclass paths, and Chandra's solution returns the url to the file not to the jar file, so you'll need to parse the path to find the jar file.
notnoop