2011-12-23 37 views
6

Tôi đang viết một ứng dụng (cụ thể là một plugin cho máy chủ Minecraft Bukkit). Làm điều này yêu cầu tôi truy cập tệp .properties từ JAR của ứng dụng. Đây là nơi tôi gặp phải một vấn đề lạ. Khi tôi kiểm tra chương trình trên PC phát triển của tôi, nó chạy tốt. Tệp .properties được tải và mọi thứ đều ổn. Tuy nhiên, trên máy tính khác mà tôi thử nghiệm trên, tôi cố gắng khởi động ứng dụng và không thể tải các thuộc tính và InputStreamnull. Dưới đây là phương pháp trong đó tôi tải các file:Không thể truy cập tài nguyên trong một JAR trên tất cả các máy tính

public class Points { 
    private HashMap<String, MessageFormat> messages; 

    public Points() { 
     buildMessages(); 
    } 

public static void buildMessages() { 
     Properties messageProps = new Properties(); 
     InputStream in = Points.class.getResourceAsStream("resources/messages.properties"); 
     messages = new HashMap<String, MessageFormat>(); 
     Enumeration en; 
     try { 
      messageProps.load(in); 
     } catch(IOException ex) { 
      System.err.println("Couldn't read message properties file!"); 
      return; 
     } catch(NullPointerException ex) { 
      System.err.println("Couldn't read message properties file!"); 
      if(in == null) 
       System.out.println("IOStream null"); 
      return; 
     } 
     en = messageProps.propertyNames(); 
     while(en.hasMoreElements()) { 
      String key = (String)en.nextElement(); 
      String prop = messageProps.getProperty(key); 
      MessageFormat form = new MessageFormat(prop.replaceAll("&", 
       "\u00a7").replaceAll("`", "")); 
      messages.put(key, form); 
     } 
    } 
} 

tôi đã bỏ qua một số mã không liên quan, nhưng đó là ý chính của nó. Cấu trúc của JAR là như sau:

com/ 
     pvminecraft/ 
      points/ 
       Points.java <-- The class where the file is loaded 
       resources/ 
        messages.properties <-- The file being loaded 

Trên PC của tôi tập tin được tải từ resources/messages.properties, nhưng vào file khác, InputStream là null, và catch khối của tôi cho NullPointerException được chạy. Điều gì có thể gây ra sự cố và làm cách nào để khắc phục sự cố? Cảm ơn.

Cập nhật: Ngay cả khi sử dụng đường dẫn đầy đủ (/com/pvminecraft/points/resources/messages.properties), vấn đề tương tự vẫn còn dai dẳng.

Cập nhật 2: Đây là full stack trace:

java.lang.NullPointerException 
    at java.util.Properties$LineReader.readLine(Properties.java:435) 
    at java.util.Properties.load0(Properties.java:354) 
    at java.util.Properties.load(Properties.java:342) 
    at com.pvminecraft.points.Points.buildMessages(Unknown Source) 
    at com.pvminecraft.points.Points.onEnable(Unknown Source) 
    at org.bukkit.plugin.java.JavaPlugin.setEnabled(JavaPlugin.java:188) 
    at org.bukkit.plugin.java.JavaPluginLoader.enablePlugin(JavaPluginLoader.java:968) 
    at org.bukkit.plugin.SimplePluginManager.enablePlugin(SimplePluginManager.java:280) 
    at org.bukkit.craftbukkit.CraftServer.loadPlugin(CraftServer.java:186) 
    at org.bukkit.craftbukkit.CraftServer.enablePlugins(CraftServer.java:169) 
    at org.bukkit.craftbukkit.CraftServer.reload(CraftServer.java:436) 
    at org.bukkit.Bukkit.reload(Bukkit.java:187) 
    at org.bukkit.command.defaults.ReloadCommand.execute(ReloadCommand.java:22) 
    at org.bukkit.command.SimpleCommandMap.dispatch(SimpleCommandMap.java:165) 
    at org.bukkit.craftbukkit.CraftServer.dispatchCommand(CraftServer.java:378) 
    at org.bukkit.craftbukkit.CraftServer.dispatchCommand(CraftServer.java:374) 
    at net.minecraft.server.MinecraftServer.b(MinecraftServer.java:564) 
    at net.minecraft.server.MinecraftServer.w(MinecraftServer.java:541) 
    at net.minecraft.server.MinecraftServer.run(MinecraftServer.java:425) 
    at net.minecraft.server.ThreadServerApplication.run(SourceFile:457) 

Tất cả những thứ org.bukkitorg.craftbukkit là máy chủ. Tệp .properties được tải theo phương thức buildMessages, được gọi theo phương thức onEnable của Points.

Cập nhật 3: Trên bản cài đặt mới của Arch Linux, tệp thuộc tính thư được tải chính xác và tất cả đều tốt. Máy chủ từ xa là Ubuntu Linux và máy tính cá nhân của tôi là Arch.

Cập nhật 4: Được rồi, đây là loại phân giải. Nó có vẻ là một vấn đề địa phương hóa. Tôi nói rằng vì tôi đã quản lý để có được quyền truy cập vào hai máy tính khác, và chương trình chạy một cách chính xác trên cả hai. Trong khi nó gây phiền nhiễu, điều này không có vẻ là bất cứ điều gì sai trái với mã của tôi hoặc xây dựng kịch bản. Tôi vẫn muốn biết có gì sai, nhưng nó không phải là bức xúc nữa. Tôi sẽ tiếp tục xem xét điều này. Cảm ơn mọi người.

+0

Thư mục "tài nguyên" không nằm trên đường dẫn lớp - xem câu trả lời của Tom. Tuy nhiên, thư mục "com/pvminecraft/points/resources" là. Kiểm tra môi trường phát triển của bạn - Tôi tin rằng bạn đã có hoặc là classpath thiết lập funny hoặc một bản sao của các tập tin thuộc tính ngồi một nơi nào đó mà là trên classpath. – Paul

+0

Vui lòng đăng toàn bộ dấu vết ngăn xếp. – Paul

+0

số dòng ở đây là gì: 'at com.pvminecraft.points.Points.buildMessages (Unknown Source)'. Phiên bản JDK của bạn là gì? – Kowser

Trả lời

2

Có vẻ như sự tinh tế nhỏ giữa các trình nạp lớp Java khác nhau và đường dẫn tìm kiếm của chúng. Trước khi đi sâu vào các chi tiết này; tại sao bạn không thử đường dẫn đầy đủ trong tệp jar này? (ví dụ: một cái gì đó như thế này:

Points.class.getResourceAsStream("com/pvminecraft/points/resources/messages.properties"); 

)

+3

Đường dẫn đầy đủ bắt đầu bằng "/" –

+0

Đó là cùng một câu chuyện với thay đổi đó. Hoạt động trên PC của tôi, nhưng không phải là máy tính khác. –

+0

@MichaelSmith - làm thế nào bạn chạy nó trên máy tính của bạn? – Paul

2

Point.class.getClassLoader().getResourceAsStream("com/pvminecraft/points/resources/messages.properties");

Hãy thử nó mà không là người đầu tiên '/' và nó sẽ làm việc bất cứ nơi nào chạy một JVM.

Nếu điều đó không hiệu quả, hãy thử đặt tệp trong ROOT của tệp JAR và thử lại.

Nếu vẫn không hoạt động, hãy thử sử dụng phương pháp này:

public static byte[] getFile(File zip, String fileName) throws FileNotFoundException, ZipException, IOException { 
     String filename = fileName; 

     if (!zip.exists()) { 
      throw new FileNotFoundException(zip.getName()); 
     } 
     while (filename.charAt(0) == '/' || filename.charAt(0) == '\\') { 
      filename = filename.substring(1); 
     } 

     if (filename.contains("\\")) { 
      filename = filename.replace("\\", "/"); 
     } 

     ZipFile zipFile = new ZipFile(zip); 
     Enumeration entries = zipFile.entries(); 

     ByteArrayOutputStream output; 
     byte[] result = null; 

     while (entries.hasMoreElements()) { 
      ZipEntry entry = (ZipEntry) entries.nextElement(); 

      if (entry.getName().equalsIgnoreCase(filename)) { 
       FileUtils.copyInputStream(zipFile.getInputStream(entry), output = new ByteArrayOutputStream()); 
       result = output.toByteArray(); 
       zipFile.close(); 
       output.close(); 
       return result; 
      } 
     } 

     zipFile.close(); 
     throw new FileNotFoundException(filename); 
    } 

Bạn sẽ cần điều này

public static void copyInputStream(InputStream in, OutputStream out) throws IOException { 
    byte[] buffer = new byte[1024]; 
    int len; 
    while (((len = in.read(buffer)) >= 0)) { 
     out.write(buffer, 0, len); 
    } 
    out.flush(); 
} 

Lấy đường dẫn của jar chạy

String currentJar = ""; 
             // Get current jar path. Since user may rename this file, we need to do this way 
       try { 
        currentJar = (Points.class.getProtectionDomain().getCodeSource().getLocation().toURI().getPath()); 
        if (currentJar.startsWith("/")) currentJar = currentJar.substring(1); 
        } catch (URISyntaxException ex) { 
        } 

Đầu tiên '/' Tôi không thực sự nhớ tại sao nó xuất hiện, nhưng nó làm, vì vậy bạn phải loại bỏ nó:

Cuối cùng gọi phương thức: getFile(currentJar, "PATH_TO_PROPERTIES_FILE");

Bạn sẽ có một mảng byte để làm việc. Chỉ cần đặt nó như là một ByteArrayInputStream và các vấn đề của bạn nên được giải quyết.


đang Đó là một phần của một lớp util tôi đã tạo ra, đó là lý do tại sao hưởng ứng nhiệt liệt đọc cho một mảng byte, nhưng OFC, bạn có thể thay đổi nó để sử dụng trực tiếp InputStream đó đến Properties.load() phương pháp .

liên kết cho lớp ZIP util

http://all-inhonmodman.svn.sourceforge.net/viewvc/all-inhonmodman/ModManager/src/modmanager/utility/ZIP.java?revision=292&content-type=text%2Fplain

liên kết cho FileUtils util lớp

http://all-inhonmodman.svn.sourceforge.net/viewvc/all-inhonmodman/ModManager/src/modmanager/utility/FileUtils.java?revision=294&content-type=text%2Fplain

0

Bạn cũng có thể muốn chắc chắn nếu bạn xây dựng kịch bản (Ant, Maven) hoặc IDE của bạn không loại bỏ/di chuyển các messages.properties đó (coz nó không phải là .class) từ tệp JAR kết quả. Bạn có thể kiểm tra nội dung JAR của mình bằng cách sử dụng các công cụ như 7zip hoặc WinZip.

0

Vì bạn đang tải tài nguyên trong cùng một gói của lớp học (Point), bạn không cần sử dụng đường dẫn tuyệt đối để tải nó.

Máy chủ có sử dụng bất kỳ loại bộ nhớ cache nào để tải các plugin không? Điều này có thể được gây ra bởi một phiên bản cũ hơn của các plugin jar đang có mặt trong đường dẫn lớp. Để xác minh rằng máy chủ đang thực sự tải phiên bản đúng của tệp jar, bạn có thể thử triển khai phiên bản plugin của bạn để ghi nhật ký gì đó vào bảng điều khiển và xem có điều gì xảy ra không (nếu thông báo được ghi nhật ký).

Ngoài ra, tôi không biết cách phân cấp trình nạp lớp được tổ chức trong máy chủ, nhưng bạn có thể thử tải tài nguyên từ trình tải lớp chuỗi hiện tại (thường là trình nạp lớp gốc của cha mẹ, sẽ tìm tài nguyên đó trong mọi trình nạp lớp con khác). Bạn sẽ phải sử dụng đường dẫn tuyệt đối cho điều đó.

ClassLoader rootCL = Thread.currentThread().getContextClassLoader(); 
InputStream resource = rootCL.getResourceAsStream(
     "/com/pvminecraft/points/resources/messages.properties"); 

Kiểm tra điều này question để tìm hiểu thêm về trình tải lớp khác nhau.

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