mismo - Registro y uso de un protocolo personalizado java.net.URL
uri y url es lo mismo (3)
Intenté invocar una custom url
de mi programa java, por lo tanto, utilicé algo como esto:
URL myURL;
try {
myURL = new URL("CustomURI:");
URLConnection myURLConnection = myURL.openConnection();
myURLConnection.connect();
} catch (Exception e) {
e.printStackTrace();
}
Obtuve la siguiente excepción:
java.net.MalformedURLException: protocolo desconocido: CustomURI en java.net.URL. (Fuente desconocida) en java.net.URL. (Fuente desconocida) en java.net.URL. (Fuente desconocida) en com.demo.TestDemo. main (TestDemo.java:14)
Si desencadenar el URI
desde un navegador, funciona como se esperaba, pero si intento invocarlo desde el Java Program
recibo la excepción anterior.
EDITAR:
A continuación se detallan los pasos que probé (me falta algo seguro, por favor avísenme al respecto):
Paso 1: Agregar el URI personalizado en java.protocol.handler.pkgs
Paso 2: Activación del URI personalizado desde la URL
Código:
public class CustomURI {
public static void main(String[] args) {
try {
add("CustomURI:");
URL uri = new URL("CustomURI:");
URLConnection uc = uri.openConnection();
uc.connect();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void add( String handlerPackage ){
final String key = "java.protocol.handler.pkgs";
String newValue = handlerPackage;
if ( System.getProperty( key ) != null )
{
final String previousValue = System.getProperty( key );
newValue += "|" + previousValue;
}
System.setProperty( key, newValue );
System.out.println(System.getProperty("java.protocol.handler.pkgs"));
}
}
Cuando ejecuto este código, CustomURI:
el CustomURI:
impreso en mi consola (desde el método add) pero luego recibo esta excepción cuando la URL
se inicializa con CustomURI:
como un constructor:
Exception in thread "main" java.lang.StackOverflowError
at java.lang.Class.forName0(Native Method)
at java.lang.Class.forName(Unknown Source)
at java.net.URL.getURLStreamHandler(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at sun.misc.URLClassPath$FileLoader.getResource(Unknown Source)
at sun.misc.URLClassPath.getResource(Unknown Source)
at java.net.URLClassLoader$1.run(Unknown Source)
at java.security.AccessController.doPrivileged(Native Method)
at java.net.URLClassLoader.findClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at sun.misc.Launcher$AppClassLoader.loadClass(Unknown Source)
at java.lang.ClassLoader.loadClass(Unknown Source)
at java.net.URL.getURLStreamHandler(Unknown Source)
at java.net.URL.<init>(Unknown Source)
at java.net.URL.<init>(Unknown Source)
Por favor, consejos sobre cómo hacer que esto funcione.
Cree una implementación
URLConnection
personalizada que realice el trabajo en el métodoconnect()
.public class CustomURLConnection extends URLConnection { protected CustomURLConnection(URL url) { super(url); } @Override public void connect() throws IOException { // Do your job here. As of now it merely prints "Connected!". System.out.println("Connected!"); } }
No olvides anular e implementar otros métodos como
getInputStream()
consecuencia. No se pueden dar más detalles al respecto porque esta información falta en la pregunta.Cree una implementación de
URLStreamHandler
personalizada que la devuelva enopenConnection()
.public class CustomURLStreamHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) throws IOException { return new CustomURLConnection(url); } }
No olvide anular e implementar otros métodos si es necesario.
Cree una
URLStreamHandlerFactory
personalizada que la cree y la devuelva en función del protocolo.public class CustomURLStreamHandlerFactory implements URLStreamHandlerFactory { @Override public URLStreamHandler createURLStreamHandler(String protocol) { if ("customuri".equals(protocol)) { return new CustomURLStreamHandler(); } return null; } }
Tenga en cuenta que los protocolos always son minúsculos.
Finalmente regístrelo durante el inicio de la aplicación a través del
URL#setURLStreamHandlerFactory()
URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
Tenga en cuenta que el URL#setURLStreamHandlerFactory() dice explícitamente que puede establecerlo como máximo una vez. Por lo tanto, si tiene la intención de admitir varios protocolos personalizados en la misma aplicación, deberá generar la implementación personalizada de
URLStreamHandlerFactory
para cubrirlos dentro del métodocreateURLStreamHandler()
.Alternativamente, si no te gusta la Ley de Demeter, ponlo todo junto en clases anónimas para la minificación del código:
URL.setURLStreamHandlerFactory(new URLStreamHandlerFactory() { public URLStreamHandler createURLStreamHandler(String protocol) { return "customuri".equals(protocol) ? new URLStreamHandler() { protected URLConnection openConnection(URL url) throws IOException { return new URLConnection(url) { public void connect() throws IOException { System.out.println("Connected!"); } }; } } : null; } });
Si ya está en Java 8, reemplace la interfaz funcional
URLStreamHandlerFactory
por una lambda para una mayor minificación:URL.setURLStreamHandlerFactory(protocol -> "customuri".equals(protocol) ? new URLStreamHandler() { protected URLConnection openConnection(URL url) throws IOException { return new URLConnection(url) { public void connect() throws IOException { System.out.println("Connected!"); } }; } } : null);
Ahora puedes usarlo de la siguiente manera:
URLConnection connection = new URL("CustomURI:blabla").openConnection();
connection.connect();
// ...
O con protocolo en minúsculas según la especificación:
URLConnection connection = new URL("customuri:blabla").openConnection();
connection.connect();
// ...
Hiciste una recursión / bucle infinito.
El Classloader busca la clase de diferentes maneras.
El stacktrace (URLClassPath) es así:
- Cargar un recurso
- ¿Cargué todos los protocolos? ¡No!
- Cargue todos los manejadores de protocolo, no puedo encontrar el archivo
«your java.protocol.handler.pkgs-package».CustomURI.Handler
. - La clase es un recurso! ¿Cargué todos los protocolos? ¡No!
- Cargue todos los manejadores de protocolo, no puedo encontrar el archivo
«your java.protocol.handler.pkgs-package».CustomURI.Handler
. - La clase es un recurso! ¿Cargué todos los protocolos? ¡No!
- Cargue todos los manejadores de protocolo, no puedo encontrar el archivo
«your java.protocol.handler.pkgs-package».CustomURI.Handler
. - La clase es un recurso! ¿Cargué todos los protocolos? ¡No!
- Cargue todos los manejadores de protocolo, no puedo encontrar el archivo
«your java.protocol.handler.pkgs-package».CustomURI.Handler
. - La clase es un recurso! ¿Cargué todos los protocolos? ¡No!
Cargue todos los manejadores de protocolo, no puedo encontrar el archivo
«your java.protocol.handler.pkgs-package».CustomURI.Handler
....... Exception !!!
Si no desea hacerse cargo de la URLStreamHandlerFactory única y única, puede utilizar una convención de nomenclatura horrible, pero efectiva para entrar en la implementación predeterminada.
Debe nombrar su URLStreamHandler
clase URLStreamHandler
, y el protocolo al que se asigna es el último segmento del paquete de esa clase.
Por lo tanto, com.foo.myproto.Handler
-> myproto:urls
, siempre que agregue su paquete com.foo
a la lista de "paquetes de origen de la secuencia de url" para buscar en un protocolo desconocido. Esto se hace a través de la propiedad del sistema "java.protocol.handler.pkgs"
(que es una lista delimitada de nombres de paquetes para buscar).
Aquí hay una clase abstracta que realiza lo que necesita: (no importa la falta de StringTo<Out1<String>>
o StringURLConnection
, estos hacen lo que sugieren sus nombres y puede usar las abstracciones que prefiera)
public abstract class AbstractURLStreamHandler extends URLStreamHandler {
protected abstract StringTo<Out1<String>> dynamicFiles();
protected static void addMyPackage(Class<? extends URLStreamHandler> handlerClass) {
// Ensure that we are registered as a url protocol handler for JavaFxCss:/path css files.
String was = System.getProperty("java.protocol.handler.pkgs", "");
String pkg = handlerClass.getPackage().getName();
int ind = pkg.lastIndexOf(''.'');
assert ind != -1 : "You can''t add url handlers in the base package";
assert "Handler".equals(handlerClass.getSimpleName()) : "A URLStreamHandler must be in a class named Handler; not " + handlerClass.getSimpleName();
System.setProperty("java.protocol.handler.pkgs", handlerClass.getPackage().getName().substring(0, ind) +
(was.isEmpty() ? "" : "|" + was ));
}
@Override
protected URLConnection openConnection(URL u) throws IOException {
final String path = u.getPath();
final Out1<String> file = dynamicFiles().get(path);
return new StringURLConnection(u, file);
}
}
Entonces, aquí está la clase real implementando el controlador abstracto (para dynamic:
urls:
package xapi.dev.api.dynamic;
// imports elided for brevity
public class Handler extends AbstractURLStreamHandler {
private static final StringTo<Out1<String>> dynamicFiles = X_Collect.newStringMap(Out1.class,
CollectionOptions.asConcurrent(true)
.mutable(true)
.insertionOrdered(false)
.build());
static {
addMyPackage(Handler.class);
}
@Override
protected StringTo<Out1<String>> dynamicFiles() {
return dynamicFiles;
}
public static String registerDynamicUrl(String path, Out1<String> contents) {
dynamicFiles.put(path, contents);
return path;
}
public static void clearDynamicUrl(String path) {
dynamicFiles.remove(path);
}
}