web-dev-qa-db-fra.com

Enregistrement et utilisation d'un protocole Java.net.URL personnalisé

J'essayais d'invoquer un custom url de mon Java, donc j'ai utilisé quelque chose comme ceci:

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

J'ai obtenu l'exception ci-dessous:

Java.net.MalformedURLException: protocole inconnu: CustomURI sur Java.net.URL. (Source inconnue) sur Java.net.URL. (Source inconnue) sur Java.net.URL. (Source inconnue) sur com.demo.TestDemo. main (TestDemo.Java:14)

Si je déclenche le URI à partir d'un navigateur, cela fonctionne comme prévu, mais si j'essaie de l'invoquer à partir du Java Program alors je reçois l'exception ci-dessus.

MODIFIER:

Voici les étapes que j'ai essayées (il me manque quelque chose, assurez-vous de m'en informer):

Étape 1: ajout de l'URI personnalisé dans Java.protocol.handler.pkgs

Étape 2: déclenchement de l'URI personnalisé à partir de l'URL

Code:

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

}

}

Lorsque j'exécute ce code, j'obtiens le CustomURI: imprimé dans ma console (à partir de la méthode add) mais je reçois cette exception lorsque le URL est initialisé avec CustomURI: en tant que constructeur:

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)

Veuillez indiquer comment faire fonctionner cela.

32
user182944
  1. Créez une implémentation URLConnection personnalisée qui exécute le travail dans la méthode 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!");
        }
    
    }
    

    N'oubliez pas de remplacer et d'implémenter d'autres méthodes comme getInputStream() en conséquence. Plus de détails à ce sujet ne peuvent être donnés car ces informations manquent dans la question.


  2. Créez une implémentation URLStreamHandler personnalisée qui la renvoie dans openConnection().

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

    N'oubliez pas de remplacer et d'implémenter d'autres méthodes si nécessaire.


  3. Créez un URLStreamHandlerFactory personnalisé qui le crée et le renvoie en fonction du protocole.

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

    Notez que les protocoles sont toujours en minuscules.


  4. Enfin enregistrez-le lors du démarrage de l'application via URL#setURLStreamHandlerFactory()

    URL.setURLStreamHandlerFactory(new CustomURLStreamHandlerFactory());
    

    Notez que Javadoc indique explicitement que vous pouvez le définir au plus une fois. Donc, si vous avez l'intention de prendre en charge plusieurs protocoles personnalisés dans la même application, vous devez générer l'implémentation URLStreamHandlerFactory personnalisée pour les couvrir tous à l'intérieur de la méthode createURLStreamHandler().


    Alternativement, si vous n'aimez pas la loi de Demeter, jetez le tout ensemble dans des classes anonymes pour la minification du code:

    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 vous êtes sur Java 8 déjà, remplacez l'interface fonctionnelle URLStreamHandlerFactory par un lambda pour une minification supplémentaire:

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

Vous pouvez maintenant l'utiliser comme suit:

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

Ou avec un protocole en minuscule selon la spécification:

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

Si vous ne voulez pas prendre en charge la seule et unique URLStreamHandlerFactory, vous pouvez réellement utiliser une convention de dénomination hideuse mais efficace pour obtenir l'implémentation par défaut.

Vous devez nommer votre classe URLStreamHandlerHandler, et le protocole auquel elle correspond est le dernier segment du package de cette classe.

Alors, com.foo.myproto.Handler-> myproto:urls, à condition d'ajouter votre package com.foo à la liste des "packages source de flux url" pour la recherche sur un protocole inconnu. Pour ce faire, via la propriété système "Java.protocol.handler.pkgs" (qui est une liste | délimitée de noms de packages à rechercher).

Voici une classe abstraite qui exécute ce dont vous avez besoin: (ne vous occupez pas du StringTo<Out1<String>> ou StringURLConnection, ceux-ci font ce que leur nom suggère et vous pouvez utiliser les abstractions que vous préférez)

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

Ensuite, voici la classe réelle implémentant le gestionnaire abstrait (pour dynamic: URL:

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

}
4
Ajax

Vous avez fait une récursion/boucle sans fin.

Le Classloader recherche la classe de différentes manières.

Le stacktrace (URLClassPath) est comme ceci:

  1. Chargez une ressource.
  2. Ai-je chargé tous les protocoles? Non!
  3. Chargez tous les gestionnaires de protocoles, je ne trouve pas le fichier «your Java.protocol.handler.pkgs-package».CustomURI.Handler.
  4. La classe est une ressource! Ai-je chargé tous les protocoles? Non!
  5. Chargez tous les gestionnaires de protocoles, je ne trouve pas le fichier «your Java.protocol.handler.pkgs-package».CustomURI.Handler.
  6. La classe est une ressource! Ai-je chargé tous les protocoles? Non!
  7. Chargez tous les gestionnaires de protocoles, je ne trouve pas le fichier «your Java.protocol.handler.pkgs-package».CustomURI.Handler.
  8. La classe est une ressource! Ai-je chargé tous les protocoles? Non!
  9. Chargez tous les gestionnaires de protocoles, je ne trouve pas le fichier «your Java.protocol.handler.pkgs-package».CustomURI.Handler.
  10. La classe est une ressource! Ai-je chargé tous les protocoles? Non!
  11. Chargez tous les gestionnaires de protocoles, je ne trouve pas le fichier «your Java.protocol.handler.pkgs-package».CustomURI.Handler.

    ...... StackOverflowException !!!

1
Peter Rader