Код, работающий под ant, не находит файл свойств, если я не разветвляю JVM

Я только начинаю работать с Ant, и у меня проблемы с запуском цели. Часть моего кода загружает файл свойств, и он всегда не может найти этот файл, если я не заставлю свою цель запуска использовать новую JVM. Ниже приведен очень упрощенный пример, цель «run» не работает, цель «run_fork» работает. Насколько я понимаю, у Ant есть собственный загрузчик классов, который заменяет загрузчик по умолчанию, поэтому я полагаю, что это каким-то образом искажает путь поиска. Могу ли я каким-либо образом изменить свой код, чтобы это работало без необходимости создания новой JVM?

build.xml:

<project name="PropsExample" default="compile" basedir=".">

<property name="src" location="src"/>
<property name="bin" location="bin"/>

<target name="init">
    <tstamp/>
    <mkdir dir="${bin}"/>
</target>

<target name="compile" depends="init">
    <javac includeAntRuntime="false" srcdir="${src}" destdir="${bin}"/>
    <copy todir="${bin}">
        <fileset dir="${src}" includes="**/*.properties"/>
    </copy>
</target>

<target name="clean">
    <delete dir="${bin}"/>
    <delete dir="${dist}"/>
</target>

<target name="run" depends="compile">
    <java classname="com.example.Test">
        <classpath>
            <pathelement location="${bin}"/>
        </classpath>
    </java>
</target>

<target name="run_fork" depends="compile">
    <java fork="true" classname="com.example.Test">
        <classpath>
            <pathelement location="${bin}"/>
        </classpath>
    </java>
</target>

example code:

package com.example;

import java.util.Properties;
import java.io.InputStream;

public class PropertiesLoader {

    public static String getProperty() throws Exception {

        InputStream in = ClassLoader.getSystemResourceAsStream("com/example/test.properties");
        if ( in == null ) {
            throw new Exception("Cannot find test.properties");
        }
        Properties p = new Properties();
        p.load(in);
        in.close();
        return p.getProperty("test");
    }
}

а также:

package com.example;

public class Test {

    public static void main(String[] args) throws Exception {

        try {
            System.out.println(PropertiesLoader.getProperty());
        } catch ( Exception e ) {
            e.printStackTrace(System.out);
        }
    }
}

person abayliss    schedule 09.05.2011    source источник
comment
Должен добавить, это с Java 1.6.0_22 и Ant 1.8.1   -  person abayliss    schedule 09.05.2011


Ответы (1)


ANT, уже запущенный к тому времени, когда он считывает файл XML, содержащий указанный путь к классам вашего запуска, не может фактически сбросить путь к классам работающей JVM. Вместо этого он пытается добавить к нему цепочку загрузчиков классов; однако ваш вызов загрузчика классов, скорее всего, захватывает корневой загрузчик классов. Возможно, вы захотите сделать что-то вроде этого:

this.getClass().getClassLoader().getResourceAsStream("com/example/test.properties");

что заставит класс использовать тот же загрузчик классов, с которым он был загружен. Это должно (надеюсь) перейти в цепочку ClassLoader в нужном месте, как если бы он загрузил текущий класс, а файл свойств был соответствующим образом «перемещен» вместе с текущим классом, тогда файл свойств должен быть доступен через тот же загрузчик классов.

Обратите внимание, что в любом случае есть много веских причин для создания форка JVM. На мой взгляд, наиболее важным является избавление всей JVM от классов, связанных с ANT. Вы не хотите случайно привязывать свою среду выполнения к классам, которые доступны только в процессе сборки программного обеспечения, и если вы хотите привязать свои классы к ANT, им следует управлять как сторонней библиотекой (чтобы вы могли контролировать версии он привязывается, степень привязки, возможность идентично воспроизвести сборку в нескольких версиях/выпусках ANT и т. д.)

person Edwin Buck    schedule 09.05.2011
comment
извините, удалил лишний класс, который был ненужным. - person Edwin Buck; 09.05.2011
comment
Разве вы не имеете в виду getResourceAsStream, а не getSystemResourceAsStream? - person matt; 10.05.2011
comment
@Edwin Спасибо, я всегда склонялся к разветвлению именно по этим причинам, просто казалось, что то, что я делаю, должно работать. Еще одна строка, которую я обнаружил, которая работала независимо от того, разветвлялся я или нет, была Thread.currentThread().getContextClassLoader().getResourceAsStream("com/example/test.properties");, но я не совсем понимаю, почему это работает, поскольку нет потоковой передачи (или есть?). - person abayliss; 10.05.2011
comment
@Abayliss, когда JVM запускается, она считывает переменную свойства CLASSPATH и настраивает загрузчики основного класса (их 3). Затем он загружает классы для ANT и начинает обработку кода ANT, который затем считывает файл build.xml. Внутри файла build.xml ANT определяет, что ему нужно что-то запустить с помощью команды java, поэтому он загружает нужный класс. Но ждать! Этот класс не находится в пути к классам, поэтому ANT использует собственный загрузчик классов (который он может создавать/уничтожать по команде) для загрузки класса. Запрос системного загрузчика классов не будет использовать правильный загрузчик классов для пути, настроенного муравьем. - person Edwin Buck; 10.05.2011
comment
@Abayliss, Итак, указание объекту использовать тот же загрузчик классов, с которым он был загружен, для загрузки файла свойств, является правильным способом загрузки файла свойств. Если вы загружаетесь другим способом (например, в ant или tomcat или контейнере EJB и т. д.), вы не можете быть уверены, что загрузчик системного класса знает что-либо о загруженном классе (или связанных с ним файлах свойств). Причина, по которой он работает при разветвлении на 100%, заключается в том, что ANT сообщает разветвленной JVM при запуске, поэтому загрузчик системного класса настраивается с указанием пути к корзине. - person Edwin Buck; 10.05.2011