2014-10-02 24 views
5

Tóm tắt:tải một lọ từ một chương trình Java chạy gây ra một NoClassDefFoundError do ClassNotFoundException do phụ thuộc liên lớp (ví dụ import báo cáo). Làm thế nào tôi có thể vượt qua nó?tải một lọ trong thời gian chạy gây ra một NoClassDefFoundError/ClassNotFoundException

Vấn đề chi tiết hơn:

Tôi đang cố gắng để lập trình tải một file jar - chúng ta hãy gọi nó là "máy chủ" - vào Java Virtual Machine thông qua chương trình Java của riêng tôi - hãy gọi nó "ServerAPI" - và sử dụng phần mở rộng và một số thủ thuật khác để thay đổi hành vi và tương tác với Server. ServerAPI phụ thuộc vào Server, nhưng nếu Server không có mặt, ServerAPI vẫn phải có khả năng chạy và download Server từ một website.

Để tránh các lỗi do ServerAPI gây ra mà không đáp ứng các phụ thuộc của nó từ Máy chủ, tôi đã khởi chạy - hãy gọi nó là "Trình khởi chạy" - dự định tải xuống Máy chủ và thiết lập ServerAPI khi cần thiết, sau đó tải Máy chủ và ServerAPI, sau đó chạy ServerAPI.

Tuy nhiên, khi tôi cố gắng tải bình từ Trình khởi chạy, tôi gặp phải lỗi do Trình nạp lớp không thể giải quyết các lớp khác trong tệp mà lớp đang tải phụ thuộc vào. Tóm lại, nếu tôi cố gắng tải Class A, nó sẽ ném một lỗi nếu A nhập B vì tôi chưa tải B. Tuy nhiên, nếu B cũng nhập A, tôi bị kẹt vì tôi không thể tìm ra cách tải hai lớp cùng một lúc hoặc cách tải một lớp mà không có JVM chạy xác thực của nó.

Tại sao tất cả các hạn chế đã dẫn tôi đến vấn đề này:

Tôi đang cố gắng để sửa đổi và bổ sung vào hành vi của Server, nhưng vì lý do pháp lý phức tạp, tôi không thể sửa đổi chương trình trực tiếp, vì vậy tôi có tạo ServerAPI phụ thuộc vào và có thể tinh chỉnh hành vi của Server từ bên ngoài.

Tuy nhiên, vì các lý do pháp lý phức tạp hơn, Server và ServerAPI không thể tải xuống đơn giản cùng nhau. Trình khởi chạy (xem ở trên) phải được tải xuống với ServerAPI, khi đó Trình khởi chạy cần tải xuống Máy chủ. Cuối cùng, ServerAPI có thể chạy bằng Server như một phụ thuộc. Đó là lý do tại sao vấn đề này quá phức tạp.

Vấn đề này cũng sẽ áp dụng cho một phần sau của dự án, sẽ liên quan đến giao diện API dựa trên plugin cần có khả năng tải và gỡ bỏ các plugin khỏi tệp jar trong khi chạy.

nghiên cứu tôi đã thực hiện về vấn đề này:

Tôi đã đọc qua và thất bại trong việc được giúp đỡ bởi:

  • this question, mà chỉ đề cập đến các vấn đề của một phương pháp duy nhất và không lỗi phụ thuộc địa chỉ lớp;
  • this question, sẽ không hoạt động vì tôi không thể tắt và khởi động lại chương trình mỗi lần bình được tải hoặc dỡ (chủ yếu cho phần plugin tôi đã đề cập ngắn gọn);
  • this question, chỉ hoạt động cho các trường hợp phụ thuộc có mặt khi chương trình bắt đầu;
  • this question, có cùng vấn đề với # 2;
  • this question, có cùng vấn đề như # 3;
  • this article, từ đó tôi đã học về loadClass ẩn (String, boolean) phương pháp, nhưng cố gắng với đúng giá trị sai không giúp;
  • this question, có cùng vấn đề với # 1;

và hơn thế nữa. Không có gì có hiệu quả.

// EDIT: nỗ lực tôi đã thực hiện cho đến nay:

Tôi đã cố gắng sử dụng URLClassLoaders để tải jar sử dụng JarEntries từ JarFile tương tự như this question. Tôi đã thử điều này bằng cách sử dụng và gọi phương thức loadClass(String) của URLClassLoader và bằng cách tạo một lớp mở rộng URLClassLoader để tôi có thể sử dụng loadClass(String, boolean resolve) để cố gắng buộc ClassLoader giải quyết tất cả các lớp mà nó tải. Cả hai cách, tôi đã nhận lỗi này giống nhau:

I couldn't find the class in the JarEntry! 
entry name="org/apache/logging/log4j/core/appender/db/jpa/converter/ContextMapAttributeConverter.class" 
class name="org.apache.logging.log4j.core.appender.db.jpa.converter.ContextMapAttributeConverter" 
java.lang.NoClassDefFoundError: javax/persistence/AttributeConverter 
    at java.lang.ClassLoader.defineClass1(Native Method) 
    at java.lang.ClassLoader.defineClass(ClassLoader.java:760) 
    at java.security.SecureClassLoader.defineClass(SecureClassLoader.java:142) 
    at java.net.URLClassLoader.defineClass(URLClassLoader.java:455) 
    at java.net.URLClassLoader.access$100(URLClassLoader.java:73) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:367) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 
    at Corundum.launcher.CorundumClassLoader.load(CorundumClassLoader.java:52) 
    at Corundum.launcher.CorundumLauncher.main(CorundumLauncher.java:47) 
Caused by: java.lang.ClassNotFoundException: javax.persistence.AttributeConverter 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:372) 
    at java.net.URLClassLoader$1.run(URLClassLoader.java:361) 
    at java.security.AccessController.doPrivileged(Native Method) 
    at java.net.URLClassLoader.findClass(URLClassLoader.java:360) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:424) 
    at java.lang.ClassLoader.loadClass(ClassLoader.java:357) 
    ... 12 more 

// END EDIT

// EDIT 2:

Dưới đây là một mẫu mã mà tôi sử dụng để tải một trong khi cố gắng giải quyết nó. Đây là một lớp học mà tôi đã thực hiện để mở rộng URLClassLoader. Trên dòng bắt đầu bằng Class<?> clazz = loadClass(, tôi đã thử dùng true và false làm đối số boolean; cả hai lần thử đều dẫn đến cùng một lỗi ở trên.

public boolean load(ClassLoadAction class_action, FinishLoadAction end_action) { 
    // establish the jar associated with this ClassLoader as a JarFile 
    JarFile jar; 
    try { 
     jar = new JarFile(jar_path); 
    } catch (IOException exception) { 
     System.out.println("There was a problem loading the " + jar_path + "!"); 
     exception.printStackTrace(); 
     return false; 
    } 

    // load each class in the JarFile through its JarEntries 
    Enumeration<JarEntry> entries = jar.entries(); 

    if (entries.hasMoreElements()) 
     for (JarEntry entry = entries.nextElement(); entries.hasMoreElements(); entry = entries.nextElement()) 
      if (!entry.isDirectory() && entry.getName().endsWith(".class")) 
       try { 
        /* this "true" in the line below is the whole reason this class is necessary; it makes the URLClassLoader this class extends "resolve" the class, 
        * meaning it also loads all the classes this class refers to */ 
        Class<?> clazz = loadClass(entry.getName().substring(0, entry.getName().length() - 6).replaceAll("/", "."), true); 
        class_action.onClassLoad(this, jar, clazz, end_action); 
       } catch (ClassNotFoundException | NoClassDefFoundError exception) { 
        try { 
         close(); 
        } catch (IOException exception2) { 
         System.out.println("There was a problem closing the URLClassLoader after the following " + exception2.getClass().getSimpleName() + "!"); 
         exception.printStackTrace(); 
        } 
        try { 
         jar.close(); 
        } catch (IOException exception2) { 
         System.out.println("There was a problem closing the JarFile after the following ClassNotFoundException!"); 
         exception.printStackTrace(); 
        } 
        System.out.println("I couldn't find the class in the JarEntry!\nentry name=\"" + entry.getName() + "\"\nclass name=\"" 
          + entry.getName().substring(0, entry.getName().length() - 6).replaceAll("/", ".") + "\""); 
        exception.printStackTrace(); 
        return false; 
       } 

    // once all the classes are loaded, close the ClassLoader and run the plugin's main class(es) load() method(s) 
    try { 
     jar.close(); 
    } catch (IOException exception) { 
     System.out.println("I couldn't close the URLClassLoader used to load this jar file!\njar file=\"" + jar.getName() + "\""); 
     exception.printStackTrace(); 
     return false; 
    } 

    end_action.onFinishLoad(this, null, class_action); 
    System.out.println("loaded " + jar_path); 
    // TODO TEST 
    try { 
     close(); 
    } catch (IOException exception) { 
     System.out.println("I couldn't close the URLClassLoader used to load this jar file!\njar file=\"" + jar_path + "\""); 
     exception.printStackTrace(); 
     return false; 
    } 
    return true; 
} 

// END EDIT 2

Tôi nhận ra rằng phải có một giải pháp đơn giản này, nhưng đối với cuộc sống của tôi, tôi dường như không thể tìm thấy nó. Bất kỳ sự giúp đỡ nào sẽ làm tôi mãi mãi biết ơn. Cảm ơn bạn.

+0

Bạn đã xác nhận từ dấu vết ngăn xếp rằng thực tế bản thân quá trình nhập đang gây ra sự cố và không (nói) truy cập vô ý của lớp khác từ một hàm tạo hoặc biến tĩnh? (Và nếu nó thực sự là nhập khẩu, điều gì sẽ xảy ra nếu bạn loại bỏ việc nhập và đủ điều kiện tất cả các tham chiếu đối tượng đến lớp thay thế?) Cuối cùng, bạn có thể đăng tải cách bạn đang nạp jar không? –

+0

Trong các trường hợp như thế này, có thể hữu ích khi mô phỏng một ví dụ rất đơn giản với hai lớp không có gì trong các lọ riêng biệt và tìm hiểu xem chúng có cùng vấn đề khi bạn tải chúng hay không. Nếu không, hãy tiếp tục tăng độ phức tạp của mô hình cho đến khi nó phản ánh các mẫu được sử dụng trong ứng dụng thực tế của bạn. Ví dụ, tôi đã nhận thấy (trong môi trường OSGi) rằng trình nạp lớp thích phân giải tất cả các lớp được tham chiếu theo cách này hay cách khác từ lớp mà tôi tải trực tiếp, nhưng nó không cố gắng giải quyết các lớp được tham chiếu trong một lớp. các lớp được gỡ bỏ từng bước cho đến khi chúng được gọi. –

+0

(Trong trường hợp đó, thêm một lớp shim giữa các lớp nguồn và đích là đủ để làm cho trình nạp lớp hạnh phúc, miễn là lớp shim không bao giờ được gọi cho đến khi lớp đích được nạp.) –

Trả lời

2

Thật lạ lùng, tôi nhận thấy câu trả lời là thông báo lỗi đã nói sự thật.javax.persistence.AttributeConverter, lớp mà trình tải đã tuyên bố không có mặt, không có trong bình.

Tôi đã khắc phục sự cố bằng cách chỉ tải lớp chính và ClassLoader tất cả các lớp tham chiếu, về cơ bản tải tất cả các lớp trong lọ được sử dụng trong chương trình, đó là tất cả những gì tôi cần.

Bây giờ, tôi có thể thề rằng tôi đã kiểm tra điều này trước đây và thấy rằng lớp học; Tôi hình tôi đã thực sự đã kiểm tra kho lưu trữ nguồn mở Apache cho lớp chứ không phải là Máy chủ thực tế khi tôi kiểm tra điều đó. Tôi không thể nhớ.

Trong mọi trường hợp, thiếu AttributeConverter. Tôi không biết làm thế nào hoặc tại sao họ quản lý để biên dịch một cái lọ với phụ thuộc bị thiếu, nhưng tôi đoán các quy trình chính của họ không bao giờ sử dụng một phần của mã, vì vậy nó không bao giờ ném lỗi.

Tôi xin lỗi vì đã lãng phí thời gian của mọi người ... kể cả của riêng tôi. Tôi đã bị mắc kẹt về vấn đề này trong một thời gian.

đạo đức của câu chuyện này:

Nếu bạn đang cố gắng để tải một jar thực thi, đừng bận tâm tải tất cả các lớp trong một cái lọ, trừ khi bạn thực sự phải. Chỉ cần tải lớp chính; sẽ tải mọi thứ mà chương trình cần chạy.

// EDIT:

bây giờ tôi đã bắt đầu có những lỗi tương tự, nhưng nó không xuất hiện cho đến khi tôi cố gắng để gọi một phương thức từ một lớp được nạp. Câu hỏi dường như vẫn mở. Xin vui lòng downvote và bỏ qua câu trả lời này.

Các vấn đề liên quan