Modularità in Java 9: ​​impilabile con Project Jigsaw, Penrose e OSGi

Questo articolo fornisce una panoramica di proposte, specifiche e piattaforme volte a rendere la tecnologia Java più modulare in Java 9. Discuterò i fattori che contribuiscono alla necessità di un'architettura Java più modulare, descriverò brevemente e confronterò le soluzioni che sono state proposte, e introdurre i tre aggiornamenti di modularità pianificati per Java 9, compreso il loro potenziale impatto sullo sviluppo Java.

Perché abbiamo bisogno della modularità Java?

La modularità è un concetto generale. Nel software, si applica alla scrittura e all'implementazione di un programma o di un sistema informatico come un numero di moduli unici, piuttosto che come un singolo progetto monolitico. Viene quindi utilizzata un'interfaccia standardizzata per consentire ai moduli di comunicare. Il partizionamento di un ambiente di costrutti software in moduli distinti ci aiuta a ridurre al minimo l'accoppiamento, ottimizzare lo sviluppo delle applicazioni e ridurre la complessità del sistema.

La modularità consente ai programmatori di eseguire test di funzionalità in isolamento e di impegnarsi in attività di sviluppo parallelo durante un dato sprint o progetto. Ciò aumenta l'efficienza durante l'intero ciclo di vita dello sviluppo del software.

Alcuni attributi caratterizzanti di un modulo originale sono:

  • Un'unità di distribuzione autonoma (accoppiamento libero)
  • Un'identità coerente e univoca (ID modulo e versione)
  • Requisiti e dipendenze facilmente identificati e rilevati (tempo di compilazione standard e strutture di distribuzione e meta-informazioni)
  • Un'interfaccia aperta e comprensibile (contratto di comunicazione)
  • Dettagli di implementazione nascosti (incapsulamento)

I sistemi creati per elaborare in modo efficiente i moduli dovrebbero eseguire le seguenti operazioni:

  • Supporta la modularità e l'individuazione delle dipendenze in fase di compilazione
  • Esegui i moduli in un ambiente di runtime che supporta una facile distribuzione e ridistribuzione senza tempi di inattività del sistema
  • Implementa un ciclo di vita di esecuzione chiaro e robusto
  • Fornire servizi per un facile registro e rilevamento dei moduli

Le soluzioni orientate agli oggetti, ai componenti e ai servizi hanno tutte tentato di consentire la pura modularità. Ogni soluzione ha una propria serie di stranezze che le impediscono di raggiungere la perfezione modulare, tuttavia. Consideriamo brevemente.

Classi e oggetti Java come costrutti modulari

La natura orientata agli oggetti di Java non soddisfa i requisiti di modularità? Dopo tutto, la programmazione orientata agli oggetti con Java sottolinea e talvolta impone l'unicità, l'incapsulamento dei dati e l'accoppiamento libero. Sebbene questi punti siano un buon inizio, notate i requisiti di modularità che non sono soddisfatti dal framework orientato agli oggetti di Java: l'identità a livello di oggetto non è affidabile; le interfacce non sono dotate di versione: e le classi non sono univoche a livello di distribuzione. L'accoppiamento allentato è una best practice, ma certamente non applicata.

Riutilizzare le classi in Java è difficile quando le dipendenze di terze parti sono così facilmente abusate. Strumenti in fase di compilazione come Maven cercano di affrontare questa lacuna. Le convenzioni e i costrutti del linguaggio dopo il fatto, come l'inserimento delle dipendenze e l'inversione del controllo, aiutano gli sviluppatori nei nostri tentativi di controllare l'ambiente di runtime e talvolta ci riescono, specialmente se usati con rigida disciplina. Sfortunatamente, questa situazione lascia il compito di creare un ambiente modulare fino a convenzioni e configurazioni del framework proprietario.

Java aggiunge anche spazi dei nomi dei pacchetti e visibilità dell'ambito al mix come mezzo per creare meccanismi modulari per la compilazione e la distribuzione. Ma queste caratteristiche del linguaggio possono essere facilmente eluse, come spiegherò.

Pacchetti come soluzione modulare

I pacchetti tentano di aggiungere un livello di astrazione al panorama di programmazione Java. Forniscono servizi per spazi dei nomi di codifica e contesti di configurazione univoci. Purtroppo, però, le convenzioni sui pacchetti possono essere facilmente aggirate, portando spesso a un ambiente di pericolosi accoppiamenti in fase di compilazione.

Lo stato di modularità in Java attualmente (a parte OSGi, di cui parlerò a breve) è più spesso ottenuto utilizzando spazi dei nomi dei pacchetti, convenzioni JavaBeans e configurazioni di framework proprietarie come quelle trovate in Spring.

I file JAR non sono abbastanza modulari?

I file JAR e l'ambiente di distribuzione in cui operano migliorano notevolmente le numerose convenzioni di distribuzione legacy altrimenti disponibili. Ma i file JAR non hanno unicità intrinseca, a parte un numero di versione usato raramente, che è nascosto in un manifest .jar. Il file JAR e il manifest facoltativo non vengono utilizzati come convenzioni di modularità all'interno dell'ambiente di runtime Java. Quindi i nomi dei pacchetti delle classi nel file e la loro partecipazione a un classpath sono le uniche parti della struttura JAR che conferiscono modularità all'ambiente di runtime.

In breve, i JAR sono un buon tentativo di modularizzazione, ma non soddisfano tutti i requisiti per un ambiente veramente modulare. Framework e piattaforme come Spring e OSGi utilizzano modelli e miglioramenti alla specifica JAR per fornire ambienti per la creazione di sistemi modulari e molto capaci. Nel tempo, tuttavia, anche questi strumenti soccomberanno a uno sfortunato effetto collaterale dell'inferno JAR della specifica JAR!

Classpath / JAR inferno

Quando l'ambiente di runtime Java consente meccanismi di caricamento JAR arbitrariamente complessi, gli sviluppatori sanno di trovarsi nell'inferno classpath o nell'inferno JAR . Diverse configurazioni possono portare a questa condizione.

Innanzitutto, si consideri una situazione in cui uno sviluppatore di applicazioni Java fornisce una versione aggiornata dell'applicazione e l'ha inserita in un file JAR con lo stesso identico nome della vecchia versione. L'ambiente di runtime Java non fornisce funzioni di convalida per determinare il file JAR corretto. L'ambiente di runtime caricherà semplicemente le classi dal file JAR che trova per primo o che soddisfa una delle tante regole del percorso di classe. Questo porta a comportamenti inaspettati nella migliore delle ipotesi.

Un altro esempio dell'inferno JAR si verifica in cui due o più applicazioni o processi dipendono da versioni diverse di una libreria di terze parti. Utilizzando le funzionalità di caricamento delle classi standard, in fase di esecuzione sarà disponibile solo una versione della libreria di terze parti, causando errori in almeno un'applicazione o processo.

Un sistema di moduli Java completo ed efficiente dovrebbe facilitare la separazione del codice in moduli distinti, facilmente comprensibili e liberamente accoppiati. Le dipendenze dovrebbero essere chiaramente specificate e applicate rigorosamente. Dovrebbero essere disponibili strutture che consentano l'aggiornamento dei moduli senza avere un effetto negativo su altri moduli. Un ambiente di runtime modulare dovrebbe consentire configurazioni specifiche per un particolare dominio o mercato verticale, riducendo così il tempo di avvio e l'impronta di sistema dell'ambiente.

Soluzioni di modularità per Java

Insieme alle funzionalità di modularità menzionate finora, gli sforzi recenti ne aggiungono altre. Le seguenti funzionalità hanno lo scopo di ottimizzare le prestazioni e abilitare l'estensione dell'ambiente di runtime:

  • Codice sorgente segmentato : codice sorgente separato in segmenti distinti e memorizzati nella cache, ciascuno dei quali contiene un tipo specifico di codice compilato. Gli obiettivi includono saltare il codice non di metodo durante le operazioni di garbage sweep, build incrementali e una migliore gestione della memoria.
  • Applicazioni in fase di compilazione : costrutti di linguaggio per applicare spazi dei nomi, controllo delle versioni, dipendenze e altro
  • Funzionalità di distribuzione : supporto per la distribuzione di ambienti di runtime scalati in base a esigenze specifiche, come quelle di un ambiente per dispositivi mobili.

Un certo numero di specifiche e framework di modularità hanno cercato di facilitare queste funzionalità, e alcuni sono recentemente diventati i primi nelle proposte per Java 9. Di seguito è riportata una panoramica delle proposte di modularità di Java.

JSR (Java Specification Request) 277

Attualmente inattivo è Java Specification Request (JSR) 277, il Java Module System; introdotto da Sun nel giugno del 2005. Questa specifica copriva la maggior parte delle stesse aree di OSGi. Come OSGi, JSR 277 definisce anche il rilevamento, il caricamento e la coerenza dei moduli, con supporto scarso per le modifiche del runtime e / o il controllo dell'integrità.

Gli svantaggi di JSR 277 includono:

  • Nessun caricamento e scaricamento dinamico di moduli / bundle
  • Nessun runtime controlla l'unicità dello spazio di classe

OSGi (Open Service Gateway Initiative)

Introdotta dalla OSGI Alliance nel novembre del 1998, la piattaforma OSGI è la risposta di modularità più ampiamente utilizzata alla domanda standard formale per Java. Attualmente alla versione 6, la specifica OSGi è ampiamente accettata e utilizzata, soprattutto negli ultimi tempi.

In sostanza, OSGi è un sistema modulare e una piattaforma di servizi per il linguaggio di programmazione Java che implementa un modello di componenti completo e dinamico sotto forma di moduli, servizi, bundle distribuibili e così via.

I livelli primari dell'architettura OSGI sono i seguenti:

  • Ambiente di esecuzione : l'ambiente Java (ad esempio, Java EE o Java SE) in cui verrà eseguito un bundle.
  • Modulo : dove il framework OSGi elabora gli aspetti modulari di un bundle. I metadati del bundle vengono elaborati qui.
  • Ciclo di vita : l'inizializzazione, l'avvio e l'arresto dei pacchetti avvengono qui.
  • Registro dei servizi : dove i bundle elencano i loro servizi per essere rilevati da altri bundle.

Uno dei maggiori svantaggi di OSGi è la mancanza di un meccanismo formale per l'installazione dei pacchetti nativi.

JSR 291

JSR 291 è un framework di componenti dinamici per Java SE che si basa su OSGi, è attualmente nella fase finale di sviluppo. Questo sforzo si concentra sul portare OSGi nel mainstream Java, come è stato fatto per l'ambiente mobile Java da JSR 232.

JSR 294

JSR 294 definisce un sistema di meta-moduli e delega l'effettiva forma di realizzazione dei moduli collegabili (versioni, dipendenze, restrizioni, ecc.) A fornitori esterni. Questa specifica introduce estensioni del linguaggio, come "superpackage" e moduli correlati gerarchicamente, per facilitare la modularità. Anche l'incapsulamento rigoroso e le unità di compilazione distinte fanno parte dell'attenzione delle specifiche. JSR 294 è attualmente dormiente.

Progetto Jigsaw

Project Jigsaw è il candidato più probabile per la modularità in Java 9. Jigsaw cerca di utilizzare costrutti di linguaggio e configurazioni di ambiente per definire un sistema di moduli scalabili per Java SE. Gli obiettivi principali di Jigsaw includono:

  • Rende molto facile ridimensionare il runtime di Java SE e il JDK su piccoli dispositivi.
  • Migliorare la sicurezza di Java SE e JDK vietando l'accesso alle API JDK interne e applicando e migliorando il SecurityManager.checkPackageAccessmetodo.
  • Miglioramento delle prestazioni dell'applicazione tramite l'ottimizzazione del codice esistente e facilitazione delle tecniche di ottimizzazione del programma previsionale.
  • Semplificare lo sviluppo di applicazioni all'interno di Java SE consentendo di costruire librerie e applicazioni da moduli forniti dagli sviluppatori e da un JDK modulare
  • Richiedere e applicare un insieme finito di vincoli di versione

JEP (Java Enhancement Proposal) 200

Java Enhancement Proposal 200, creata nel luglio del 2014, cerca di definire una struttura modulare per JDK. JEP 200 si basa sul framework Jigsaw per facilitare la segmentazione di JDK, secondo Java 8 Compact Profiles, in set di moduli che possono essere combinati in fase di compilazione, build time e deploy time. Queste combinazioni di moduli possono quindi essere distribuite come ambienti di runtime ridimensionati composti da moduli conformi a Jigsaw.

JEP 201

JEP 201 cerca di costruire su Jigsaw per riorganizzare il codice sorgente JDK in moduli. Questi moduli possono quindi essere compilati come unità distinte da un sistema di compilazione avanzato che impone i limiti dei moduli. JEP 201 propone uno schema di ristrutturazione del codice sorgente in tutto il JDK che enfatizza i confini dei moduli al livello più alto degli alberi del codice sorgente.

Penrose

Penrose gestirà l'interoperabilità tra Jigsaw e OSGi. In particolare, faciliterebbe la capacità di modificare i micro-kernel OSGi in modo che i bundle in esecuzione nel kernel modificato utilizzino i moduli Jigsaw. Si basa sull'utilizzo di JSON per descrivere i moduli.

Piani per Java 9

Java 9 è una versione principale unica per Java. Ciò che lo rende unico è l'introduzione di componenti e segmenti modulari in tutto il JDK . Le caratteristiche principali che supportano la modularizzazione sono:

  • Codice sorgente modulare : in Java 9, JRE e JDK verranno riorganizzati in moduli interoperabili. Ciò consentirà la creazione di runtime scalabili che possono essere eseguiti su piccoli dispositivi.
  • Cache di codice segmentata : sebbene non sia strettamente una struttura modulare, la nuova cache di codice segmentata di Java 9 seguirà lo spirito della modularizzazione e godrà di alcuni degli stessi vantaggi. La nuova cache del codice prenderà decisioni intelligenti per compilare segmenti di codice a cui si accede di frequente in codice nativo e archiviarli per una ricerca ottimizzata e un'esecuzione futura. L'heap sarà anche segmentato in 3 unità distinte: codice non di metodo che verrà memorizzato permanentemente nella cache; codice che ha un ciclo di vita potenzialmente lungo (noto come "codice non profilato"); e codice transitorio (noto come "codice profilato").
  • Applicazione in fase di compilazione: il sistema di compilazione verrà migliorato, tramite JEP 201, per compilare e applicare i confini dei moduli.
  • Strutture di distribuzione : all'interno del progetto Jigsaw verranno forniti strumenti che supporteranno i confini, i vincoli e le dipendenze dei moduli al momento della distribuzione.

Versione ad accesso anticipato di Java 9

Sebbene la data esatta di rilascio di Java 9 rimanga un mistero, è possibile scaricare una versione ad accesso anticipato su Java.net.

In conclusione

Questo articolo è stato una panoramica della modularità all'interno della piattaforma Java, comprese le prospettive di modularità in Java 9. Ho spiegato come problemi di vecchia data come l'inferno del percorso di classe contribuiscono alla necessità di un'architettura Java più modulare e ho discusso alcune delle nuove modularità più recenti caratteristiche proposte per Java. Ho quindi descritto e contestualizzato ciascuna delle proposte o piattaforme di modularità Java, inclusi OSGi e Project Jigsaw.

La necessità di un'architettura Java più modulare è chiara. Gli attuali tentativi sono falliti, anche se OSGi si avvicina molto. Per la versione Java 9 Project Jigsaw e OSGi saranno i principali attori nello spazio modulare per Java, con Penrose che potrebbe fornire il collante tra di loro.

Questa storia, "Modularità in Java 9: ​​Stacking up with Project Jigsaw, Penrose e OSGi" è stata originariamente pubblicata da JavaWorld.