Unable to access resource in JAR on all computers

I am writing an application (in particular, a plugin for the Bukkit Minecraft server). This requires access to the .properties file from the application JAR. Here I ran into some strange problem. When I test the program on my development computer, it works just fine. The .properties file loads, and everything is fine. However, on another computer on which I am testing it, I am trying to run the application and it cannot load properties, and InputStream is null . Here is the file upload method:

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

I have omitted some irrelevant code, but this is its essence. The structure of the JAR is as follows:

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

On my PC, the file is downloaded from resources/messages.properties , but in another InputStream file is null, and my catch for NullPointerException fired. What could be causing the problem, and how can I fix it? Thanks.

Update: Even using the full path ( /com/pvminecraft/points/resources/messages.properties ), the same problem still persists.

Update 2: Here is the 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) 

All elements of org.bukkit and org.craftbukkit are the server. The .properties file is loaded into the buildMessages method, called by the onEnable Points method.

Update 3: On a new installation of Arch Linux, the message properties file loads correctly, and everything is fine. The remote server is Ubuntu Linux, and my dev computer is Arch.

Update 4: Ok, this is a kind of permission. This seems to be a localized issue. I say this because I managed to access two more computers, and the program works correctly on both. Although this is annoying, it doesn't seem like something is wrong with my code or assembly scripts. I still want to know what happened, but he no longer insists. I will continue to study this. Thanks to everyone.

+6
source share
4 answers

It looks like a little subtlety between the various Java class loaders and their search paths. Before moving on to these details; why don't you try the full path in this jar file? (e.g. something like this:

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

)

+2
source

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

Try without the first "/", and it should work anywhere in the JVM.

If this does not work, try placing the file in the ROOT of the JAR file and try again.

If still not working, try using this method:

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

You will need

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

Get the way to a working bank

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

The first "/" I really donโ€™t remember why it appears, but it is, so you should remove it:

Finally, call the method: getFile(currentJar, "PATH_TO_PROPERTIES_FILE");

You will have an array of bytes to work with. Just put it as ByteArrayInputStream and your problems should be solved.


This code is part of the utility class that I created, so it is unnecessary to read into a byte array, but cc, you can change it to directly use this InputStream for the Properties.load () method.

Link to the ZIP utility class

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

Link to the utility class FileUtils

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

+2
source

You can also make sure that your build script (Ant, Maven) or your IDE did not remove / move these .properties (not from the .class class) from the resulting JAR file. You can check the contents of the JAR with tools such as 7zip or WinZip.

0
source

Since you are loading a resource in the same package of your class ( Point ), you do not need to use an absolute path to load it.

Does the server use any cache to load plugins? This may be due to the fact that an earlier version of the plugin banner is present in the class path. To make sure that the server is really downloading the correct version of the jar file, you can try deploying a version of your plugin that writes something to the console and sees something happening (if the message is actually logged).

In addition, I donโ€™t know how the hierarchy of the class loader is organized on the server, but you can try to load the resource from the current stream class loader (this is usually the root loader of the parent class that will look for the resource in every other child class loader). You will need to use the absolute path for this.

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

Check out the question to learn more about different class loaders.

0
source

Source: https://habr.com/ru/post/904421/


All Articles