Java e i protocolli per IoT

L’Internet of Things (IoT) richiede canali di comunicazione affidabili tra dispositivi, gateway ed ecosistemi cloud. Java, grazie al vasto ecosistema di librerie, alla portabilità su CPU/OS differenti e alle ottimizzazioni recenti della JVM, è una scelta robusta per implementare client, gateway ed endpoint server-side. In questo articolo analizziamo i principali protocolli usati in IoT e mostriamo come integrarli in Java, toccando sicurezza, performance e best practice.

Panoramica rapida dei protocolli

Protocollo Livello Caso d’uso tipico Caratteristiche chiave Supporto Java
MQTT 3.1.1/5.0 Applicazione su TCP Telemetria leggera, pub/sub QoS 0/1/2, topic, retained, sessioni Eclipse Paho, HiveMQ Client, Vert.x MQTT
CoAP Applicazione su UDP REST leggero in reti lossy CON/NON, Observe, block-wise Eclipse Californium (Cf)
HTTP/REST Applicazione su TCP APIs, gestione dispositivi Ampliamente supportato, caching, proxy Spring Boot, JAX-RS/Jakarta REST, Vert.x
AMQP 1.0 Messaggistica su TCP Code/stream affidabili Routing, transazioni, flow control Qpid JMS/Proton-J
DDS Data-centric pub/sub Tempo reale, robotica/OT QoS ricche, discovery, RT eProsima Fast DDS (bindings), OpenDDS
OPC UA OT/Industriale Industrial IoT, PLC/SCADA Modello dati ricco, sicurezza Eclipse Milo

Quando scegliere cosa

  • MQTT: flussi telemetrici e comandi con vincoli di banda/alimentazione. Broker diffusi: Mosquitto, EMQX, HiveMQ.
  • CoAP: reti UDP/6LoWPAN, dispositivi molto vincolati, pattern REST “leggero”.
  • HTTP/REST: provisioning, gestione dispositivi, integrazione IT standard.
  • AMQP: pipeline affidabili, richiesta di semantiche di coda e transazioni.
  • DDS: robotica, veicoli autonomi, control loop con QoS fine.
  • OPC UA: shop-floor e interoperabilità OT/IT.

Java nel mondo IoT: runtime ed ecosistema

  • JDK: usare LTS (17/21). Su ARM (es. Raspberry Pi) OpenJDK gira bene; valutare cds e jlink per immagini ridotte.
  • Framework: Spring Boot per rapidità; Vert.x/Micronaut/Quarkus per footprint e reattività; Jakarta EE per standardizzazione.
  • Reactive: Project Reactor, RxJava, Vert.x per backpressure e I/O non bloccante.
  • Native: GraalVM Native Image per startup rapidi su gateway ed edge (attenzione alla configurazione di riflessione).

Esempi pratici

MQTT con Eclipse Paho (sincrono)


// Gradle: implementation("org.eclipse.paho:org.eclipse.paho.client.mqttv3:1.2.5")
import org.eclipse.paho.client.mqttv3.*;

public class MqttPublisher {
  public static void main(String[] args) throws Exception {
    String broker = "tcp://localhost:1883";
    String clientId = "java-edge-01";
    MqttClient client = new MqttClient(broker, clientId, new MqttDefaultFilePersistence());
    MqttConnectOptions opts = new MqttConnectOptions();
    opts.setAutomaticReconnect(true);
    opts.setCleanSession(false);
    opts.setUserName("device01");
    opts.setPassword("s3cr3t".toCharArray());
    client.connect(opts);

    MqttMessage msg = new MqttMessage("{\"t\":23.7}".getBytes());
    msg.setQos(1); // QoS at-least-once
    client.publish("sensors/roomA/temp", msg);

    client.disconnect();
    client.close();

  }
} 

MQTT 5.0 con HiveMQ Client (reattivo)


// Gradle: implementation("com.hivemq:hivemq-mqtt-client:1.3.3")
import com.hivemq.client.mqtt.mqtt5.*;
import com.hivemq.client.mqtt.datatypes.MqttQos;

public class Mqtt5Reactive {
  public static void main(String[] args) {
    var client = MqttClient.builder()
    .useMqttVersion5()
    .serverHost("broker.local")
    .serverPort(8883) // TLS
    .sslWithDefaultConfig()
    .identifier("gw-01")
    .buildAsync();

    client.connectWith().simpleAuth()
    .username("gw01").password("...".getBytes()).applySimpleAuth()
    .send().join();

    client.subscribeWith()
    .topicFilter("cmd/roomA/#")
    .qos(MqttQos.AT_LEAST_ONCE)
    .callback(p -> System.out.println("CMD: " + p.getTopic() + " " + new     String(p.getPayloadAsBytes())))
  .send().join();

    client.publishWith()
    .topic("telemetry/roomA/temp")
    .qos(MqttQos.AT_LEAST_ONCE)
    .payload("{\"t\":23.1}".getBytes())
    .send();

  }
} 

CoAP con Eclipse Californium


// Gradle: implementation("org.eclipse.californium:californium-core:3.11.0")
import org.eclipse.californium.core.*;

public class CoapGet {
  public static void main(String[] args) {
    CoapClient client = new CoapClient("coap://[fe80::1%eth0]/sensors/temp");
    String text = client.get().getResponseText();
    System.out.println("Temp: " + text);
  }
} 

REST HTTP con Spring Boot (endpoint di telemetria)


// Gradle: implementation("org.springframework.boot:spring-boot-starter-web")
import org.springframework.web.bind.annotation.*;

@RestController
class TelemetryController {
  record Telemetry(String deviceId, double t, long ts) {}
  @PostMapping("/ingest")
  void ingest(@RequestBody Telemetry telemetry) {
  // TODO: validazione e invio su coda
    System.out.println("RX " + telemetry);
  }
} 

OPC UA client con Eclipse Milo (lettura variabile)


// Gradle: implementation("org.eclipse.milo:sdk-client:0.6.14")
import org.eclipse.milo.opcua.sdk.client.*;
import org.eclipse.milo.opcua.stack.core.types.builtin.*;

public class OpcUaRead {
  public static void main(String[] args) throws Exception {
    var client = OpcUaClient.create("opc.tcp://plc.local:4840");
    client.connect().get();
    Variant v = client.readValue(0, TimestampsToReturn.Both,
    new NodeId(2, "RoomA.Temp")).get().getValue().getValue();
    System.out.println("Temp: " + v);
    client.disconnect().get();
  }
} 

Sicurezza end-to-end

  • TLS/DTLS: TLS per MQTT/HTTP/AMQP; DTLS per CoAP. Usare TLS 1.2+ e cifre moderne.
  • Autenticazione: mTLS con certificati per dispositivi; in alternativa OAuth 2.0 Device Flow/JWT per gateway.
  • Provisioning: onboarding con CSR firmate, rotazione chiavi, revoca CRL/OCSP.
  • Autorizzazione: ACL sui topic MQTT, ruoli-per-risorsa su REST/AMQP, namespace OPC UA.
  • Hardening: secure boot, TPM/SE, minima superficie d’attacco, aggiornamenti OTA firmati (es. hawkBit).

Pattern architetturali

  • Edge Gateway: aggrega BLE/Zigbee/Modbus verso MQTT/AMQP/HTTP.
  • Digital Twin: mappa device state in un modello (Eclipse Ditto) con comandi asincroni.
  • Command & Control: topic separati per telemetria/command/reply; correlazione con correlationId.
  • Store & Forward: buffer su disco in caso di rete intermittente.

Qualità del servizio e affidabilità

  • MQTT QoS: 0 (fire-and-forget), 1 (at-least-once), 2 (exactly-once, più costoso).
  • CoAP: messaggi CON con ritrasmissione; Observe per notifiche.
  • AMQP: acknowledgements espliciti, link credit (flow control).
  • DDS: QoS granulari (latenza, affidabilità, durabilità).

Prestazioni e footprint

  • GC e latenza: ZGC/Shenandoah per bassa pausa su gateway; G1 ben bilanciato su server.
  • I/O non bloccante: Netty/Vert.x riducono thread e memoria su endpoint ad alta concorrenza.
  • Ottimizzazioni: record class per payload, Project Loom (virtual threads) per semplificare senza perdere scalabilità.
  • Immagini ridotte: jlink per creare runtime dedicati; -XX:+UseContainerSupport in container.

Serializzazione dei payload

  • JSON: interoperabile, più verboso. Usare Jackson/JSON-B, preferire formati compatti su reti strette.
  • CBOR: binario, ottimo con CoAP; supporto Jackson CBOR.
  • Protobuf/Avro: schema-first, compatibilità evolutiva, adatto a pipeline AMQP/HTTP/MQTT.

Osservabilità e gestione

  • Metrics/Tracing: Micrometer/OpenTelemetry con esportazione su Prometheus/OTLP.
  • Health: endpoint di liveness/readiness, watchdog a bordo dispositivo.
  • Test: Testcontainers per far girare broker (Mosquitto/EMQX) in CI; simulazioni di rete (latency/loss).

Esempi di robustezza applicativa


import java.time.Duration;
import java.util.concurrent.ThreadLocalRandom;

class Retry {
  static Duration jitter(Duration base) {
    long ms = base.toMillis();
    long delta = ThreadLocalRandom.current().nextLong(ms / 2);
    return Duration.ofMillis(ms - ms/4 + delta);
  }
 static <T> T withRetries(IOCall<T> call, int max) throws Exception {
   Duration backoff = Duration.ofMillis(200);
   for (int i = 0; i < max; i++) {
      try { return call.run(); }
      catch (TransientIoException e) {
        Thread.sleep(jitter(backoff).toMillis());
        backoff = backoff.multipliedBy(2);
      }
   }
   throw new Exception("Too many retries");
}
interface IOCall<T> { T run() throws Exception; }
  static class TransientIoException extends Exception {}
} 

Linee guida di scelta

  1. Preferire MQTT per telemetria continua e comandi, HTTP per provisioning e gestione, CoAP per ambienti UDP/low-power, AMQP per affidabilità e code, DDS/OPC UA in contesti OT/real-time.
  2. Definire contratti di payload (schema o versioni) e politiche di compatibilità.
  3. Imporre mTLS, rotazione periodica delle credenziali e autorizzazioni minime.
  4. Progettare per intermittenza (buffering, retry con backoff, idempotenza).
  5. Integrare observability dall’inizio: metriche, log strutturati, trace distribuiti.

Conclusioni

Java resta una piattaforma matura per l’IoT grazie a portabilità, toolchain e librerie per tutti i protocolli chiave. Scegliendo il protocollo giusto per il contesto (banda, energia, affidabilità, tempo reale) e applicando buone pratiche di sicurezza e robustezza, si ottengono soluzioni scalabili dall’edge al cloud. L’ecosistema open source (Paho, Californium, Vert.x, Milo, Ditto, Hono) copre l’intera catena dal dispositivo al digital twin, consentendo di concentrarsi sul valore applicativo anziché sulle integrazioni di base.

Torna su