Bestimmen Sie, aus welcher JAR-Datei eine Klasse stammt

154

Ich bin gerade nicht vor einer IDE, sondern schaue mir nur die API-Spezifikationen an.

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

Ich möchte feststellen, aus welcher JAR-Datei eine Klasse stammt. Ist das der Weg, es zu tun?

Chrisaycock
quelle
2
Gibt es eine Möglichkeit, dies von der Konsole aus zu tun? So etwas wie java -findjar -cp /some/path/with/libs/*.jar my.java.Class-> my.jar.
Kub1x

Antworten:

191

Ja. Es funktioniert für alle Klassen außer Klassen, die vom Bootstrap-Klassenladeprogramm geladen werden. Der andere Weg zu bestimmen ist:

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

Wie notnoop hervorhob, klass.getResource()gibt die Methode den Speicherort der Klassendatei selbst zurück. Beispielsweise:

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

Die getProtectionDomain().getCodeSource().getLocation()Methode gibt den Speicherort der JAR-Datei oder von CLASSPATH zurück

file:/Users/home/java/libs/ejb3-persistence-1.0.2.GA.jar
file:/projects/classes
Chandra Patni
quelle
Dadurch werden Annahmen über die Zuordnung vom Klassennamen zur Klassendatei getroffen. Funktioniert es ordnungsgemäß für anonyme Klassen? Verschachtelte Klassen?
Thorbjørn Ravn Andersen
1
Dies zeigt auf die URL der Klasse, nicht auf das Glas selbst. Die URL muss analysiert werden, um die JAR-Datei zu finden.
Notnoop
@notnoop. Ich habe die Antwort geklärt.
Chandra Patni
19
Es ist genau umgekehrt, wie es in der Antwort geschrieben steht. Verwenden getProtectionDomain().getCodeSource().getLocation()Sie diese Option, wenn Sie den Speicherort der JAR-Datei ermitteln möchten
Peter,
Vielen Dank für diese Antwort, es hat mich inspiriert, diese Frage zu beantworten .
Kriegaex
12

Kasse der LiveInjector.findPathJar()von Lombok PatcherLiveInjector.java . Beachten Sie, dass es sich um Sonderfälle handelt, in denen die Datei nicht in einem Glas gespeichert ist, und dass Sie dies möglicherweise ändern möchten.

/**
 * 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
quelle
2
Dies scheint zu kompliziert, um etwas sehr Einfaches zu bekommen. Ich habe mich einfach hingesetzt und versucht, was ich zuvor gefunden habe, und es scheint zu funktionieren. Ich wollte nur eine Bestätigung.
Gut. Ihr Code verarbeitet keine Dateien in Bootklassenpfaden, und Chandras Lösung gibt die URL an die Datei zurück, nicht an die JAR-Datei. Daher müssen Sie den Pfad analysieren, um die JAR-Datei zu finden.
Notnoop
2

Verwenden

String path = <Any of your class within the jar>.class.getProtectionDomain().getCodeSource().getLocation().getPath(); 

Wenn dies mehrere Einträge enthält, führen Sie eine Teilzeichenfolgenoperation aus.

Abhishek Chatterjee
quelle
1
private String resourceLookup(String lookupResourceName) {



    try {

        if (lookupResourceName == null || lookupResourceName.length()==0) {
            return "";
        }
        // "/java/lang/String.class"

        // Check if entered data was in java class name format
        if (lookupResourceName.indexOf("/")==-1) {
            lookupResourceName = lookupResourceName.replaceAll("[.]", "/");
            lookupResourceName =  "/" + lookupResourceName + ".class";
        }

        URL url = this.getClass().getResource(lookupResourceName);
        if (url == null) {
            return("Unable to locate resource "+ lookupResourceName);

        }

        String resourceUrl = url.toExternalForm();

        Pattern pattern =
            Pattern.compile("(zip:|jar:file:/)(.*)!/(.*)", Pattern.CASE_INSENSITIVE);

        String jarFilename = null;
        String resourceFilename = null;
        Matcher m = pattern.matcher(resourceUrl);
        if (m.find()) {
            jarFilename = m.group(2);
            resourceFilename = m.group(3);
        } else {
            return "Unable to parse URL: "+ resourceUrl;

        }

        if (!jarFilename.startsWith("C:") ){
          jarFilename = "/"+jarFilename;  // make absolute path on Linux
        }

        File file = new File(jarFilename);
        Long jarSize=null;
        Date jarDate=null;
        Long resourceSize=null;
        Date resourceDate=null;
        if (file.exists() && file.isFile()) {

            jarSize = file.length();
            jarDate = new Date(file.lastModified());

            try {
                JarFile jarFile = new JarFile(file, false);
                ZipEntry entry = jarFile.getEntry(resourceFilename);
                resourceSize = entry.getSize();
                resourceDate = new Date(entry.getTime());
            } catch (Throwable e) {
                return ("Unable to open JAR" + jarFilename + "   "+resourceUrl +"\n"+e.getMessage());

            }

           return "\nresource: "+resourceFilename+"\njar: "+jarFilename + "  \nJarSize: " +jarSize+"  \nJarDate: " +jarDate.toString()+"  \nresourceSize: " +resourceSize+"  \nresourceDate: " +resourceDate.toString()+"\n";


        } else {
            return("Unable to load jar:" + jarFilename+ "  \nUrl: " +resourceUrl);

        }
    } catch (Exception e){
        return e.getMessage();
    }


}
Don
quelle
Der obige Code findet jede Ressource auf dem Pfad. Wenn in einem Glas das Glas gefunden wird, drucken Sie die Größe und das Datum des Glases sowie die Größe und das Datum der Ressource im Glas
Don