Introducción e implementación básica
Primero, necesitará al menos un URLStreamHandler. Esto realmente abrirá la conexión a una URL dada. Tenga en cuenta que esto simplemente se llama Handler
; esto le permite especificar java -Djava.protocol.handler.pkgs=org.my.protocols
y se recogerá automáticamente, utilizando el nombre del paquete "simple" como protocolo compatible (en este caso, "classpath").
Uso
new URL("classpath:org/my/package/resource.extension").openConnection();
Código
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();
}
}
Problemas de lanzamiento
Si eres como yo, no quieres depender de una propiedad establecida en el lanzamiento para llevarte a algún lugar (en mi caso, me gusta mantener mis opciones abiertas como Java WebStart, por
eso necesito todo esto )
Soluciones / Mejoras
Código manual Especificación del controlador
Si controlas el código, puedes hacer
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
y esto usará su controlador para abrir la conexión.
Pero, de nuevo, esto es menos que satisfactorio, ya que no necesita una URL para hacer esto; desea hacerlo porque alguna biblioteca que no puede (o no quiere) controlar quiere URL ...
Registro de controlador JVM
La última opción es registrar un URLStreamHandlerFactory
que maneje todas las URL en la 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);
}
}
Para registrar el controlador, llame URL.setURLStreamHandlerFactory()
a su fábrica configurada. Entonces haz new URL("classpath:org/my/package/resource.extension")
como el primer ejemplo y listo.
Problema de registro del controlador JVM
Tenga en cuenta que este método solo se puede llamar una vez por JVM, y tenga en cuenta que Tomcat utilizará este método para registrar un controlador JNDI (AFAIK). Prueba Jetty (lo estaré); en el peor de los casos, puede usar el método primero y luego tiene que funcionar a su alrededor.
Licencia
Lanzo esto al dominio público y le pido que, si desea modificarlo, inicie un proyecto OSS en algún lugar y comente aquí con los detalles. Una mejor implementación sería tener una URLStreamHandlerFactory
que use ThreadLocal
s para almacenar URLStreamHandler
s para cada uno Thread.currentThread().getContextClassLoader()
. Incluso te daré mis modificaciones y clases de prueba.
com.github.fommil.common-utils
paquete que planeo actualizar y lanzar pronto a través de Sonatype.