2011-12-23 11 views
6

Estoy escribiendo una aplicación (específicamente un complemento para el servidor Bukkit Minecraft). Hacer esto requiere que acceda a un archivo .properties desde el JAR de la aplicación. Aquí es donde encuentro un extraño problema. Cuando pruebo el programa en mi PC de desarrollo, funciona bien. El archivo .properties se carga y todo está bien. Sin embargo, en la otra computadora en la que lo pruebo, intento iniciar la aplicación y no puede cargar las propiedades, y el InputStream es null. Aquí está el método en el que me carga el archivo:No se puede acceder al recurso en un JAR en todas las computadoras

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

he omitido algún código irrelevante, pero que es la esencia de la misma. La estructura del JAR es el siguiente:

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

En mi PC se carga el archivo de resources/messages.properties, pero por otro archivo, el InputStream es nula, y está dirigido mi bloque catch para la NullPointerException. ¿Qué podría estar causando el problema y cómo podría solucionarlo? Gracias.

Actualización: Incluso utilizando la ruta completa (/com/pvminecraft/points/resources/messages.properties), el mismo problema persiste.

Actualización 2: Aquí está la pila-traza completa:

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) 

todas las cosas y org.bukkitorg.craftbukkit es el servidor. El archivo .properties se carga en el método buildMessages, llamado por el método onEnable de Points.

Actualización 3: En una nueva instalación de Arch Linux, el archivo de propiedades del mensaje se carga correctamente y todo está bien. El servidor remoto es Ubuntu Linux, y mi PC de desarrollo es Arch.

Actualización 4: Muy bien, esto es una especie de resolución. Parece ser un problema localizado. Lo digo porque he logrado acceder a dos computadoras más y el programa se ejecuta correctamente en ambas. Si bien es molesto, esto no parece ser nada malo con mi código o compilación de scripts. Todavía estoy queriendo saber qué está mal, pero ya no está presionando. Continuaré investigando esto. Gracias a todos.

+0

El directorio "recursos" no se encuentra en el classpath; consulte la respuesta de Tom. El directorio "com/pvminecraft/points/resources" es, sin embargo. Comprueba tu entorno de desarrollo: creo que tienes el classpath divertido o una copia de los archivos de propiedades en algún lugar de la classpath. – Paul

+0

Por favor, publique todo el seguimiento de la pila. – Paul

+0

cuál es el número de línea aquí: 'at com.pvminecraft.points.Points.buildMessages (fuente desconocida)'. ¿Cuál es tu versión JDK? – Kowser

Respuesta

2

Parece que hay pequeñas sutilezas entre los diferentes cargadores de clase Java y sus rutas de búsqueda. Antes de entrar en estos detalles; ¿Por qué no prueba la ruta completa dentro de este archivo jar? (es decir, algo como esto:

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

)

+3

Las rutas completas comienzan con "/" –

+0

Es la misma historia con ese cambio. Funciona en mi PC, pero no en la otra. –

+0

@MichaelSmith: ¿cómo lo está ejecutando en su computadora? – Paul

2

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

Pruébalo sin el primer '/' y debería funcionar dondequiera que se ejecuta una JVM.

Si eso no funcionó, intente poner el archivo en la RAÍZ del archivo JAR e intente de nuevo.

Si todavía no funciona, trate de usar este método:

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

que necesitará esta

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

obtener la ruta de la jarra corriendo

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) { 
        } 

La primera '/' Realmente no recuerdo por qué aparece, pero sí, así que debes eliminarlo:

Finalmente llame al método: getFile(currentJar, "PATH_TO_PROPERTIES_FILE");

Tendrá una matriz de bytes para trabajar. Simplemente póngalo como ByteArrayInputStream y sus problemas deben ser resueltos.


Ese código es parte de una clase util que he creado, por eso el innecesario leer a una matriz de bytes, pero OFC, puede cambiarlo a utilizar directamente que InputStream a la Properties.load() método .

Enlace para la clase postal util

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

Enlace para los FileUtils util clase

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

0

También es posible que desee asegurarse de si su escritura de la estructura (Ant, Maven) o su IDE no eliminó/reubicó ese messages.properties (porque no es .class) del archivo JAR resultante. Puede verificar los contenidos de su JAR utilizando herramientas como 7zip o WinZip.

0

Dado que está cargando un recurso que está en el mismo paquete de su clase (Point), no necesita utilizar la ruta absoluta para cargarlo.

¿El servidor utiliza algún tipo de caché para cargar los complementos? Esto podría deberse a que una versión anterior del archivo jar del complemento esté presente en la ruta de la clase. Para verificar que el servidor está realmente cargando la versión correcta del archivo jar, puede intentar implementar una versión de su complemento que registra algo en la consola y ver si sucede algo (si el mensaje se registra).

Además, no sé cómo se organiza la jerarquía del cargador de clases en el servidor, pero podría intentar cargar el recurso desde el cargador de clases de hilo actual (generalmente el cargador de clases principal raíz, que buscará el recurso en cualquier otro cargador de clases de niños). Tendría que usar la ruta absoluta para eso.

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

Marque esta question para aprender más sobre los cargadores de clases diferentes.

Cuestiones relacionadas