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?


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

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.");
    }
}

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.


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();
    }


}

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
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.