Einführung und grundlegende Implementierung
Zunächst benötigen Sie mindestens einen URLStreamHandler. Dadurch wird die Verbindung zu einer bestimmten URL tatsächlich geöffnet. Beachten Sie, dass dies einfach aufgerufen wird Handler
. Auf diese Weise können Sie angeben, dass java -Djava.protocol.handler.pkgs=org.my.protocols
es automatisch unter Verwendung des "einfachen" Paketnamens als unterstütztes Protokoll (in diesem Fall "Klassenpfad") abgeholt wird.
Verwendung
new URL("classpath:org/my/package/resource.extension").openConnection();
Code
package org.my.protocols.classpath;
import java.io.IOException;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLStreamHandler;
/** A {@link URLStreamHandler} that handles resources on the classpath. */
public class Handler extends URLStreamHandler {
/** The classloader to find resources from. */
private final ClassLoader classLoader;
public Handler() {
this.classLoader = getClass().getClassLoader();
}
public Handler(ClassLoader classLoader) {
this.classLoader = classLoader;
}
@Override
protected URLConnection openConnection(URL u) throws IOException {
final URL resourceUrl = classLoader.getResource(u.getPath());
return resourceUrl.openConnection();
}
}
Probleme starten
Wenn Sie so etwas wie ich sind, möchten Sie sich nicht darauf verlassen, dass beim Start eine Eigenschaft festgelegt wird, um Sie irgendwohin zu bringen (in meinem Fall möchte ich meine Optionen wie Java WebStart offen halten - deshalb brauche
ich all dies ).
Problemumgehungen / Verbesserungen
Manuelle Code-Handler-Spezifikation
Wenn Sie den Code steuern, können Sie dies tun
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
und dies wird Ihren Handler verwenden, um die Verbindung zu öffnen.
Aber auch dies ist weniger als zufriedenstellend, da Sie dazu keine URL benötigen - Sie möchten dies tun, weil eine Bibliothek, die Sie nicht steuern können (oder wollen), URLs benötigt ...
Registrierung des JVM-Handlers
Die ultimative Option besteht darin, eine zu registrieren URLStreamHandlerFactory
, die alle URLs im gesamten JVM verarbeitet:
package my.org.url;
import java.net.URLStreamHandler;
import java.net.URLStreamHandlerFactory;
import java.util.HashMap;
import java.util.Map;
class ConfigurableStreamHandlerFactory implements URLStreamHandlerFactory {
private final Map<String, URLStreamHandler> protocolHandlers;
public ConfigurableStreamHandlerFactory(String protocol, URLStreamHandler urlHandler) {
protocolHandlers = new HashMap<String, URLStreamHandler>();
addHandler(protocol, urlHandler);
}
public void addHandler(String protocol, URLStreamHandler urlHandler) {
protocolHandlers.put(protocol, urlHandler);
}
public URLStreamHandler createURLStreamHandler(String protocol) {
return protocolHandlers.get(protocol);
}
}
Um den Handler zu registrieren, wenden Sie sich URL.setURLStreamHandlerFactory()
an Ihre konfigurierte Fabrik. Dann new URL("classpath:org/my/package/resource.extension")
gefällt dir das erste Beispiel und los geht's.
Problem bei der Registrierung des JVM-Handlers
Beachten Sie, dass diese Methode nur einmal pro JVM aufgerufen werden darf, und beachten Sie, dass Tomcat diese Methode verwendet, um einen JNDI-Handler (AFAIK) zu registrieren. Versuchen Sie Jetty (ich werde sein); im schlimmsten Fall können Sie die Methode zuerst anwenden und dann muss sie um Sie herum funktionieren!
Lizenz
Ich gebe dies für die Öffentlichkeit frei und bitte Sie, wenn Sie Änderungen vornehmen möchten, irgendwo ein OSS-Projekt zu starten und hier mit den Details zu kommentieren. Eine bessere Implementierung wäre eine URLStreamHandlerFactory
, die ThreadLocal
s verwendet, um URLStreamHandler
s für jede zu speichern Thread.currentThread().getContextClassLoader()
. Ich werde Ihnen sogar meine Modifikationen und Testklassen geben.
com.github.fommil.common-utils
Pakets hinzufügen, dass ich in Kürze über Sonatype aktualisieren und veröffentlichen möchte.