Исполняемая банка не найдет файлы свойств

Я использую этот код в своей программе для загрузки файла свойств:

Properties properties = new Properties();
URL url = new App().getClass().getResource(PROPERTIES_FILE);
properties.load(url.openStream());

Код отлично работает в Eclipse. Затем я упаковываю программу в JAR с именем MyProgram.jar и запускаю ее, я получил исключение NullPointerException во второй строке. JAR не содержит файл свойств, они оба находятся в одном каталоге. Я использую Maven для создания JAR. Как я могу исправить эту проблему?

UPDATE: я не хочу добавлять файл свойств в JAR, так как он будет создан во время развертывания.

Ответы

Ответ 1

BalusC прав, вам нужно указать Maven для создания MANIFEST.MF с текущим каталогом (.) в записи Class-Path:.

Предполагая, что вы все еще используете Maven Assembly Plugin и дескриптор jar-with-dependencies для создания исполняемого JAR, вы можете сказать плагину сделать это, используя следующее:

  <plugin>
    <artifactId>maven-assembly-plugin</artifactId>
    <version>2.2</version>
    <configuration>
      <descriptorRefs>
        <descriptorRef>jar-with-dependencies</descriptorRef>
      </descriptorRefs>
      <archive>
        <manifest>
          <mainClass>com.stackoverflow.App</mainClass>
        </manifest>
        <manifestEntries>
          <Class-Path>.</Class-Path> <!-- HERE IS THE IMPORTANT BIT -->
        </manifestEntries>
      </archive>
    </configuration>
    <executions>
      <execution>
        <id>make-assembly</id> <!-- this is used for inheritance merges -->
        <phase>package</phase> <!-- append to the packaging phase. -->
        <goals>
          <goal>single</goal> <!-- goals == mojos -->
        </goals>
      </execution>
    </executions>
  </plugin>

Ответ 2

Есть два обходных решения:

  • Не используйте JAR как executabele JAR, а как библиотеку.

    java -cp .;filename.jar com.example.YourClassWithMain
    
  • Получить корневое расположение файла JAR и получить из него файл свойств.

    URL root = getClass().getProtectionDomain().getCodeSource().getLocation();
    URL propertiesFile = new URL(root, "filename.properties");
    Properties properties = new Properties();
    properties.load(propertiesFile.openStream());
    

Ни один из них не рекомендуется! Рекомендуемый подход состоит в следующем: запись в файле JAR /META-INF/MANIFEST.MF:

Class-Path: .

Затем он будет доступен как ресурс пути к классам обычным способом. Вам действительно нужно будет как-то указать Maven для создания файла MANIFEST.MF.

Ответ 3

EDIT: это ответ на ваш комментарий:

Вам нужно убедиться, что файл свойств находится в пути класса с правильным корнем для вызова java, который запускает файл jar. если ваш путь

stuff/things.properties

и время выполнения

/opt/myapp/etc/stuff/things.properties

а файл jar находится в

/opt/myapp/bin/myjar

то вам нужно запустить как

/path/to/java -cp "/opt/myapp/etc:/opt/myapp/bin/myjar.jar" my.pkg.KavaMain

работа с подобным конфигом может быть неприятной в среде dev, к счастью, там maven exec plugin, который доставит вам правильный вид сценария запуска.

Оригинальный ответ:

Вы хотите прочитать плагин ресурсов maven.

В основном вы хотите добавить что-то вроде этого:

<plugin>
        <artifactId>maven-jar-plugin</artifactId>
        <configuration>
                <resources>
                        <resource>
                                <directory>src/main/java</directory>
                                <includes>
                                        <include>**/*properties</include>
                                </includes>
                        </resource>
                </resources>
        </configuration>
<plugin>

к вашему pom.xml, предполагая, что вы файл propertis с вашими источниками java - действительно, он должен быть в src/main/resources.

Ответ 4

У меня была аналогичная проблема, и эта тема была большой помощью! FYI, я модифицировал свой файл Ant buildfile для выполнения MANIFEST-создания, а затем обозначил этот манифест, когда JAR-код моей серверной части:

<!-- Create a custom MANIFEST.MF file, setting the classpath. -->
<delete file="${project.base.dir}/resources/MANIFEST.MF" failonerror="false"/>
<manifest file="${project.base.dir}/resources/MANIFEST.MF">
  <attribute name="Class-Path" value="." />
</manifest>

<!-- JAR the server-side code, using the custom manifest from above. -->
<jar destfile="services/${name}.aar" manifest="${project.base.dir}/resources/MANIFEST.MF">
[....]