Registrazione e utilizzo di un protocollo java.net.URL personalizzato

Stavo cercando di invocare un custom url dal mio programma java, quindi ho usato qualcosa di simile a questo:

 URL myURL; try { myURL = new URL("CustomURI:"); URLConnection myURLConnection = myURL.openConnection(); myURLConnection.connect(); } catch (Exception e) { e.printStackTrace(); } 

Ho ricevuto l’eccezione seguente:

java.net.MalformsdURLException: protocollo sconosciuto: CustomURI su java.net.URL. (Origine sconosciuta) su java.net.URL. (Origine sconosciuta) su java.net.URL. (Fonte sconosciuta) su com.demo.TestDemo. principale (TestDemo.java:14)

Se faccio scattare l’ URI da un browser, funziona come previsto ma se provo a richiamarlo dal Java Program ricevo l’eccezione di cui sopra.

MODIFICARE:

Di seguito sono riportati i passaggi che ho provato (mi manca qualcosa di sicuro, per favore fatemelo sapere su questo):

Passaggio 1: aggiunta dell’URI personalizzato in java.protocol.handler.pkgs

Passaggio 2: triggerszione dell’URI personalizzato dall’URL

Codice:

 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")); } } 

Quando CustomURI: questo codice, ottengo CustomURI: stampato nella mia console (dal metodo add), ma poi ottengo questa eccezione quando l’ URL viene inizializzato con CustomURI: come costruttore:

 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.(Unknown Source) at java.net.URL.(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.(Unknown Source) at java.net.URL.(Unknown Source) 

Si prega di consigli su come fare questo lavoro.

  1. Creare un’implementazione URLConnection personalizzata che esegua il lavoro nel metodo connect() .

     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!"); } } 

    Non dimenticare di sovrascrivere e implementare altri metodi come getInputStream() conseguenza. Più dettagli su questo non possono essere dati poiché questa informazione manca nella domanda.


  2. Crea URLStreamHandler personalizzata di URLStreamHandler che la restituisce in openConnection() .

     public class CustomURLStreamHandler extends URLStreamHandler { @Override protected URLConnection openConnection(URL url) throws IOException { return new CustomURLConnection(url); } } 

    Non dimenticare di sovrascrivere e implementare altri metodi, se necessario.


  3. Crea un URLStreamHandlerFactory personalizzato che crea e lo restituisce in base al protocollo.

     public class CustomURLStreamHandlerFactory implements URLStreamHandlerFactory { @Override public URLStreamHandler createURLStreamHandler(String protocol) { if ("customuri".equals(protocol)) { return new CustomURLStreamHandler(); } return null; } } 

    Nota che i protocolli sono sempre in minuscolo.


  4. Infine registralo durante l’avvio dell’applicazione tramite URL#setURLStreamHandlerFactory()

     URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory()); 

    Si noti che Javadoc dice esplicitamente che è ansible impostarlo al massimo una volta. Pertanto, se si intende supportare più protocolli personalizzati nella stessa applicazione, è necessario generare l’implementazione personalizzata URLStreamHandlerFactory per URLStreamHandlerFactory tutti all’interno del metodo createURLStreamHandler() .


    In alternativa, se non ti piace la legge di Demeter, buttala tutta insieme in classi anonime per la minifrazione del codice:

     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; } }); 

    Se sei già su Java 8, sostituisci l’interfaccia funzionale URLStreamHandlerFactory con un lambda per un’ulteriore modifica:

     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); 

Ora puoi usarlo come segue:

 URLConnection connection = new URL("CustomURI:blabla").openConnection(); connection.connect(); // ... 

O con il protocollo in minuscolo secondo le specifiche:

 URLConnection connection = new URL("customuri:blabla").openConnection(); connection.connect(); // ... 

Hai fatto una ricorsione / loop infinito.

Il Classloader cerca la class in modi diversi.

Lo stacktrace (URLClassPath) è così:

  1. Carica una risorsa
  2. Ho caricato ogni protocollo? No!
  3. Carica tutti i gestori di protocoll, non trovo il file «your java.protocol.handler.pkgs-package».CustomURI.Handler .
  4. La class è una risorsa! Ho caricato ogni protocollo? No!
  5. Carica tutti i gestori di protocoll, non trovo il file «your java.protocol.handler.pkgs-package».CustomURI.Handler .
  6. La class è una risorsa! Ho caricato ogni protocollo? No!
  7. Carica tutti i gestori di protocoll, non trovo il file «your java.protocol.handler.pkgs-package».CustomURI.Handler .
  8. La class è una risorsa! Ho caricato ogni protocollo? No!
  9. Carica tutti i gestori di protocoll, non trovo il file «your java.protocol.handler.pkgs-package».CustomURI.Handler .
  10. La class è una risorsa! Ho caricato ogni protocollo? No!
  11. Carica tutti i gestori di protocoll, non trovo il file «your java.protocol.handler.pkgs-package».CustomURI.Handler .

    …… StackOverflowException !!!

Se non si desidera prendere in carico il solo URLStreamHandlerFactory, è ansible utilizzare una convenzione di denominazione orribile, ma efficace per accedere all’implementazione predefinita.

È necessario URLStreamHandler un nome al URLStreamHandler class URLStreamHandler e il protocollo a cui fa il mapping è l’ultimo segmento del pacchetto di quella class.

Quindi, com.foo.myproto.Handler -> myproto:urls , purché tu aggiunga il tuo pacchetto com.foo alla lista di “pacchetti sorgente del stream url” per la ricerca su protocollo sconosciuto. Lo fai tramite la proprietà di sistema "java.protocol.handler.pkgs" (che è un elenco delimitato da nomi di pacchetti da cercare).

Ecco una class astratta che esegue ciò che ti serve: (non preoccuparti della StringTo> mancante StringTo> o StringURLConnection , fanno ciò che suggeriscono i loro nomi e puoi usare qualunque astrazione tu preferisca)

 public abstract class AbstractURLStreamHandler extends URLStreamHandler { protected abstract StringTo> 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 file = dynamicFiles().get(path); return new StringURLConnection(u, file); } } 

Quindi, ecco la class effettiva che implementa il gestore astratto (per dynamic: urls:

 package xapi.dev.api.dynamic; // imports elided for brevity public class Handler extends AbstractURLStreamHandler { private static final StringTo> dynamicFiles = X_Collect.newStringMap(Out1.class, CollectionOptions.asConcurrent(true) .mutable(true) .insertionOrdered(false) .build()); static { addMyPackage(Handler.class); } @Override protected StringTo> dynamicFiles() { return dynamicFiles; } public static String registerDynamicUrl(String path, Out1 contents) { dynamicFiles.put(path, contents); return path; } public static void clearDynamicUrl(String path) { dynamicFiles.remove(path); } }