Не удается получить доступ к ресурсу в JAR на всех компьютерах - PullRequest
6 голосов
/ 24 декабря 2011

Я пишу приложение (в частности, плагин для сервера Bukkit Minecraft). Для этого требуется доступ к файлу .properties из JAR приложения. Здесь я сталкиваюсь со странной проблемой. Когда я тестирую программу на своем ПК, она работает нормально. Файл .properties загружается и все в порядке. Однако на другом компьютере, на котором я тестирую его, я пытаюсь запустить приложение, и оно не может загрузить свойства, а InputStream равно null. Вот метод, которым я загружаю файл:

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

Я опустил какой-то нерелевантный код, но в этом суть. Структура JAR выглядит следующим образом:

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

На моем компьютере файл загружается из resources/messages.properties, но в другом файле InputStream равен нулю, и мой блок catch для NullPointerException запускается. Что может быть причиной проблемы, и как я могу это исправить? Спасибо.

Обновление: Даже при использовании полного пути (/com/pvminecraft/points/resources/messages.properties), эта проблема все еще сохраняется.

Обновление 2: Вот полная трассировка стека:

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)

Все содержимое org.bukkit и org.craftbukkit является сервером. Файл .properties загружается в методе buildMessages, вызываемом методом onEnable из Points.

Обновление 3: При новой установке Arch Linux файл свойств сообщения загружается правильно, и все в порядке. Удаленный сервер - Ubuntu Linux, а мой компьютер разработчика - Arch.

Обновление 4: Хорошо, это своего рода разрешение. Кажется, это локализованная проблема. Я говорю это, потому что мне удалось получить доступ еще к двум компьютерам, и программа работает правильно на обоих. Хотя это и раздражает, но с моим кодом или сценариями сборки это не так. Я все еще хочу знать, что не так, но это больше не актуально. Я продолжу изучать это. Спасибо всем.

Ответы [ 4 ]

2 голосов
/ 05 января 2012

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

Попробуйте это без первого '/', и оно должно работать везде, где работает JVM.

Если это не сработало, попробуйте поместить файл в корень файла JAR и повторите попытку.

Если все еще не работает, попробуйте использовать этот метод:

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

Вам понадобится это

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

Получить путь бегущей банки

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

Первый '/' Я действительно не помню, почему он появляется, но он появляется, поэтому вы должны удалить его:

Наконец, вызовите метод: getFile(currentJar, "PATH_TO_PROPERTIES_FILE");

У вас будет массив байтов для работы. Просто укажите его как ByteArrayInputStream, и ваши проблемы должны быть решены.


Этот код является частью класса util, который я создал, поэтому ненужное чтение байтового массива, но, конечно, вы можете изменить его, чтобы использовать непосредственно этот InputStream для метода Properties.load ().

Ссылка для класса утилит ZIP

* ** тысячи двадцать-восемь ** тысячи двадцать-девять +1030 * -inhonmodman.svn.sourceforge.net / ViewVC / все-inhonmodman / ModManager / SRC / modmanager / утилиты / ZIP.java? Редакция = 292 & Content-Type = текст% 2Fplain * 1 032 *

Ссылка на класс утилит FileUtils

* 1 037 * -inhonmodman.svn.sourceforge.net / ViewVC / все-inhonmodman / ModManager / SRC / modmanager / утилиты / FileUtils.java? Редакция = 294 & Content-Type = текст% 2Fplain * тысяча тридцать девять *

2 голосов
/ 24 декабря 2011

Похоже на незначительные тонкости между различными загрузчиками классов Java и их путями поиска.Прежде чем вдаваться в эти детали;почему бы вам не попробовать полный путь в этом файле JAR?(т.е. что-то вроде этого:

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

)

0 голосов
/ 07 января 2012

Поскольку вы загружаете ресурс, который находится в том же пакете вашего класса (Point), вам не нужно использовать абсолютный путь для его загрузки.

Использует ли сервер какой-либо типкеша для загрузки плагинов?Это может быть вызвано тем, что в пути к классу присутствует старая версия jar плагина.Чтобы убедиться, что сервер действительно загружает правильную версию jar-файла, вы можете попробовать развернуть версию вашего плагина, которая регистрирует что-то на консоли и посмотреть, если что-то происходит (если сообщение действительно регистрируется).* Кроме того, я не знаю, как организована иерархия загрузчиков классов на сервере, но вы можете попробовать загрузить ресурс из загрузчика классов текущего потока (обычно это загрузчик корневого родительского класса, который будет искать ресурс в каждом другом дочернем элементе).класс загрузчик).Для этого вам нужно будет использовать абсолютный путь.

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

Проверьте этот вопрос , чтобы узнать больше о различных загрузчиках классов.

0 голосов
/ 06 января 2012

Вы также можете убедиться, что ваш скрипт сборки (Ant, Maven) или ваша IDE не удалили / не переместили эти messages.properties (потому что это не .class) из получившегося файла JAR.Вы можете проверить содержимое JAR с помощью таких инструментов, как 7zip или WinZip.

...