Tentukan dari mana file JAR berasal

154

Saya tidak di depan IDE sekarang, hanya melihat spesifikasi API.

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

Saya ingin menentukan dari mana file JAR berasal. Apakah ini cara untuk melakukannya?

chrisaycock
sumber
2
Apakah ada cara untuk melakukan ini dari konsol? Sesuatu seperti java -findjar -cp /some/path/with/libs/*.jar my.java.Class-> my.jar.
kub1x

Jawaban:

191

Iya. Ini bekerja untuk semua kelas kecuali kelas yang dimuat oleh bootstrap classloader. Cara lain untuk menentukan adalah:

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

Sebagai notnoop menunjukkan klass.getResource()metode mengembalikan lokasi file kelas itu sendiri. Sebagai contoh:

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

The getProtectionDomain().getCodeSource().getLocation()Metode mengembalikan lokasi file jar atau CLASSPATH

file:/Users/home/java/libs/ejb3-persistence-1.0.2.GA.jar
file:/projects/classes
Chandra Patni
sumber
Ini membuat asumsi tentang pemetaan dari nama kelas ke file kelas. Apakah ini akan berfungsi dengan baik untuk kelas anonim? Kelas bersarang?
Thorbjørn Ravn Andersen
1
Ini menunjuk ke url kelas bukan toples itu sendiri. Url perlu diurai untuk menemukan file jar.
notnoop
@tidak. Saya sudah mengklarifikasi jawabannya.
Chandra Patni
19
Justru sebaliknya seperti yang tertulis dalam jawaban, gunakan getProtectionDomain().getCodeSource().getLocation()jika Anda ingin mendapatkan lokasi file jar
peter
Terima kasih atas jawaban ini, itu mengilhami saya untuk menjawab pertanyaan ini .
kriegaex
12

Lihat LiveInjector.findPathJar()dari Lombok PatcherLiveInjector.java . Perhatikan bahwa ini merupakan kasus khusus di mana file tersebut tidak benar-benar hidup dalam toples, dan Anda mungkin ingin mengubahnya.

/**
 * 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.");
    }
}
tidak ada
sumber
2
Ini tampaknya terlalu rumit untuk mendapatkan sesuatu yang sangat sederhana. Saya hanya duduk dan mencoba apa yang saya temukan sebelumnya, dan tampaknya berhasil. Hanya ingin beberapa validasi.
Baik. Kode Anda tidak menangani file di jalur bootclass, dan solusi Chandra mengembalikan url ke file bukan ke file jar, jadi Anda harus mengurai path untuk menemukan file jar.
notnoop
2

Menggunakan

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

Jika ini berisi beberapa entri maka lakukan beberapa operasi substring.

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


}
Mengenakan
sumber
Kode di atas akan menemukan sumber daya apa pun di jalur. Jika dalam sebuah Jar akan menemukan jar mencetak ukuran & tanggal jar dan ukuran dan tanggal sumber daya dalam jar
Don