La classe java.net.URL veniva aggiunta nella libreria standard di Java nel 1995, e già allora era presente un grave problema con l’implementazione del suo metodo equals. Il problema è evidente dall’immagine che segue:

Usando la classe URL, due url differenti risultano uguali secondo equals.

Infatti, direttamente dalla sua documentazione:

Two hosts are considered equivalent if both host names can be resolved into the same IP addresses; else if either host name can’t be resolved, the host names must be equal without regard to case; or both host names equal to null.
Since hosts comparison requires name resolution, this operation is a blocking operation.

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/URL.html#equals(java.lang.Object)

In pratica, quando eseguiamo l’equals di due oggetti URL, verrà eseguita una risoluzione DNS (operazione peraltro bloccante) di entrambi, e se gli ip risolti coincidono, allora gli oggetti vengono considerati uguali.

Perchè l’implementazione di equals di java.net.URI è problematica

Partiamo col dire che, a livello utente, non ci si aspetterebbe che un’implementazione di equals richieda una chiamata di rete.

Una richiesta ad un server dns, potrebbe fallire in più punti e per più motivi. A livello pratico questo si traduce in:

  1. Siti che il giorno prima equivalevano, il giorno dopo non equivalgono più solo perchè magari c’è stato un problema a raggiungere il server DNS.
  2. Lo stesso vale in caso gli host cambino i loro ip. Se due host si affidano alla stessa CDN (ad esempio Cloudflare), allora il loro equals sarà positivo.
  3. Infine, questo vuol dire che possano manifestarsi problemi come questo: JDK-4426753 : URL insert into MAP as key performance bug. Ovvero la risoluzione potrebbe bloccare l’esecuzione per tempi anche molto lunghi.

Soluzione: usare la classe java.net.URI

Questo bug tracker: JDK-4434494 : The URL class treats as equal different URLs, ci fornisce maggiore background sul problema e la sua soluzione:

We are well aware of the problem with URL.equals and URL.hashCode. The cause of the problem is due to the existing spec and implementation, where we will try to compare the two URLs by resolving the host IP addresses, instead of just doing a string comparison. Because hashCode has to maintain certain relationships with equals, namely if two objects are equal, they should have the same hashCode, the implementation of hashCode also tries to resolve the host in the URL into an IP address. As a result, we are facing problems with http virtual hosting, as described in the Description part, and performance hit due to DNS name resolutions.

Unfortunately, changing the behavior now would break backward compatibility in a serious way, plus Java Security mechanism depends on it in some parts of the implementation. We can’t change it now.

However, to address URI parsing in general, we introduced a new class called URI in Merlin (jdk1.4). People are encouraged to use URI for parsing and URI comparison, and leave URL class for accessing the URI itself, getting at the protocol handler, interacting with the protocol etc. So, at present, we don’t plan on changing the URL.equals/hashCode behavior and we will leave the bug open until Tiger, when we re-investigate our options.

Quindi la soluzione è di evitare l’utilizzo di URL in favore di URI qando possibile.