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




lunedì 28 settembre 2015

Come linkare il client C di PostgreSQL da un programma C++ usando CMake

Mi sono imbattuto di recente sul problema di linkare una libreria condivisa con il driver di PostgreSQL, il tutto usando Linux su una macchina a 64 bit.

Il programma è in C++ quindi la prima scelta è ricaduta su libpqxx.
Con grande delusione ho notato che libpqxx da problemi se il programma principale segue lo standard C++ 11 o l'ultimo C++ 14.
Poco male, ho pensato di linkare direttamente il client C di PostgreSQL, tuttavia la documentazione in giro non è molta.
Sono riuscito nell'impresa con questi accorgimenti:

nel file CMakeList.txt bisogna mettere
link_directories (/usr/lib64)
target_link_libraries (nomedelprogetto pq)

pq indica il file libpq.so, che si trova in /usr/lib64.

Nella parte C++ bisogna che l'inclusione della parte del driver indichi che si tratta di una libreria in C, quindi:

extern "C" {
    include <libpq-fe.h>
}

Questo permette di usare strutture e funzioni del driver, come PQconnectdb, PQexec, PGconn, PGresult, PQgetvalue, PQclear o PQfinish ecc...


venerdì 11 settembre 2015

Riferimenti Incrociati

Una lezione che non mi aspettavo, durante questo mio studio del C++ è che i riferimenti incrociati sono un anti pattern, una pratica da evitare.
Il C++ onestamente gestisce malissimo i riferimenti incrociati.
Con riferimento incrociato si intente una classe A che ha un riferimento a una classe B, mentre proprio quest'ultima ha un riferimento alla classe A.

Esiste un trucco che si chiama class forwarding che consiste nel dichiarare la classe chiamata in una delle due, però non si possono chiamare i metodi degli oggetti di tale classe.

Diverso è il caso nel quale si ricorra all'uso dei vecchi puntatori puri, i quali però hanno il tragico difetto che non dicono chi è l'incaricato di liberare la memoria.

Questo succede perchè il compilatore, mentre lavora su A, non conosce la dimensione di B e, mentre esamina B, non ha ancora finito di compilare A.
Mentre in Java il problema non si presenta, perché il compilatore fa un lavoro più semplice, trattandosi di un linguaggio con molte meno regole e sfumature, è impossibile per un compilatore C++ lavorare su classi parzialmente compilate.

Funziona con i puntatori perché la dimensione del puntatore è fissa, indipendentemente dall'oggetto puntato.

Dopo il primo shock quando mi sono accorto di questa limitazione, ho fatto un po' di ricerche.

Effettivamente i riferimenti circolari sono una possibile fonte di problemi, si pensi per esempio ai loop infiniti. C'è chi li considera un esempio di cattiva progettazione. Personalmente considero questa affermazione come un po' forte, di sicuro con il C++ sono un problema.

In Java li ho sempre visti con grande libertà. Ripensare un programma con molti riferimenti circolari, di sicuro porta a una struttura finale decisamente più semplice.


mercoledì 9 settembre 2015

Puntatori intelligenti per problemi stupidi

Negli ultimi mesi ho studiato una parte delle novità introdotte negli ultimi anni nella libreria standard di C++.

I puntatori puri, le famose stelline * tanto famose con il C sono di fatto quasi deprecati, alcuni autori dicono che non ci sono ragioni oggi per usarli se non quando si presentino problemi di prestazioni o in caso di risorse molto ridotte.

Devo dire che districarmi tra puntatori, riferimenti, shared_ptr e unique_ptr per me abituato a Java, è stato piuttosto difficile: la gestione della memoria "semi automatica" del nuovo C++ e la scelta tra tenere un'istanza nello stack o nello heap impongono quasi di imparare a programmare di nuovo.

Attualmente il mio livello di comprensione mi porta alle seguenti conclusioni.

Effettivamente il puntatore puro è evitabile in un normale programma gestionale, a meno che non ci si debba interfacciare con delle librerie che lo richiedono.


In C++ oggi è importante stabilire quale oggetto è proprietario di un altro e il proprietario è quello che determina il rilascio del secondo oggetto, cioé quando il proprietario è distrutto, questo determina la distruzione del secondo oggetto. Tale operazione si può fare in automatico, usando un unique_ptr.

Nel caso di variabili di istanza, io ho individuato i seguenti casi:

Il riferimento indicato con la e commerciale & è comodissimo per fare riferimento all'istanza di un altro oggetto se sicuramente mai nullo e mai usato dopo il suo rilascio, senza che il primo ne sia il proprietario.

Una variabile di istanza di un oggetto di proprietà mai nullo, se non è troppo grande, può stare nello stack senza puntatori di alcun genere o riferimenti.

Un riferimento ad un oggetto di proprietà, ma che può valere null è un buon candidato per essere puntato da un unique_ptr o da uno shared_ptr, perché il riferimento & non può mai valere null.

Una variabile di istanza di un oggetto che è di proprietà di un altro oggetto, che potrebbe valere null, potrebbe essere un candidato per uno shared_ptr.

Una lista concatenata o un vettore di oggetti che sono proprietà di altri oggetti, dovrebbe contenere degli std::reference_wrapper.

Una lista concatenata o un vettore (si chiamano contenitori in C++, sarebbero le Collections di Java) di oggetti di proprietà,  può mantenere tali oggetti nello stack.

L'uso di shared_obj non poi così limitato come si legge in giro. Si usa dove si potrebbe usare & ma c'è il rischio che l'oggetto puntato sia tolto dalla memoria prima del riferimento e dove si cerchi di chiamare i metodi dell'oggetto tolto.

Questo unito all'impossibilità di valere null, è un vero e proprio rischio nell'usare il riferimento &. A differenza di Java, in C++ è possibile per errore togliere dalla memoria un oggetto che ha dei riferimenti attivi, così come è possibile tentare di cancellare gli oggetti più di una volta, creando problemi com'è ovvio...

In generale, a meno che non si tratti di oggetti enormi, conviene tenere tutto nello stack, perché è più semplice, il problmema è se il dato può valere null, personalmente non capisco perché si sia rimandata l'introduzione della classe Optional allo standard C++ 17, ma pazienza...

Dopo questi mesi di studio, mi sento di affermare che il C++ offre diverse possibilità di gestione della memoria manuale, che però richiede molta attenzione.


venerdì 21 agosto 2015

No, non sono un mago

A volte ci si aspetta che l'informatico, sia esso un programmatore o un sistemista o magari anche un più generico consulente aziendale, sia in realtà un mago in grado di risolvere tutti i problemi di natura tecnica, indipendentemente dalla complessità e natura del problema.

Siamo persone, fatte di carne e ossa, che hanno studiato e hanno più o meno esperienza nel settore, ma non possiamo risolvere tutto.
Il problema, secondo me, sta nella scarsissima cultura informatica di molte persone con compiti dirigenziali, le quali semplicemente non si rendono conto di cosa si può chiedere, di cosa ci si può aspettare. Si può chiedere di tenere in equilibrio una matita per la punta, se non si hanno nozioni di fisica o se non si sa cos'è una matita...

Un altro aspetto che mia sempre sconcertato  è la scarsa propensione a formare il personale che lavora in ambito tecnologico. Pensare che la tecnologia oggi è sempre più mettere assieme parti fatte da altri, con dei comportamenti e delle interfacce che vanno conosciute. Le stesse persone che non vogliono formare i dipendenti sono le stesse che si aspettano risultati.

Sempre più spesso di vedono aziende che cercano personale già formato, anche su tecnologie nate per gli hobbisti, che un professionista con esperienza imparerebbe in meno di 15 giorni... L'importante è non investire sul personale, usare il know how della gente full time per un periodo limitato di tempo, finito il quale se si rende necessaria una nuova tecnologia, si cambia persona...

E' un mondo feroce.

Le aziende non cercano tecnologie moderne e sofisticate, che garantiscono libertà dai vendor e magari una forte scalabilità, bensì in tecnologie buone per fare prototipi, l'importante è avere qualcosa presto, poi ci penseremo.

Cosa ci salverà?
  1. Le aziende dovrebbero prendere coscienza che sviluppare un progetto non significa semplicemente "tirare fuori qualcosa", che la fase di progettazione e studio hanno il valore maggiore.
  2. Le aziende dovrebbero adottare le soluzioni open source, non soltanto quelle banali e semplici, ma soprattutto le piattaforme e gli strumenti che permettono, con una fase di configurazione, magari dopo diversi tentativi, di realizzare il servizio del quale hanno bisogno. In questo modo i costi ti tengono bassi (l'open source spesso cosa meno) e i tempi sono corti (il programma spesso è giù fatto, va soltanto configurato).
  3. Le aziende dovrebbero finalmente capire che il personale non è un costo, le persone fanno l'azienda, non il denaro, se le persone crescono, conoscono e si trovano bene, in un ambiente umano, non se ne andranno, sprecando così l'investimento fatto su di loro, anzi saranno proprio le persone a creare il profitto.
  4. Noi dovremmo renderci pienamente conto di cosa cerca il mercato e capire una volta per tutte che ciò che rende soldi nel breve periodo, difficilmente li renderà nel lungo periodo e viceversa...

lunedì 3 agosto 2015

Chi me lo fa fare? Me stesso

Oggi chattavo con un caro amico, un ingegnere bravissimo, il quale mi ha chiesto come mai sto studiando il C++, chi me lo fa fare.
Il C++ potrà servire per le nuove Internet of Things o nel mercato embedded, ma quando il ferro costa poco, come con i cloud...

Le mie previsioni sono più ottimistiche.
Su internet  le risorse si pagano salate e soltanto consumandone poche, l'applicazione costa poco.

Ci sono strumenti per scrivere applicazioni veloci e cross platform, si possono scrivere applicazioni che girano ovunque, c'è il sempre verde PHP.

Io ho il massimo rispetto per le soluzioni buone per le demo e per gli strumenti che aiutano i neofiti a cominciare. Anche io sinceramente sento che se conoscessi meglio il PHP avrei dei vantaggi per fare un'applicazione web in poco tempo e c'è tutt'ora una discreta richiesta di programmatori PHP, il motivo è che le applicazioni che necessitano di carichi di lavoro bassi e devono essere scritte con budget molto risicati, possono essere tranquillamente scritte in PHP, il problema semmai è quando queste condizioni cadono, aumenta il carico di lavoro o l'aplicazione cresce e diventa più complicata.

E' ancora possibile usare un linguaggio semplice come il PHP, però le cose diventano ancora più complicate che se si fosse usato Java o, perché no, il C++.

Ritengo che il C++ possa svilupparsi anche in ambito web, oggi più che nel passato, a patto che ci sia una libreria utile. In questo caso si può sviluppare velocemente. Questa è la mia scommessa.

Quindi chi mi fa studiare il C++ ? Io !

martedì 21 luglio 2015

C++ che passione

  Seguo il C++ da anni. Non ho mai avuto il coraggio fino ad ora di consigliarne l'uso in dei progetti in produzione, a meno che non mi fosse espressamente richiesto.
Si tratta di un linguaggio potentissimo e terribilmente complicato da imparare.

  Rimanere compatibile (provarci almeno) con dei sorgenti scritti quasi 40 anni fa, ha "arricchito" il linguaggio a tal punto che la stessa azione si può compiere a volte in 4 o più modi diversi, però soltanto un modo è il migliore, gli atri sono da conoscere solo per capire cosa fa il vecchio codice esistente...

  Prendo per esempio i puntatori.
  Sono una delle prime cause di errore. I puntatori puri oggi, dopo la pubblicazione del C++ 14, sono suggeriti come "evil", il diavolo da evitare come la peste.
Quindi abbiamo:
  • I puntatori puri * che provengono dal C e sono da evitare.
  • I riferimenti & che sono un po' più comodi ma non risolvono il problema della memoria mai rilasciata
  • Gli smart pointers (shared_ptr e weak_ptr) per i quali spesso si sconglia l'utilizzo, perché creano il problema di condividere la proprietà di un oggetto e non si sa, se questo possa essere effettivamente tolto
  • Lo unique_ptr che pare essere la panacea di tutti i mali, la risposta al problema del memory leak, però non può essere copiato, ma solo spostato...
  E' interessante notare come un vettore non possa contenere dei riferimenti, ma solo puntatori puri...si possono usare gli shared_ptr, ma li trovo un po' complicati. Oggi non saprei dire con certezza se i puntatori puri siano da evitare sempre nei progetti nuovi.

  A differenza di quanto avviene in Java o in .Net, dove un pesantissimo garbage collector cerca di ripulire la memoria non più utilizzata, gli smart pointers del C++ richiedono al programmatore di porsi il problema di chi è il proprietario del dato. Lo smart pointer rilascerà la memoria, quando sarà rilasciato il proprietario... è un nuovo approccio, a mio avviso semplice e geniale, per eliminare l'overhead del GC, croce e delizia dei programmatori, soprattutto neofiti, ma croce e basta dei sistemisti...

  Il problema fondamentale con il C++ secondo me non è tanto che il tempo di apprendimento sia lungo, che la curva di apprendimento sia ripida o che costi tantissimo formare il personale, il vero problema con il C++ è che ti costringe a PENSARE. Non si può programmare meccanicamente, non c'è posto per le scimmiette ammaestrate che "vanno", che "producono" codice ripetuto e da buttare via dopo 6 mesi... Con il C++ ci vuole un thread in testa che continuamente ti dica: "stai facendo veramente la cosa giusta?"

  Si può spendere un sacco di tempo su poche righe di codice, ho scritto spendere, non buttare...