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