mercoledì 31 marzo 2021

Cosa va provato ?

Di qualcosa e di qualcuno bisogna fidarsi. Non si tratta di una scorciatoia, è il modo per non lavorare come dei buoi.

Se facciamo, per esempio, una funzione che somma due numeri, fare un unit test che provi le somme o farne cinque, sei oppure mille è la stessa identica cosa, infatti non potremo mai provare tutte le combinazioni di numeri possibili: dobbiamo solo verificare che il nostro programma sommi e che non faccia sottrazioni o altre operazioni. Fare tanti test significa provare "l'operatore somma" dello strumento e non ha senso: se non va, si cambia strumento immediatamente.

Allo stesso modo chi fa test di un' applicazione di database non deve provare il funzionamento del database stesso, perché se si scoprisse un bug sul database, bisognerebbe intervenire subito a sostituirlo o ad applicare una correzione. E' necessario avere dei punti certi, fissi.

Credo che troppo spesso nelle software house italiane si premi la velocità di sviluppo, perché la velocità determina il costo di produzione. Credo che questo dipenda in larga parte dal fatto che esse producono soluzioni con basso valore aggiunto, quindi il fattore determinante è il costo. E' chiaro infatti che produrre un sistema per una banca online senza il quale la banca non esisterebbe, ha un gran valore e il cliente è ben disposto a pagare la novità e la qualità. Fare lavoretti, che al massimo danno prestigio all'azienda di lustrini italiana, non porta valore e bisogna fare presto. Allora via libera a modi di lavorare alienanti e tempi asfissianti, che portano per assurdo a lavorare di più e a metterci di più.

Oltre alla frustrazione per il lavoratore, il costo è più alto! Vaglielo a spiegare.

I test vanno fatti con intelligenza, non con fretta.




domenica 28 febbraio 2021

Tutto passa, anche la jvm

Credo che il tempo glorioso dei linguaggi (ma sarebbe meglio parlare di ecosistemi) basati su virtual machine stia volgendo al termine.

Con questo non voglio dire che i vari Java (quindi Scala, Jython ecc...) così come .NET, spariranno domattina, vista la base di installato, ma anche vista l'enorme disponibilità di librerie e strumenti.

Voglio però dire che c'è sempre meno bisogno di un programma cross platform, grazie alla possibilità di virtualizzare un sistema operativo intero. Ci sono strumenti che compilano programmi cross platform e non hanno quindi tutto l'overhead di una virtual machine. Se poi guardiamo i sistemi con i container, i quali magari si lanciano e chiudono dinamicamente per la scalabilità orizzontale dell'applicazione, è preferibile avere strumenti che usino poche risorse e che siano veloci a caricarsi; è quindi evidente che l'overhead di una virtual machine è un problema. Non mi stupisce quindi che si spingano tecnologie come GraalVM, ma mi chiedo se a questo punto non valga la pena di guardare altrove.

Da un lato C++ spaventa i neofiti così come spaventa le aziende, che sono abituate a cercare programmatori a basso costo, bassa formazione e alta sostituibilità e ne ho viste tante, veramente troppe. Oggi i linguaggi possibili sul cloud non sono comunemente compilati, penso a Javascript con Node.js, che si può usare anche nelle funzioni o lambda dei cloud più comuni oppure sono semisconosciuti (come Swift e Rust, soprattutto quest'ultimo mi sembra una vera chicca, ma non trovo offerte di lavoro su di esso, quindi mi chiedo se in Italia lo usi qualcuno). Altre soluzioni mi sembrano troppo sperimentali.

Il concetto però è che usare Java o .NET in una funzione di un cloud come in un container non è la scelta migliore, perché quando sono nati non si parlava certo di container o di lambda. Se poi dobbiamo fare una piccola web app, con poco budget e magari ci occupiamo sia del front end che del back end, di sicuro dobbiamo imparare javascript, allora tanto vale usarlo anche sul server, perché imparare un linguaggio diverso?

Non voglio parlare poi del desktop, che sta diventando sempre più una nicchia, ma personalmente trovo che Java in quel settore sia sempre stato un pesce fuor d'acqua: evidentemente non muoveva il denaro dietro allo sviluppo lato server (il nuovo Cobol si diceva), quindi le proposte fatte nel tempo (AWT, Swing e alla fine JavaFX) non hanno mai convinto del tutto. Oggi JavaFX è stato scorporato dal JDK e se dovessi fare un'applicazione desktop, oggi cercherei delle alternative e questo vale anche per .NET, lento, grosso, un framework che non mi è mai piaciuto.

Java ha perso un'occasione fantastica con gli smartphone, perché se da un lato Android per me è un fork di Java e non mi stancherò mai di dirlo, su iOS non esiste proprio.

Ho investito gran parte della mia carriera lavorativa su questo linguaggio, ma oggi vedo sempre meno opportunità.

Oggi è meglio conoscere un po' di diverse tecnologie, imparare bene nei dettagli uno strumento richiede un investimento di tempo che difficilmente ritorna, le tecnologie invecchiano in fretta e si rischia veramente che l'investimento non ritorni, però non voglio giustificare chi si butta nei progetti e, soprattutto, butta i collaboratori, senza conoscere lo strumento. I corsi sono tutt'ora molto utili per evitare i guai, ma credo che imparare bene un framework intero paghi meno che sapersi districare su diversi strumenti. Non vale soltanto per la programmazione, anche con i database è così e oggi bisogna conoscere almeno un database NoSQL, perché i database relazionali non sono la soluzione migliore con i microservizi e a questo potrei dedicare un articolo più avanti.

Noi non siamo la tecnologia che usiamo. Non ci dobbiamo identificare con essa e dobbiamo essere pronti a cambiarla, quando i tempi e le condizioni di mercato lo impongono.

Bisogna sempre stare con la valigia in mano insomma. Da un lato si dice che serve una grande specializzazione, ma dall'altro specializzarsi non deve significare legarsi a una tecnologia, che passa.



giovedì 2 aprile 2020

Studia che poi avrai un buon lavoro

Ma perché? La maggior parte del tempo che passiamo sui libri, lo impegniamo di fatto a imparare nozioni completamente irrilevanti per il mercato del lavoro.

Gli insegnanti spesso vivono in una realtà, quella accademica, che è ben lontana dal mondo delle imprese, dai suoi bisogni, dai suoi ritmi e dalle sue condizioni.

Eppure è evidente come i laureati abbiano una strada preferenziale nelle assunzioni, credo che sia legittimo a questo punto chiedersi cosa cercano le aziende e cosa trovano in questi ragazzi freschi di laurea. Bene io credo che tra le prime caratteristiche necessarie per portare avanti un percorso di studi sia una buona resistenza alla noia e la capacità di "pensare per obiettivi", cioè di raggiungere un obiettivo in qualche modo, facendosi poche domande sulla sua effettiva utilità strategica e quindi di fare ciò che gli viene richiesto, indipendentemente dal fatto che gli piaccia, che lo ritenga veramente utile o che si illuda che ciò sia vero. Ed è proprio questo che moltissime aziende cercano: servi.

Ciò che effettivamente il candidato conosce è poco importante, perché deve solo obbedire al capo e intuire velocemente ciò che il capo gli dice male o parzialmente. Anche questa grande pagliacciata del lavoro di gruppo maschera il fatto, che chi comanda non sia in grado di coordinare (non dico di formare) una squadra, quindi si buttano nel calderone (il gruppo) persone sperando che si coordino tra di loro, quasi magicamente, come un sistema che si equilibra da solo. Anche i gruppi di studio all'università servono a preparare questa condizione. In realtà c'è uno scenario anche peggiore: quando la gerarchia nel gruppo c'è eccome, ma ci sono più capi in competizione tra di loro e il nuovo arrivato deve barcamenarsi nel cercare di capire quale padrone servire, mediare scontentando tutti o accontentarli a turni, ben consapevole degli strali che gli arriveranno dai capi scontenti.

Questo scenario riguarda purtroppo diverse software house italiane. Non sono tutte così, perché grazie al cielo ho avuto modo di confrontarmi anche con gente seria, ma i bravi si possono contare sulle punte delle dita di una mano, mozza di qualche dito, magari.

Difficile anche dire cosa vuol dire "bravo" o "fatto bene", diciamo che il termine andrebbe completato con " per chi ti comanda" o meglio per ciò che gli è stato insegnato svariati decenni prima da un insegnante a scuola o sul lavoro... e il loop continua.

Dubito che in fabbrica o in altre realtà la situazione sia diversa, temo anche che la condizione del lavoratore in ambienti con uno scarso livello di istruzione sia ben peggiore, ma non ho esperienza diretta di quei casi, quindi non me la sento di scriverne qui.

Tornando al tema principale di questo articolo, l'università, credo che rifletta pienamente la media della società in cui ci troviamo e che abbia fallito l'obiettivo di cercare di migliorarla.

Consiglierei senz'altro a un giovane di studiare, proprio perché questi difetti diventano pregi negli occhi di chi dovrà offrirgli uno stipendio e di fatto è un plus notevole avere un titolo di studio, ancora di più se il lavoro sarà statale, perché lo stato non può contraddirsi (troppo) apertamente e premia chi ha obbedito al di là della sua bravura in senso assoluto, che abbiamo visto prima come non esista, quindi lo stato premia con condizioni di lavoro decisamente migliori della media, chi è stato un bravo servo fedele e ha obbedito, dedicando tanti e insostituibili anni della sua vita a fare cose largamente inutili se prese come sono, ma utilissime a dimostrare la propria fedeltà all'esecuzione del compito assegnato.

Altro discorso invece è per chi si porta fuori da questo circo e cerca di risolvere i problemi per gli altri, posto che questi altri si fidino dei propri occhi e non si basino sul giudizio degli altri (certificazioni e titoli) per giudicare il lavoro fatto e in ultima analisi, chi lo ha fatto.

La conclusione sulla strategia migliore da adottare è: scegliere con cura i propri insegnanti, cercando scuole che preparino non tanto al lavoro, quanto a non diventarne schiavi, il che si traduce nella capacità di pensare con la propria testa e nel saper persuadere che ciò che è stato fatto risolve il problema per cui è stato pagato o almeno ha risolto altri problemi, che non erano evidenti all'inizio... La seconda scelta da fare è non lavorare per tutti, ma attorniarsi di gente che ci apprezza e apprezza ciò che facciamo al di là dei titoli. Questo non significa cercare una claque, che ci applauda al di là degli errori che facciamo, perché sarebbe un punto di vista narcisistico e praticamente irrealizzabile, oltre che palesemente ingiusto. Io dico che dobbiamo circondarci di gente che ci permetta di sbagliare, perché lo sbaglio non è un lusso: è uno dei tanti passi necessari per raggiungere il successo. Questo è necessario e stranamente pare non essere considerato dalla società latina nella quale viviamo, dove si incensano i vincenti e si ignorano gli altri. Vorrei veramente conoscere un'azienda che dica "ci hai provato tante volte senza riuscirci, cos'hai imparato dai tuoi fallimenti?" anziché "eh ma questo voto era basso, vabbé era giovane, secondo me puoi sbagliare solo fino ai 18 anni, poi non è più concesso". Il lettore mi creda, l'ho sentito dire veramente, per fortuna non su di me ma su una persona, che avevo proposto per una posizione; quando ci penso ancora mi chiedo cosa avesse fatto in vita sua quel tizio dopo i 18 anni...

Un'ultima cosa: affermare con sicurezza che si posseggono le carte giuste per raggiungere un obiettivo, per prendere un titolo, ma avere scelto di non farlo, generalmente non è creduto... quindi se chi abbiamo di fronte cerca un titolo e non l'abbiamo forse è talmente stolto che non vale nemmeno la pena di convincerlo; possiamo comunque sempre cercare di ottenere quel titolo per convincerlo, se proprio ci teniamo tanto a quel posto, ma forse l'errore è proprio tenerci così tanto.

In questo discorso rimangono fuori i corsi di formazione professionale. Imparare facendo anziché ascoltando. E' così semplice e chiaro che ancora non capisco come si possano sottovalutarli: un corso professionale prepara a fare un compito, quindi se i datori di lavoro proponessero i corsi formativi per i loro obiettivi, questi sarebbero il completamento ideale per un percorso formativo solo teorico. Perché non capiscono che la velocità nell'esecuzione di un compito dipende in larghissima misura, per non dire del tutto, dall'allenamento che si ha ad eseguirlo? Non è che forse il problema dei giovani che non trovano lavoro non è che sono choosy, ma solo che il tessuto produttivo soffre di un'ignoranza sconcertante, proprio perché chi decide ha ricevuto a sua volta un'istruzione inadeguata?




martedì 25 febbraio 2020

Unbreakable

Mi è capitata una discussione  decisamente spiacevole su un tema che oggi dovrebbe essere superato, ma evidentemente non è così: la laicità dell'uso dell'istruzione break per uscire da un ciclo for in Java.

Vediamo un esempio banale di ricerca su un array con uscita quando si trova l'elemento cercato

package com.byteliberi;
import java.util.List;
import java.util.Arrays;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = "";
for (String poligono : poligoni) {
if ("quadrato".equals(poligono)) {
trovato = poligono;
break;
}
}
System.out.println(trovato);
}
}

L'output è la parola: quadrato

Il codice in esempio non è e non vuole essere un esempio stilistico di come si cerca un elemento in una lista in Java,  però è un modo pratico e veloce per cercare un elemento e uscire una volta trovato.
L'istruzione break, il cui uso in Java è legittimo e non si ottiene alcun warning usandola, provoca un "salto" fuori dal for. C'è chi non accetta questa soluzione e si aspetta da un professionista l'uso del più elegante ciclo while.

Ora, concordo che i salti non siano il massimo dell'eleganza e che possano portare a codice difficile da leggere se si esagera e si annida l'uso dell'istruzione break con l'istruzione continue, però sono soluzioni pratiche. Esistono perché credo che derivino dal linguaggio C, ma sono pericolose per la leggibilità se ne si abusa, in pratica non creano problemi di affidabilità del codice.

L'uso del while ha un piccolo inconveniente, che lo rende meno pratico, vediamo la nuova versione:

package com.byteliberi;
import java.util.List;
import java.util.ListIterator;
import java.util.Arrays;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = "";
ListIterator<String> iterator = poligoni.listIterator();
while (iterator.hasNext() && trovato.isEmpty()) {
String poligono = iterator.next();
if ("quadrato".equals(poligono)) {
trovato = poligono;
}
}
System.out.println(trovato);
}

}

Accedere ad un elemento di una lista con il metodo get e l'indice della posizione è efficiente soltanto con gli ArrayList e non con le LinkedList. Non sappiamo e non dobbiamo preoccuparci di quale implementazione sia ritornata da asList, quindi si usa un iteratore. Ora un codice del genere francamente non mi sembra più leggibile di prima, invece ho dovuto introdurre una riga in più ed esplicitare un controllo in più nell'if (c'è un and con due condizioni).

Vediamo ora cosa esiste in Java da qualche versione, le famose lambda che a mio parare (e non solo mio) sono molto eleganti.

package com.byteliberi;
import java.util.Arrays;
import java.util.List;

public class Main {

public static void main(String[] args) {
String[] poliArr = {"triangolo", "quadrato", "pentagono", "esagono"};
List<String> poligoni = Arrays.asList(poliArr);
String trovato = poligoni.stream()
.filter(poligono -> "quadrato".equals(poligono))
.findFirst().orElse(null);
assert(trovato != null);
System.out.println(trovato);
}
}

In questo modo non si deve neppure ricorrere all'uso di un ciclo e si sfrutta una "novità" (anche se è comparsa ormai diversi anni fa, con Java 8) che è la lambda. Questa mi sembra di gran lunga la strada più elegante, ma non la più comoda, confesso che trovo quel break semplicemente comodo.

Oggi credo che discussioni su se sia meglio usare un for o un while lascino il tempo che trovano e che i programmatori che discutono, per non dire litigano su queste cose probabilmente hanno una visione troppo ristretta e manierista del mestiere. Credo che gli elementi migliori debbano avere una visione d'insieme e che questi dettagli implementativi non valgano la pena di essere indagati ulteriormente, se non in sede accademica. Credo inoltre che si lavori assieme e si possa lavorare assieme introducendo un elemento di tolleranza verso lo stile degli altri e di rispetto delle conclusioni degli altri, anche quando non coincidono con le nostre. Lavorare assieme è molto difficile perché gli elementi che entrano in gioco non sono soltanto di natura tecnica, si parla sempre di più di competenze trasversali e discutere su un for o un while in azienda è qualcosa che non dovrebbe succedere.

Java mi piace più di altri linguaggi, anche se oggi non è più il mio solo linguaggio di riferimento, perché è molto potente e permette di superare questi vecchie, inutili diatribe.





giovedì 26 dicembre 2019

Chi è un programmatore ?

Ho letto questo blog che quoto in pieno:
https://alessandrobottoni.wordpress.com/2010/05/28/come-riconoscere-un-buon-programmatore/

L'unica cosa che mi da da pensare è che la programmazione Agile ha veramente cambiato lo scenario, non sempre in meglio: se nel team c'è disaccordo, chi comanda? Il più bravo o chi mette i soldi? A parità di ruolo, ha ragione il più arrogante, non il più timido. Potrei parlare a lungo della realtà di software house italiane, ma ho troppo schifo per farlo adesso.

sabato 24 novembre 2018

Back to school

E' passato veramente molto tempo dall'ultimo post, mi sono successe tante cose, tante porte si sono chiuse, ma ne ho aperta una a testate: mi sono iscritto a Interfacce e Tecnologie della Comunicazione a Rovereto, nonostante tutto.
Si tratta di una facoltà inserita nel dipartimento di Scienze Cognitive (psicologia).
La prima cosa che ci hanno insegnato è l'importanza di un design incentrato sull'utente. Mettere l'utente al primo posto significa sforzarsi di progettare in modo che l'utente abbia la miglior esperienza possibile: messaggi per guidarlo durante l'utilizzo del sistema, funzionalità pensate perché non si senta perso.
Ma la principale differenza tra questa scuola e le normali scuole tecniche sta nel trasmettere agli studenti la convinzione che la tecnica da sola non basta: per fare un buon prodotto, bisogna conoscere chi lo usa, cosa gli è comodo. Se non si studia e si conosce la società, così come ragionano le persone, è difficile capire cosa gli utenti troveranno comodo.
Tutto questo sembra ignorato dalle nostre aziende, dove si cercano ossessivamente tecnici, informatici, ingegneri, ma evidentemente queste aziende ignorano l'importanza di sociologi, antropologi e psicologi, come se quei prodotti non dovessero poi essere usati da degli umani...
La competenza si raggiunge osservano il sistema a trecentosessanta gradi, non solo con il punto di vista di un tecnico.

Stiamo facendo una società dominata dalla tecnica, che non si sa più servire della tecnica per raggiungere i propri scopi, ma siamo noi schiavi dei prodotti della tecnica. Peccato che questo non porti a niente, possiamo pensare che la gente usi un prodotto perché è cool e non perché è utile? Non è forse una moda passeggera?
Chi vuole fare dei prodotti che non siano meteore destinate a scomparire, non può non mettere al primo posto i bisogni reali degli utenti, ma per comprenderli, deve capire gli utenti.

lunedì 5 giugno 2017

Ping Applicativo con REST

Oggi voglio scrivere su alcune funzionalità presenti in Java 8: la Duration per indicare un intervallo di tempo e un nuovo client standard per chiamare un web service di tipo REST.

Si tratta di un servizio di Ping applicativo, cioè il client chiama il server un certo numero di volte e ne misura il tempo di risposta, poi fa una media del tempo di tutte le chiamate.
Il client si chiama a riga di comando indicando come parametri il numero di ping, l'indirizzo e volendo vedere la risposta completa, si può specificare il comando -showResponse

Per esempio un client potrebbe avere i seguenti parametri dopo il nome del file .jar: -c4 http://localhost:8080/MiaApp/rs/ping/ -showResponse

Veniamo ora ai dettagli applicativi.
Per provare ho usato come server wildfly in versione 10.1.0

Il codice più rilevante del server è questa classe, che risponde alle chiamate del client:

@Path("/ping")
public class ApplicativePingService {
@Inject
Logger logger;

private static final String VERSION = "0.0.1ALPHA";

@GET
@Produces("application/json")
public Response ping() {
  final ZonedDateTime now = ZonedDateTime.now(ZoneOffset.UTC);
  String hostName = null;
  try {
    hostName = InetAddress.getLocalHost().getHostName();
    final Pong pong = new Pong(VERSION, hostName,
    now.format(DateTimeFormatter.ISO_ZONED_DATE_TIME), true); // TODO chiama il db
    return Response.ok(pong, MediaType.APPLICATION_JSON).build();
  }
  catch (UnknownHostException ex) {
    logger.log(Level.SEVERE, "I cannot find the host name in ApplicativePingService.ping()", ex);
    return Response.status(Response.Status.INTERNAL_SERVER_ERROR).build();
  }
}


Questa è la classe "Pong" che contiene i dati inviati dal server al client:

public class Pong {
  private String version;
  private String hostName;
  private String dateTime;
  private boolean databaseReady;

  public Pong() {
  }

  public Pong(final String version, final String hostName, final String dateTime, final boolean databaseReady) {

    this.version = version;
    this.hostName = hostName;
    this.dateTime = dateTime;
    this.databaseReady = databaseReady;
  }

  public void setVersion(String version) {
    this.version = version;
  }

  public void setHostName(String hostName) {
    this.hostName = hostName;
  }

  public void setDateTime(String dateTime) {
   this.dateTime = dateTime;
  }

  public String getVersion() {
    return version;
  }

  public String getHostName() {
    return hostName;
  }

  public String getDateTime() {
    return dateTime;
  }

  public boolean isDatabaseReady() {
    return databaseReady;
  }
}


Per quanto riguarda il client, voglio usare la nuova interfaccia standard per i client REST di Java Enterprise sfruttando le librerie fornite già da Wildfly, nel pom.xml le dipendenze da chiamare sono:


<dependency>
  <groupid>javax</groupid>
  <artifactid>javaee-api</artifactid>
  <version>6.0</version>
  <type>jar</type>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupid>javax</groupid>
  <artifactid>javaee-web-api</artifactid>
  <version>7.0</version>
  <scope>provided</scope>
</dependency>
<dependency>
  <groupid>javax.enterprise</groupid>
  <artifactid>cdi-api</artifactid>
  <version>1.2</version>
  <scope>provided</scope>
</dependency>


Ora veniamo al codice del client più importante: un metodo che fa n chiamate a seconda del parametro passato:
Per mappare i dati JSon che arrivano dal server, usiamo la libreria jackson

import com.fasterxml.jackson.core.JsonParseException;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;



public List ping() {
  final List result = new ArrayList<>(this.pingCount);
  final ObjectMapper objectMapper = new ObjectMapper();
  Client client = ClientBuilder.newClient();
  try {
    for (int i = 0; i < pingCount; i++) {

      final LocalDateTime before = LocalDateTime.now(); 
      boolean ok = true; 
      Pong pong = null;
      String message = null;
      boolean databaseReady = false; 
      objectMapper.registerModule(new JavaTimeModule());
      WebTarget resource = client.target(address); 
      Response response = resource.request(MediaType.APPLICATION_JSON).get();
      ok = (response.getStatus() == Response.Status.OK.getStatusCode()); 
      if (ok) {
        message = response.readEntity(String.class); 
        ObjectMapper mapper = new ObjectMapper();
        try { 
          pong = mapper.readValue(message, new TypeReference() { });
          ok = (pong != null); 

          if (ok)
            databaseReady = pong.isDatabaseReady();
        } catch (JsonParseException | 
JsonMappingException | IOException e)  {
          ok = false
          e.printStackTrace();
        }
      }

      final LocalDateTime after = LocalDateTime.now();
      final Duration deltaTime = Duration.between(before, after);
      result.add(new PingResult(ok, deltaTime, pong, message, databaseReady));
    }
  } finally {
    if (client != null)
      client.close();
  }
    return result;
}


Questo codice chiama il metodo sopra per chiamare il server n volte, poi formatta la durata in minuti, secondi e millisecondi (forse solo il Java 9 vedremo dei metodi comodi per estrarre le parti da una Duration, perché i metodi attuali effettuano una conversione in una specifica unità di misura, così è veramente scomoda). Inoltre calcola la durata media di una chiamata.

Per mappare i dati JSon che arrivano dal server, usiamo la libreria jackson

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;



enum CommandLineState { UNDETERMINATE, PING_NUM, ADDRESS };

public static void main(String[] args) {
  int pingRepetition=4;
  String serverAddress = null;
  boolean showResponse = false;
  CommandLineState actualState = CommandLineState.UNDETERMINATE;

  for (String argument : args) {
    switch (actualState) {
    case UNDETERMINATE:
      if (argument.toLowerCase().startsWith("-c")) {
        if (argument.length() > 2)
          pingRepetition = Integer.parseInt(argument.substring(2));
        else
          actualState = CommandLineState.PING_NUM;
      }
      else {
        if ("-showResponse".equals(argument)) {
          showResponse = true;
        else
          serverAddress = argument;


        actualState = CommandLineState.UNDETERMINATE;
        }

        break;
      case PING_NUM:
        pingRepetition = Integer.parseInt(argument.substring(2));
        actualState = CommandLineState.UNDETERMINATE;
        break;
      case ADDRESS:
        serverAddress = argument;
        actualState = CommandLineState.UNDETERMINATE;
        break;
      }
  }

  if ((serverAddress == null) || serverAddress.isEmpty()) {
    System.err.println("Indirizzo del server mancante");
    System.exit(-1);
  }
  if (pingRepetition < 1) {

    System.err.println("Il numero di ripetizioni deve essere maggiore di 0");           System.exit(-2);
  }

  final AppPing pinger = new AppPing(serverAddress, pingRepetition); List result =   pinger.ping();
  if (result != null) {
    // Le prossime 3 righe possono provenire da un file di risorse
    final String outputStr = "%s versione %s ha risposto %s database %s in tempo: %s";
    final String outputAvg= "La durata media è : %s";
    final String rawOutputMsg = "La risposta completa del server è: ";
    final String OK = "OK";
    final String NOT_OK = "NON OK";
    Duration totalDuration = Duration.ZERO;
    for (PingResult pingResult : result) {
      System.out.println();

      final Pong pong = pingResult.getPong();

      String pingStatus = pingResult.isOk() ? OK : NOT_OK;
      String dataBaseStatus = pingResult.isDatabaseReady() ? OK : NOT_OK;

      Duration duration = pingResult.getDuration();
      totalDuration = totalDuration.plus(duration);
      String durationStr = LocalTime.MIDNIGHT.plus(duration).format(DateTimeFormatter.ofPattern("mm:ss:SSS"));

      System.out.format(outputStr, pong.getHostName(), pong.getVersion(),      pingStatus, dataBaseStatus, durationStr);

      if (showResponse) {
        System.out.println();
        System.out.println(rawOutputMsg);
        System.out.println(pingResult.getMessage());
      }
    }

    System.out.println();
    final Duration averageDuration = totalDuration.dividedBy(pingRepetition);
    final String avgResponse =     LocalTime.MIDNIGHT.plus(averageDuration).format(DateTimeFormatter.ofPattern("mm:ss:SSS"));
    System.out.format(outputAvg, avgResponse);
    System.out.println();
  }
}