Introdução e Implementação básica
Primeiro, você precisará de pelo menos um URLStreamHandler. Isso realmente abrirá a conexão com um determinado URL. Observe que isso é chamado simplesmente Handler
; isso permite que você especifique java -Djava.protocol.handler.pkgs=org.my.protocols
e ele será automaticamente escolhido, usando o nome do pacote "simples" como o protocolo suportado (neste caso, "caminho da classe").
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 lançamento
Se você é como eu, não quer depender de uma propriedade configurada no lançamento para levá-lo a algum lugar (no meu caso, gosto de manter minhas opções em aberto como o Java WebStart - é por isso
que preciso de tudo isso )
Soluções alternativas / aprimoramentos
Código manual Especificação do manipulador
Se você controlar o código, poderá fazer
new URL(null, "classpath:some/package/resource.extension", new org.my.protocols.classpath.Handler(ClassLoader.getSystemClassLoader()))
e isso usará seu manipulador para abrir a conexão.
Mas, novamente, isso é menos do que satisfatório, pois você não precisa de um URL para fazer isso - você quer fazer isso porque alguma lib que você não pode (ou não quer) controlar quer URLs ...
Registro de manipulador da JVM
A opção final é registrar um URLStreamHandlerFactory
que manipulará todos os URLs na 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 o manipulador, ligue URL.setURLStreamHandlerFactory()
para a fábrica configurada. Então faça new URL("classpath:org/my/package/resource.extension")
o primeiro exemplo e você vai embora.
Problema de registro de manipulador da JVM
Observe que esse método pode ser chamado apenas uma vez por JVM e observe bem que o Tomcat usará esse método para registrar um manipulador JNDI (AFAIK). Tente Jetty (eu serei); na pior das hipóteses, você pode usar o método primeiro e, em seguida, ele deve contornar você!
Licença
Eu libero isso para o domínio público e solicito que, se você deseja modificar, inicie um projeto OSS em algum lugar e comente aqui com os detalhes. Uma implementação melhor seria ter um URLStreamHandlerFactory
que use ThreadLocal
s para armazenar URLStreamHandler
s para cada um Thread.currentThread().getContextClassLoader()
. Eu até darei minhas modificações e aulas de teste.
com.github.fommil.common-utils
pacote que planejo atualizar e lançar em breve via Sonatype.