I'm writing an application (specifically a plugin for the Bukkit Minecraft server). Doing this requires that I access a .properties file from the JAR of the application. This is where I encounter a strange problem. When I test the program on my development PC, it runs just fine. The .properties file gets loaded and everything is fine. However, on the other computer that I test it on, I try to start the app, and it can't loaded the properties, and the InputStream
is null
. Here is the method in which I load the file:
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've omitted some irrelevant code, but that is the gist of it. 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 loaded from resources/messages.properties
, but on the other file, the InputStream
is null, and my catch
block for the NullPointerException
is run. What could be causing the problem, and how could I fix it? Thanks.
Update: Even using the full path (/com/pvminecraft/points/resources/messages.properties
), the same issue is still persistent.
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 of the org.bukkit
and org.craftbukkit
stuff is the server. The .properties file is loaded in the buildMessages
method, called by the onEnable
method of Points
.
Update 3: On a fresh install of Arch Linux, the message properties file is loaded correctly and all is well. The remote server is Ubuntu Linux, and my dev PC is Arch.
Update 4: Alright, this is sort of a resolution. It seems to be a localized problem. I say that because I've managed to get access to two more computers, and the program runs correctly on both. While it's annoying, this doesn't seem to be anything wrong with my code or build scripts. I'm still wanting to know what's wrong, but it isn't pressing any more. I'll continue looking into this. Thanks everyone.
Open File Explorer and the folder that includes your JarFile. Right-click the Jar file and select Open with then Choose another app. Select Java if it's listed among the default programs.
No, resources work perfectly well in a JAR. Just don't treat a resource as a File, because it isn't one. Instead, use the getResourceAsStream() method and you'll have an InputStream from which you can read the contents of the resource.
Seems like minor subtleties between the different Java class loaders and their search paths. Before going into these details; why don't you try the full path within this jar file? (i.e. something like this:
Points.class.getResourceAsStream("com/pvminecraft/points/resources/messages.properties");
)
Point.class.getClassLoader().getResourceAsStream("com/pvminecraft/points/resources/messages.properties");
Try it without the first '/' and it should work anywhere running a JVM.
If that didn't work, please try to put the file in the ROOT of the JAR file and try it again.
If still doesn't work, 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 this
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 path of the running jar
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 don't really remember why it appears, but it do, so you must 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 a ByteArrayInputStream and your problems should be solved.
That code is part of a util class I've created, that's why the unecessary read to a byte array, but ofc, you can change it to use directly that InputStream to the Properties.load() method.
Link for the ZIP util class
http://all-inhonmodman.svn.sourceforge.net/viewvc/all-inhonmodman/ModManager/src/modmanager/utility/ZIP.java?revision=292&content-type=text%2Fplain
Link for the FileUtils util class
http://all-inhonmodman.svn.sourceforge.net/viewvc/all-inhonmodman/ModManager/src/modmanager/utility/FileUtils.java?revision=294&content-type=text%2Fplain
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With