Настройка времени выполнения разрешения ресурса URLConnection - PullRequest
0 голосов
/ 26 апреля 2019

хотя можно утверждать, что это основано на мнении, мне любопытно, есть ли у кого-то здесь представление об этом:

Предположим, вы пишете (узкоспециализированный, но не имеет значения) сервер приложений, такой как Tomcat, Jetty, ... для веб-приложений (та же базовая проблема). Все они имеют механизмы загрузки ресурсов, обычно использующие загрузчик классов контекста потока (Tomcat) или какую-то собственную схему (классы ресурсов Spring, ...).

Я бы хотел (но пока не нашел решение elegenat) предоставить что-то вроде этого, но, кроме того, разрешить конфигурирование во время выполнения каталога "переопределения": подумайте над реализацией Tomcat, но с учетом " .conf "файл рядом с каждым" .war ", который позволяет указывать каталог. Когда ресурс загружается, он сначала ищется в этом каталоге (за войну / приложение) и, если не найден, разрешается с помощью загрузчика классов приложения (в Tomcat, через Thread.currentThread (). GetContextClassloader () - хотя я лично также посчитайте это решение неэффективным, так как не все сторонние библиотеки используют загрузчик классов контекста, и если вы используете одну из них, вам, в принципе, не повезло).

Для этого потребуется доступ к пользовательскому URLStreamHandler или URLConnection во время выполнения. Это, конечно, возможно при «подходе Tomcat» - просто вызвать URL.setURLStreamHandlerFactory с одноэлементным экземпляром и настроить что-то вроде переменной ThreadLocal в этом одноэлементном экземпляре, прежде чем вызывать что-либо (например, весь механизм javax.xml для анализа / проверки), использующий URL для разрешения «ссылок». Теперь, как все знают, синглтоны / глобальное состояние - это проклятие автоматизированного тестирования, но я просто не могу найти решение этого, не обращаясь к какому-то глобальному состоянию.

Вот решение, которое я нашел работающим, которое, к сожалению, использует глобальное состояние:

public class XmlParser {
    // this is the global state...
    private static com.example.proto.classpath.Handler cpHandler;
    static {
        cpHandler = new com.example.proto.classpath.Handler();
        URL.setURLStreamHandlerFactory(p -> proto.equalsIgnoreCase("example") ? cpHandler : null);
    }
    public static interface ResourceLocator extends Function<String, URL> {};
    public org.w3c.dom.Element parse(InputStream doc, ResourceLocator locator) {
        ResourceLocator old = cpHandler.setResourceLocator(locator);
        try {
            // do the actual parsing, not relevant here
            return doc.getDocumentElement();
        } finally {
            cpHandler.setResourceLocator(old);
        }
    }
}

с обработчиком

public class Handler {
    private ThreadLocal<ResourceLocator> loc;

    @Override
    protected URLConnection openConnection(URL url) throws IOException {
        return loc.get().apply(url.getPath()).openConnection();
    }

    public ResourceLocator setResourceLocator(ResourceLocator loc) {
        ResourceLocator old = this.loc.get();
        this.loc.set(loc);
        return old;
    }
}

Таким образом, вы можете реализовать множество ResourceLocators, используя любой желаемый загрузчик классов, сколько угодно каталогов переопределения и т. Д., Но у вас есть глобальное состояние в форме переменной cpHandler XmlParser.

Я бы хотел найти способ избавиться от глобального состояния. У какого-нибудь умного ума на этом сайте есть идея, как достигнуть этого в Java?

P.S .: В настоящее время я пытаюсь сделать это на Java 11 (поскольку это текущий LTS), поэтому JPMS также играет роль в моей текущей реализации ResourceLocator, но здесь это не имеет значения. Также не против обновления до Java 12 или раннего доступа 13 или чего-то еще, если это поможет.

...