Déterminer de quel fichier JAR provient une classe

154

Je ne suis pas devant un IDE pour le moment, je regarde juste les spécifications de l'API.

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

Je veux déterminer de quel fichier JAR provient une classe. Est-ce la manière de procéder?

chrisaycock
la source
2
Existe-t-il un moyen de le faire à partir de la console? Quelque chose comme java -findjar -cp /some/path/with/libs/*.jar my.java.Class-> my.jar.
kub1x

Réponses:

191

Oui. Cela fonctionne pour toutes les classes sauf les classes chargées par le chargeur de classe bootstrap. L'autre façon de déterminer est:

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

Comme notnoop l'a souligné, la klass.getResource()méthode renvoie l'emplacement du fichier de classe lui-même. Par exemple:

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

La getProtectionDomain().getCodeSource().getLocation()méthode renvoie l'emplacement du fichier jar ou CLASSPATH

file:/Users/home/java/libs/ejb3-persistence-1.0.2.GA.jar
file:/projects/classes
Chandra Patni
la source
Cela fait des hypothèses sur le mappage du nom de classe au fichier de classe. Cela fonctionnera-t-il correctement pour les classes anonymes? Des classes imbriquées?
Thorbjørn Ravn Andersen
1
Cela pointe vers l'url de la classe et non vers le fichier jar lui-même. L'URL doit être analysée pour trouver le fichier jar.
notnoop
@notnoop. J'ai clarifié la réponse.
Chandra Patni
19
C'est exactement l'inverse comme il est écrit dans la réponse, utilisez getProtectionDomain().getCodeSource().getLocation()si vous voulez obtenir l'emplacement du fichier jar
peter
Merci pour cette réponse, cela m'a inspiré pour répondre à cette question .
kriegaex
12

Vérifiez le LiveInjector.findPathJar()de Lombok PatcherLiveInjector.java . Notez qu'il s'agit de cas particuliers où le fichier ne vit pas réellement dans un fichier jar, et vous voudrez peut-être changer cela.

/**
 * 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
la source
2
Cela semble trop compliqué pour obtenir quelque chose de très simple. Je me suis juste assis et j'ai essayé ce que j'ai trouvé plus tôt, et cela semble fonctionner. Je voulais juste une validation.
Bien. Votre code ne gère pas les fichiers dans les chemins bootclass, et la solution de Chandra renvoie l'URL du fichier et non le fichier jar, vous devrez donc analyser le chemin pour trouver le fichier jar.
notnoop
2

Utilisation

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

Si cela contient plusieurs entrées, effectuez une opération de sous-chaîne.

Abhishek Chatterjee
la source
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
la source
Le code ci-dessus trouvera n'importe quelle ressource sur le chemin. Si dans un pot trouvera le pot, imprimez la taille et la date du pot ainsi que la taille et la date de la ressource dans le pot
Don le