Introduction et implémentation de base
Tout d'abord, vous allez avoir besoin d'au moins un URLStreamHandler. Cela ouvrira en fait la connexion à une URL donnée. Notez que cela s'appelle simplement Handler
; cela vous permet de spécifier java -Djava.protocol.handler.pkgs=org.my.protocols
et il sera automatiquement récupéré, en utilisant le nom de package "simple" comme protocole pris en charge (dans ce cas "chemin de classe").
Usage
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();
}
}
Problèmes de lancement
Si vous êtes comme moi, vous ne voulez pas compter sur une propriété définie dans le lancement pour vous trouver quelque part (dans mon cas, j'aime garder mes options ouvertes comme Java WebStart - c'est pourquoi
j'ai besoin de tout cela ).
Solutions de contournement / améliorations
Spécification du gestionnaire de code manuel
Si vous contrôlez le code, vous pouvez le faire
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
et cela utilisera votre gestionnaire pour ouvrir la connexion.
Mais encore une fois, c'est moins que satisfaisant, car vous n'avez pas besoin d'une URL pour le faire - vous voulez le faire parce qu'une bibliothèque que vous ne pouvez pas (ou ne voulez pas) contrôler veut des URL ...
Inscription du gestionnaire JVM
L'option ultime est d'enregistrer un URLStreamHandlerFactory
qui gérera toutes les URL à travers le jvm:
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);
}
}
Pour enregistrer le gestionnaire, appelez URL.setURLStreamHandlerFactory()
avec votre usine configurée. Alors faisnew URL("classpath:org/my/package/resource.extension")
comme le premier exemple et c'est parti.
Problème d'enregistrement du gestionnaire JVM
Notez que cette méthode ne peut être appelée qu'une seule fois par JVM, et notez bien que Tomcat utilisera cette méthode pour enregistrer un gestionnaire JNDI (AFAIK). Essayez Jetty (je le serai); au pire, vous pouvez d'abord utiliser la méthode et ensuite elle doit fonctionner autour de vous!
Licence
Je publie ceci dans le domaine public, et je demande que si vous souhaitez modifier que vous démarrez un projet OSS quelque part et commentez ici avec les détails. Une meilleure implémentation serait d'avoir un URLStreamHandlerFactory
qui utilise ThreadLocal
s pour stocker URLStreamHandler
s pour chacun Thread.currentThread().getContextClassLoader()
. Je vais même vous donner mes modifications et mes cours de test.
com.github.fommil.common-utils
package que je prévois de mettre à jour et de publier bientôt via Sonatype.