JEP 418, Internet-Address Resolution SPI

Contexte

Parmi les nouvelles fonctionnalités du JDK 18, nous avons la mise en place d’une SPI (Service Provider Interface) pour la résolution d’adresses Internet.

Le développeur utilise la classe java.net.InetAddress pour la résolution d’adresse Internet. Il a pour cela les méthodes suivantes à notre disposition :

  • InetAddress::getAllByName() permet de faire une recherche par nom. Elle retourne un tableau d’adresses IP

  • InetAddress::getByName() permet de faire une recherche par nom. Elle retourne la première adresse IP de la précédente liste.

Pour une adresse Internet, nous avons les méthodes suivantes :

  • InetAddress::getCanonicalHostName() permet de faire une résolution inverse (Adresse IP vers le nom).

  • InetAddress::getHostName() permet de faire une résolution inverse si c’est nécessaire.

Actuellement, la résolution des noms sur le réseau IP est réalisée à partir des fonctions systèmes natives afin d’interroger les serveurs DNS (Domain Name Server).

Les motivations

Les principales sont les suivantes :

  • Projet Loom : La résolution de noms utilise les appels systèmes qui bloquent le traitement. Cela va à l’encontre du principe de thread virtuel. Il faut passer par des solutions alternatives afin d’interroger les serveurs DNS.

avertissement Il est à noter qu’il est possible d’interroger ces serveurs à l’aide de l’API JNDI.

  • Nouveaux protocoles : Au delà du DNS, des nouveaux protocoles émergent comme DNS au dessus de HTTPS.

  • Personnalisation : Avoir la maitrise fine de la résolution, utiliser une bibliothèque adéquate.

  • Tests : Il est ainsi possible de "moquer" la résolution DNS pour les tests.

Comment cela marche ?

Pour cela, nous avons des nouvelles classes dans le package java.net.spi:

  • InetAddressResolver : C’est l’interface qui réalise concrêtement la résolution du nom. Elle est récupérer en passant par la fabrique ci-dessus. Elle définie deux méthodes :

interface InetAddressResolver {
    String lookupByAddress(byte[] addr);

    Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy)

}
  • InetAddressResolver.LookupPolicy : C’est une classe qui permet de définir les caractéristiques de la résolution. Par exemple, IPV4, IPV4_FIRST, IPV6 et IPV6_FIRST

  • InetAddressResolverProvider : C’est la classe abstraite qui renvoie le résolveur sous le modèle des fabriques. Ce fournisseur est activé par le biais du ServiceLoader

public abstract class InetAddressResolverProvider {
    public abstract InetAddressResolver get(Configuration config);

    public abstract String name();
}
  • InetAddressResolverProvider.Configuration : C’est une interface qui permet de récupérer l’implémentation par défaut.

interface InetAddressResolverProvider.Configuration {
    InetAddressResolver builtinResolver()

    String lookupLocalHostName()
}

Mise en oeuvre

Pour la mise en oeuvre, nous avons définir notre fournisseur qui fera tout simplement de la délégation vis à vis du fournisseur par défaut de OpenJDK.

Pour cela nous allons commencer avec une nouvelle classe pour notre résolveur.

public class MonResolveurDNS implements InetAddressResolver  { (1)

	private InetAddressResolver delegate; (2)

	public MonResolveurDNS(InetAddressResolver delegate) {
		this.delegate = delegate;
	}

	@Override
	public Stream<InetAddress> lookupByName(String host, LookupPolicy lookupPolicy) throws UnknownHostException  { (3)
		System.out.println("[INFO ] : Appel de la méthode 'MonResolveurDNS::lookupByName.");
		return this.delegate.lookupByName(host, lookupPolicy);
	}

	@Override
	public String lookupByAddress(byte[] addr) throws UnknownHostException { (4)
        System.out.println("[INFO ] : Appel de la méthode 'MonResolveurDNS::lookupByAddress.");
		return delegate.lookupByAddress(addr);
	}

}
  1. Nous réalisons l’interface java.net.spi.InetAddressResolverProvider

  2. Nous définissons un attribut avec le résolveur que nous souhaitons utiliser.

  3. Nous écrivons le code pour la méthode lookupByName de l’interface

  4. Nous écrivons le code pour la méthode lookupByAddress de l’interface

Pour utiliser notre résolveur, il faut définir un fournisseur via l’interface InetAddressResolverProvider

public class MonResolveurDNSProvider extends InetAddressResolverProvider { (1)

	@Override
	public InetAddressResolver get(Configuration configuration) { (2)
		System.out.println("[INFO ] : Le fournisseur 'MonResolveurDNSProvider' est chargé.");
		return new MonResolveurDNS(configuration.builtinResolver()); (3)
	}

	@Override
	public String name() {
		return "MonResolveurDNSProvider";
	}

}
  1. Nous réalisons l’interface java.net.spi.InetAddressResolverProvider

  2. Principe de fabrique qui renvoie une instance InetAddressResolver

  3. A partir de l’instance configuration, nous povons récupérer le résolveur par défaut.

Comme indiqué précédemment, c’est le ServiceLoader qui est utilisé pour récupérer le ou les fournisseurs. Pour cela, il est nécessaire de définir un fichier texte avec les règles suivantes :

  • Fichier présent dans le répertoire META-INF/services

  • Fichier ayant le nom de l’interface en tant que nom du fichier. Dans notre cas, c’est : java.net.spi.InetAddressResolverProvider

  • Fichier contenant le nom complet de la classe du fournisseur

Ayant suivi ces 3 étapes, le fournisseur est opérationnelle.

Si vous écrivez le programme suivant :

public static void main (String [] args) throws UnknownHostException {
    InetAddress adr = InetAddress.getByName("www.edf.fr");
    System.out.println(" - " + adr.getHostName() + " -> " + adr.getHostAddress());

    adr = InetAddress.getByName("www.wikipedia.fr");
    System.out.println(" - " + adr.getHostName() + " -> " + adr.getHostAddress());
}

Voici la sortie correspondant à lancement du programme :

[INFO ] : Le fournisseur 'MonResolveurDNSProvider' est chargé.
[INFO ] : Appel de la méthode 'MonResolveurDNS::lookupByName'.
 - www.edf.fr -> 23.72.9.40
[INFO ] : Appel de la méthode 'MonResolveurDNS::lookupByName'.
 - www.wikipedia.fr -> 51.254.200.228