Cap 13 Restful Java Clients

There are a few frameworks available that you can use to write RESTful Java clients.

java.net.URL

È la libreria di built-in in java per HTTP. È costruita attorno a due classi java.net.URL and java.net.HttpURLConnection. La classe URL rappresenta l'url, ecco i costruttori:

public class URL {
    public URL(java.lang.String s)
             throws java.net.MalformedURLException {}
    public java.net.URLConnection
             openConnection() throws java.io.IOException {}
...
}

Si può creare una HttpURLConnection che permette di invocare specifiche richieste, ecco un semplice esempio di richiesta GET

URL url = new URL("http://example.com/customers/1");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setRequestMethod("GET");
connection.setRequestProperty("Accept", "application/xml");
if (connection.getResponseCode() != 200) {
  throw new RuntimeException("Operation failed: "+ connection.getResponseCode());
}
System.out.println("Content-Type: " + connection.getContentType());
BufferedReader reader = new BufferedReader(new InputStreamReader(connection.getInputStream()));
String line = reader.readLine();
while (line != null) {
   System.out.println(line);
   line = reader.readLine();
}
connection.disconnect();

Vogliamo XML dal server così chiamiamo il metodo setRequestProperty() per settare l'header Accept. Per ottenere il codice di risposta chiamiamo getResponseCode per il ContentType getContentType().
Il metodo getInputStream() permette di leggere il contenuto mandto dal server usando lo streaming api di java.

Mandiamo una richiesta di post (tra post e put ci sono piccole differenze). Ecco un esempio:

URL url = new URL("http://example.com/customers");
HttpURLConnection connection = (HttpURLConnection) url.openConnection();
connection.setDoOutput(true);
connection.setInstanceFollowRedirects(false);
connection.setRequestMethod("POST");
connection.setRequestProperty("Content-Type", "application/xml");
OutputStream os = connection.getOutputStream();
os.write("<customer id='333'/>".getBytes());
os.flush();
if (connection.getResponseCode() != HttpURLConnection.HTTP_CREATED) {
   throw new RuntimeException("Failed to create customer");
}
System.out.println("Location: " + connection.getHeaderField("Location"));
connection.disconnect();

In questo esempio creiamo un customer facendo una post. Ci aspettiamo una risposta di 201 "Created" e avremmo un header di Location nella riposta che punto all'URL del nuovo customer creato.
Dobbiamo chiamare HttpURLConnection.setDoOutput(true), questo ci permette di scrivere un body per la richiesta.
Per default la HttpURLConnection automaticamente seguirà le redirects. Noi vogliamo guardare all'header di Location per questo chiamiamo setInstanceFollowRedirects(false) per disabilitare questa funzionalità. Poi si setta il tipo di metodo e il tipo di ContentType.
Settiamo tramite java.io.OutputStream l'oggetto che vogliamo passare, otteniamo il codice di ritorno della richiesta e poi effettuiamo il disconnect().

Caching

Per default si effettua il caching basato sugli headers di risposta discussi nel cap 10. Per non effettuarlo usare HttpURLConnection.setUseCaches(false)

Authentication

HttpURLConnection supporta Basic, Digest, and Client Certificate authentication.
Basic e Digest usa l'api java.net.Authenticator ecco un esempio:

Authenticator.setDefault(new Authenticator() {
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication ("username, "password".toCharArray());
    }
});

Il metodo seDefault() è un metodo statico di Authenticator, bisogna passare una istanza di Authenticator che sovrascrive il metodo getPasswordAuthentication(). Ritorna a java.net.PasswordAuthentication che incapsula l'username e la password per accedere al server. Quando si invoca HttpURLConnection l'autenticazione automaticamente setterà il tipo Basic o Digest dipendendo da cosa il server richiede.
È problematico realizzare l'autenticazione in threads multipli su server differenti con il metodo mostrato sopra. Si può usare java.lang.ThreadLocal per memorizzare username e password.
public class MultiThreadedAuthenticator extends Authenticator {
   private static ThreadLocal<String> username = new ThreadLocal<String>();
   private static ThreadLocal<String> password = new ThreadLocal<String>();
   public static void setThreadUsername(String user) {
      username.set(user);
   }
  public static void setThreadPassword(String pwd) {
      password.set(pwd);
  }
    protected PasswordAuthentication getPasswordAuthentication() {
        return new PasswordAuthentication (username.get(),
                                           password.get().toCharArray());
    }
}

La classe ThreadLocal è una classe standard, quando si chiama set() su di se i valori vengono memorizzati e e associati con il thread chiamato. Ogni thread può avere il suo proprio valore, la get() restituisce il valore memorizzato. Usare la classe vuol dire fare qualcosa di questo genere.
Authenticator.setDefault(new ThreadSafeAuthenticator());
ThreadSafeAuthenticator.setThreadUsername("bill");
ThreadSafeAuthenticator.setThreadPassword("geheim");

Client Certificate authentication

Salvo diversa indicazione, il contenuto di questa pagina è sotto licenza Creative Commons Attribution-ShareAlike 3.0 License