Могу ли я использовать путь к классам, чтобы переопределить файл в запущенной банке?

У меня есть файл JAR, содержащий приложение, а также файлы конфигурации для этого приложения. Приложение загружает файлы конфигурации из пути к классам (с использованием ClassLoader.getResource()) и полностью удовлетворяет свои зависимости с помощью файлов конфигурации, запеченных в файл JAR.

Иногда я хочу, чтобы приложение запускалось с немного другой конфигурацией (в частности, я хочу переопределить URL-адрес JDBC, чтобы указать на другую базу данных), поэтому я создаю новый файл конфигурации, сохраняю его в правильной структуре каталогов (что означает в каталог /config записи пути к классам), и я хочу сделать что-то вроде этого:

java -cp new-config:. -jar application.jar

Но я не могу заставить путь к классам иметь запись new-config пути перед содержимым JAR приложения. Жестко ли запрограммировано, что содержимое JAR всегда является первым в пути к классам?


person Guss    schedule 10.11.2009    source источник
comment
Вы пробовали разместить конфигурацию вне jar, в ее собственном файле jar по пути относительно application.jar (../conf/config.jar)? Если вы это сделаете, я думаю, вы можете установить путь к классам, указывающий на этот файл конфигурации в манифесте приложения, и вы можете установить новую конфигурацию, изменив config.jar. Мне жаль, что у меня не было больше времени, чтобы сделать демонстрацию, чтобы подтвердить свой ответ, но я не могу ... поэтому я написал это как комментарий   -  person JuanZe    schedule 10.11.2009
comment
Вы имеете в виду, а не внутри JAR?   -  person Guss    schedule 11.11.2009
comment
да, вместо того, чтобы помещать конфигурацию в ту же банку, что и приложение, во вторую банку ...   -  person JuanZe    schedule 12.11.2009
comment
Спасибо, но это не совсем подходит для моего использования - пользователи, которых я пытаюсь поддерживать, создают JAR на своих рабочих станциях с помощью сценария сборки, а затем вручную переносят его на сервер. Попросить их передать два файла будет проблемой.   -  person Guss    schedule 12.11.2009
comment
Это известная ошибка, которая до сих пор не исправлена: вы не можете комбинировать -cp и -jar: bugs.sun.com/bugdatabase/view_bug.do?bug_id=4459663   -  person Wolfgang Fahl    schedule 21.07.2012


Ответы (4)


Почему бы просто не вызвать приложение без указания -jar и вместо этого явно назвать основной класс приложения? Это позволит вам разместить как new-config, так и application.jar в пути к классам в требуемом порядке:

например (при условии, что "new-config" - это каталог, содержащий файл переопределенных свойств)

java -cp new-config:application.jar Application.Main.Class

Я считаю, что имя основного класса можно найти в файле MANIFEST.MF внутри банки ....

person alasdairg    schedule 10.11.2009
comment
Основная проблема заключается в том, что я на самом деле не использую аргумент -cp, а указываю путь к классам в файле манифеста, поскольку приложению требуется много других внешних JAR-файлов - извлечение всего, что будет довольно подвержено ошибкам. Но держу пари, что могу написать сценарий, который извлекает путь к классам из манифеста и автоматически строит соответствующую командную строку, так что, вероятно, это ответ, который я буду использовать. - person Guss; 11.11.2009

Когда вы используете опцию -jar для запуска вашего приложения:

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

как описано здесь. Обходной путь - указать путь к классам в манифесте файла jar, чтобы включить дополнительный путь (описанный здесь).

Однако, учитывая, что вы говорите только об изменении конфигурации, вы можете выбрать другой подход, который не зависит от пути к классам. Например, я обычно настраиваю свои приложения через Spring, используя файлы свойств для определения местоположения баз данных и т. Д. Моя конфигурация Spring согласована в тестовой, тестовой и живой средах, но я передаю другой файл свойств в качестве аргумента командной строки при запуске приложения. .

Фрагмент конфигурации Spring

<bean id="MyDataSource" class="org.springframework.jdbc.datasource.SingleConnectionDataSource">
    <property name="url" value="jdbc:microsoft:sqlserver://${dbServer}:${dbPort};DatabaseName=${dbName}"/>
    <property name="username" value="${dbUserName}"/>
    <property name="password" value="${dbPassword}"/>
    <property name="suppressClose" value="false"/>
</bean>

Фрагмент файла ресурса

dbServer=MyServer
dbPort=1433
dbName=MyDb
dbUserName=Me
dbPassword=foobar
person Adamski    schedule 10.11.2009
comment
Передача файла конфигурации в качестве необязательного параметра - хорошая идея, но мое приложение в настоящее время принимает довольно много параметров, и добавление еще одного будет проблематичным для пользователей. Спасибо за ответ. - person Guss; 11.11.2009
comment
Как запустить приложение? Я бы подумал, что переданные параметры будут скрыты от пользователя при запуске приложения через Webstart, .bat или .sh скрипт и т. Д. - person Adamski; 11.11.2009
comment
Нет - пользователи сами запускают приложение из командной строки, передавая необходимые параметры - даты, файлы для обработки и т. Д. - person Guss; 12.11.2009
comment
Я думаю, вы всегда можете заставить пользователей запускать приложение через файл сценария или bat, который принимает параметры или аргументы JVM и передает их JVM вместе с файлом свойств? Трудно сказать, лучший ли это вариант, не зная подробностей о ваших пользователях и т. Д. - person Adamski; 13.11.2009
comment
В конце концов мы перешли к более сложной реализации, изменив логику загрузчика, чтобы сначала просмотреть пару папок текущего рабочего каталога, прежде чем вернуться к семантике загрузки пути к классам. - person Guss; 15.11.2009

Архив JAR, указанный параметром -jar, имеет приоритет над всеми другими значениями.

Обычно это нужно делать с помощью внешнего файла конфигурации или создавать собственное решение с ClassLoader.getResource().

Мы используем индивидуальное решение для решения этой проблемы - мы загружаем внутренние свойства следующим образом:

final Properties p = new Properties();
p.load(DefaultConfiguration.class.getResourceAsStream("config.properties"));

Затем мы загружаем внешний файл таким же образом и перезаписываем внутренние значения внешними.

Для получения информации о том, как работает загрузка классов, см .:

http://java.sun.com/javase/6/docs/technotes/tools/findingclasses.html

person Gerd Klima    schedule 10.11.2009
comment
Это действительно проблема. Я понимаю, что нет другого выхода, кроме как запустить приложение другим способом - как предлагает alasdairg, или написать какой-то собственный код загрузки. Спасибо. - person Guss; 12.11.2009

Это может быть невозможно при использовании только CLASSPATH. Есть способы заставить вызов ClassLoader.getResource() использовать статический путь для поиска ресурса. Если он это делает, он игнорирует CLASSPATH.

person Kelly S. French    schedule 10.11.2009