L'evoluzione di Java: verso Java 8

V parte: Concludiamo l'analisi di Java SE 7di

Terminiamo con questo articolo la trattazione dedicata a Java SE 7, rimandando al prossimo articolo l'analisi di alcune novità previste per Java SE 8. In particolare, questo mese ci concentriamo sulle innovazioni introdotte nella libreria NIO, e su altri aggiornamenti a contorno, come il passaggio all‘Unicode 6.0.0, il supporto estensibile per i codici standard delle valute, le variazioni alle impostazioni della localizzazione (Local), le nuove feature della GUI (Nimbus, JLayer, Traslucent window), il supporto al protocollo STCP, e così via.

Introduzione

Una parte importante di Java SE 7 è indubbiamente rappresentata dalle innovazioni apportate alla API NIO: si tratta di una serie di variazioni abbastanza significative che hanno portato alla formulazione della versione NIO2. Le principali innovazioni sono condensate nei nuovi package: java.nio.file, java.nio.file.attribute, java.nio.file.spi e com.sun.nio.sctp (quest'ultimo contiene gli aggiornamenti necessari per supportare il nuovo protocollo SCTP descritto di seguito). Gli obiettivi primari di questa release (oltre a migliorie varie) puntano ad aumentare l'indipendenza dalla piattaforma di esecuzione, a fornire ulteriori servizi di alto livello e ad aggiungere il supporto per metadati e collegamenti simbolici come illustrato di seguito. Da notare che l'indipendenza dalla piattaforma è stata ottenuta (quasi paradossalmente) sfruttando massicciamente i servizi offerti dal sistema operativo e da implementazioni proprietarie.

Una feature molto interessante, che condensa le varie innovazioni, è il nuovo meccanismo di gestione dei file. Sebbene la tradizionale classe java.io.File sia ancora disponibile, è stato introdotto un nuovo meccanismo che ha al centro le nuove classi: java.nio.Path e java.nio.Files. Queste offrono una serie di funzionalità molto potenti e un codice molto robusto. Inoltre sono stati aggiunti importanti servizi di alto livello come la copia ed il trasferimento di file e directory. Per quanto concerne il supporto per i metadati, è stato introdotto il package java.nio.file.attribute allo scopo di fornire un supporto superiore ai metadata dei file, quali l'owner (metodo getOwner), informazioni circa la natura del file stesso se si tratta di un link simbolico o di un file regolare (isSymbolicLink, isRegularFile), il reperimento del timestamp dell'ultima modifica (getLastModifiedTime), i permessi di un file (getPosixFilePermissions), e così via.

Java NIO2 inoltre fornisce un supporto esteso ai link simbolici. Anche se ciò era parzialmente possibile con le versioni precedenti, vi erano alcuni problemi: per esempio non era immediato, e spesso neanche completamente possibile, scrivere del codice robusto per la navigazione ricorsiva e la gestione dei link che generavano cicli di link simbolici.

Anche la scalabilità è stata attentamente rivista. In particolare, una serie di metodi di java.io.file non presentavano una scalabilità ottimale. Per esempio, la richiesta della lista di una directory di grandi dimensioni di un server poteva bloccare seriamente il programma, senza menzionare il fatto che poteva anche causare problemi con la memoria e quindi generare il rifiuto dell'erogazione del servizio. Il nuovo package di I/O è stato disegnato prendendo nella giusta considerazione questi elementi e per assicurarsi che i vari servizi funzionino come atteso, indipendentemente dalle dimensioni dei file e directory. Oltre a queste feature, si è fatto in modo che i vari servizi presentino lo stesso funzionamento in diverse piattaforme (in passato questo non era il caso per una serie di servizi come per java.io.File.renameTo).

Breve storia di NIO

La prima versione delle API NIO (New Input/Output, "nuova libreria di input/output") fu introdotta con la versione Java J2SE 1.4 per mezzo della JSR51 (cfr. [1]). J2SE 1.4 fu rilasciata il 6 febbraio del 2002 con il nome di Merlin ("Smeriglio", piccolo falco in cui nome scientifico è Falco columbarius). L'API NIO venne introdotta per sopperire ad alcune lacune evidenziate dal package I/O originale di Java (java.io) implementato fin dalla versione Java 1.0. Sebbene per molti programmatori l'API Java NIO sia stata introdotta essenzialmente per disporre di operazioni di I/O non bloccanti, in realtà è stato fatto molto di più a partire dall'introduzione di un nuovo disegno integrato ed elegante. E allora, vediamo in breve quali furono le principali nuove feature introdotte:

  • possibilità di eseguire il mapping in memoria dei file;
  • operazioni orientate ai blocchi (al posto del tradizione flusso di byte) che sfruttano i servizi messi a disposizione dalle piattaforme di esecuzione;
  • operazioni di I/O asincrone e quindi non bloccanti e lock dei file o di opportune sezioni.

Sebbene il package java.nio includa molti elementi interessanti, il framework è basato su tre concetti fondamentali (cfr. [2]) che vediamo di seguito: buffer, channel e selector.

Buffer

I buffer sono contenitori di dati. java.nio.Buffer è una classe astratta che prevede una serie di specializzazioni (ByteBuffer, CharBuffer, DoubleBuffer, FloatBuffer, etc.) ognuna dotata di rispettivi decoder ed encoder che eseguono le varie trasformazioni, come per esempio la classe astratta java.nio.charset.CharsetEncoder le cui specializzazioni sono in grado di tradurre sequenze di caratteri di 16bit Unicode nella corrispondente rappresentazione a byte. Ovviamente, le specializzazione della classe astratta CharsetDecoder eseguono esattamente l'opposto.

Il vantaggio nel disporre di buffer risiede sia nella maggiore efficienza (l'API effettua il cashing dei dati), sia nella possibilità di eseguire una serie di operazioni che non sarebbero altresì possibili automaticamente, per esempio muoversi avanti e indietro nella lettura de dati. I lettori più attenti potrebbero obiettare che anche la libreria di IO standard include la possibilità di eseguire operazioni bufferizzate (per esempio BufferedReader); tuttavia in questo caso esistono ancora i vari limiti della libreria di IO spiegati di seguito (operazioni bloccanti) ed inoltre oggetti BufferedReader non possiedono la flessibilità ed il controllo degli oggetti Buffer.

Channel

I channel sono i canali di vario tipo che rappresentano le connessioni con dispositivi di vario tipo in grado di eseguire operazioni I/O. In parole semplici sono un po' l'equivalente degli stream: oggetti dai quali si può leggere e/o scrivere dati attraverso i buffer. Un'importante caratteristica da ricordare è che mentre gli stream sono mono-direzionali (le varie classi sono specializzazione o della casse astratta InputStream o della classe astratta OutputStream), i canali invece sono bi-direzionali, il che li rende più simili al funzionamento delle varie piattaforme sottostanti (Sistemi Operativi).

Selector

I selectors sono dei selettori che, insieme ai canali, permettono di definire operazioni multiplex e non-bloccanti. Multiplex significa che uno stesso Thread è in grado di gestire diversi canali. In particolare, la libreria NIO rende possibile registrare diversi canali a un unico selettore (figura 1).

 

 

Figura 1 - Schema logico degli elementi fondamentali dell'architettura dell'API NIO.

 

Ciò permette di utilizzare un singolo Thread per selezionare i canali che dispongono di input pronto ad essere processato o i canali pronti a scrivere. Pertanto, il meccanismo dei selettori semplifica la gestione di diversi canali da parte di un singolo Thead. Operazioni non-bloccanti significa che quando un Thread richiede dei dati da un canale, questa operazione ritorna immediatamente fornendo i dati correntemente disponibili (eventualmente nulla) senza rimanere bloccato fino al memento in cui ci siano dei dati disponibili per la lettura. Ciò lascia il Thread libero di eseguire altri compiti come per esempio eseguire altre operazioni di I/O su diversi canali. Chiaramente, lo stesso comportamento accade quando un Thread richiede di scrivere dei dati e non deve attendere che questi sia completamente scritti.

 

 

Tabella 1 - Fondamentali differenze tra Java IO e Java NIO.

 

java.nio.file.FileSystems e FileSystem

La nuova versione NIO2 introduce tutta una serie di funzionalità tipicamente fornite dalla piattaforma sottostante (sistema operativo e, più precisamente, file system) di cui la libreria fa largo utilizzo. Tuttavia, in alcuni casi in cui le funzionalità richieste non siano disponibili, è prevista un'implementazione nativa a basso livello (per esempio è il caso del watch service). Come di consueto, il modello implementato sfrutta la classica strategia Java: si definisce un framework che dipende da una serie di interfacce e classi astratte (figura 2) le cui implementazioni sono fornite da appositi service provider (i vecchi driver). Nel caso in questione, i vari servizi sono forniti da implementazioni della classe astratta FileSystemProvider.

 

 

Figura 2 - Diagramma delle classi di FileSystems e FileSystem.

 

La sostituzione di implementazioni concrete avviene attraverso apposite classi factory utilizzate sia per individuare le implementazioni delle specifiche interfacce e classi astratte (è il caso della classe FileSystem), sia per individuare lo specifico service provider (è il compito della classe FileSystems). Questo meccanismo è descritto dal diagramma delle classi di Figura 2. Da notare che il diagramma è stato disegnato per i fini di questo articolo. Inoltre, si ricorda che metodi il cui nome è sottolineato sono statici, mentre quando riportati in corsivo rappresentano metodi astratti.

Funzionamento

La classe (final) FileSystems contiene una serie di metodi factory sia per reperire l'oggetto java.nio.file.FileSystem di default, sia per generarne nuovi. La prima invocazione di uno dei metodi forniti da FileSystems genera il caricamento e l'inizializzazione del provider di default (lazy loading), il quale, identificato dalla URI schema "file", si occupa di creare l'oggetto FileSystem che fornisce l'accesso alla lista di file system accessibili dalla JVM. Se i processi di caricamento o di inizializzazione del provider predefinito falliscono, viene generato un errore non specificato.

Da notare che il FileSystem non può essere chiuso, nè esplicitamente mediante chiamata dell'apposito metodo close, nè implicitamente per mezzo del contrutto try-with-resource. Ogni tentativo di chiudere il FileSystem di default genera una UnsupportedOperationException.

La prima chiamata di uno dei metodi newFileSystem (sempre forniti dalla classe FileSystems) genera l'individuazione e il caricamento di tutti i provider di file system installati. Si tratta di istanze di specializzazioni della classe astratta java.nio.file.spi.FileSystemProvider (sempre introdotta con Java SE7). Questi sono molto importanti giacchè la stragrande maggioranza dei metodi forniti dalla classe Files (descritta di seguito), sono delegati a questi provider. I provider installati sono caricati per mezzo del servizio di caricamento dei service provider fornito dalla classe java.util.ServiceLoader. I file system provider sono tipicamente installati inserendoli in un JAR posizionato nel class path dell'applicazione o nella directory delle estensioni.

Una volta ottenuta una classe di tipo FileSystem (notare il singolare), più precisamente un'istanza di una sua specializzazione (FileSystem è una classe astratta), è possibile eseguire tutta una serie di metodi che restituiscono l'implementazione di una serie di oggetti molto utili.

Anche FileSystem è una sorta di factory in grado di restiuire una serie di oggetti. In particolare:

  • il metodo getPath si occupa di convertire path espressi per mezzo di stringhe e dipendenti dal sistema in oggetti di tipo Path utilizzabili per individuare e accedere a file e directory;
  • il metodo getPathMatcher crea un oggetto di tipo PathMatcher (l'interfaccia java.nio.file.PathMatcher è stata introdotta con Java SE 7) in grado di eseguire operazioni di match su determinati path;
  • il metodo getFileStores restituisce un oggetto iteratore sugli oggetti file-stores disponibili;
  • il metodo getUserPrincipalLookupService restituisce gli UserPrincipalLookupService (la classe astratta java.nio.file.attribute.UserPrincipalLookupService è anch'essa stata introdotta con Java SE 7) che permettono di individuare utenti o gruppi attraverso i rispettivi nomi: questa funzionalità è necessaria per lavorare con sistemi operativi che permettono di specificare gli owner dei file;
  • il metodo newWatchService permette di creare un oggetto WatchService (servizio di sorveglianza, java.nio.file.WatchService) che permette di osservare oggetti che possono cambiare.

Esempi

Ecco un listato con l'utilizzo di alcuni metodi base dell'interfaccia File System:

public static void main(String[] args) {
             FileSystem fileSystem = FileSystems.getDefault();
             System.out.println( "Default file System:"+fileSystem.toString()+
                                  "- Is it open? "+fileSystem.isOpen());
            
             Iterable rootDirectories = fileSystem.getRootDirectories();
            
             for(Path currPath : rootDirectories) {
                    System.out.println("Root directory: ‘"+currPath.toString()+"'");
             }
            
             UserPrincipalLookupService userPrincipal =
                                  fileSystem.getUserPrincipalLookupService();
             System.out.println("User Principal:"+userPrincipal.toString());
            
             // N.B. the Default File System cannot be closed.
       }

L'output generato sarà:

Default file System: sun.nio.fs.WindowsFileSystem@169ca65- Is it open? true
Root directory: ‘C:'
Root directory: ‘D:'
User Principal: sun.nio.fs.WindowsFileSystem$LookupService$1@1968e23

Ed ecco un semplice esempio di utilizzo di PathMatcher:

public static void main(String[] args) {
       final String THIS_SOURCE_FILE = 
            "C:/prj/java_se_7/src/main/java/com/lvt/javalessons/javase7/PathMatcherExample.java";
 
       Path path = FileSystems.getDefault().getPath(THIS_SOURCE_FILE);
       System.out.println(path.toString());
       System.out.println("root:"+path.getRoot()+",  file name:"+path.getFileName());
 
       System.out.println(
       FileSystems.getDefault().getPathMatcher("glob:*.java").matches(path.getFileName()));
}

E l'output prodotto è:

C:prjjava_se_7srcmainjavacomlvtjavalessonsjavase7PathMatcherExample.java
root:C:,  file name:PathMatcherExample.java
true
 
glob:

Da notare che il metodo getPathMatcher richiede un parametro che specifica la coppia: (sintassi, pattern) separati dal carattere due punti, ossia

:

Allo stato attuale sono priviste due sintassi: glob e regex.

glob e regex

La seconda, come è lecito attendersi, rappresenta le espressioni regolari (REGular EXpression, introdotte con J2SE 1.4) e quindi la componente pattern è un'espressione regolare come definito dalla classe java.util.regex.Pattern. La sintassi glob invece è una new entry data da una versione limitata e semplificata delle espressioni regolari. Qui di seguito sono riportati alcuni esempi:

 

*.java

indica un percorso che rappresenta un nome di file con estensione .java

 

*.*

indica nomi di file che contengono un carattere punto

 

*.{java,class}

indica nomi di file la cui estensione è .java o .class

 

log.?

indica nomi di file che cominciano con un nome "log." e che hanno un solo carattere di estensione

 

/home/*/*

indica un percorso /home// tipicamente su piattaforma UNIX

 

/home/**

indica un percorso /home/ tipicamente su piattaforma UNIX

 

C:\*

indica C: su piattafrome Windows (l'equivalente stringa Java è "C:\\*")

 

Per ulteriori dettagli circa la sintassi glob cfr. [6]

WatchService

Vediamo adesso un semplice esempio di utilizzo del servizio WatchService:

public class SimpleWatchService {
       /**
        * watch the given path
        * @param dirPath   directory to watch
        * @throws IOException a problem occurred
        * @throws InterruptedException
        */
       public static void launchWatchService(String dirPath) 
                       throws IOException, InterruptedException {
            
             FileSystem defaultFileSystem = FileSystems.getDefault();
             WatchService watchService = defaultFileSystem.newWatchService();
            
             Path path = defaultFileSystem.getPath(dirPath);
             // register the events to be notified
             path.register(watchService, ENTRY_CREATE, ENTRY_DELETE);
              
             boolean fine = false;
             int indx=0;
            
             while (!fine) {
                    System.out.println("Iteration:"+indx);
                   
                    // take the requested events. This is a blocking operation
                    WatchKey watchKey = watchService.take(); 
                    List<WatchEvent<?>> events = watchKey.pollEvents();
                   
                    System.out.println("--> Num Event:"+events.size());
                   
                    // goes through the list of events
                    for(WatchEvent<?> currentEvent : events) {
                           System.out.print(   "--> Event:"+currentEvent.kind()+
                                                      " on "+currentEvent.context());
                          
                           if (currentEvent.kind().equals(ENTRY_CREATE)) {
                                  System.out.println(": do nothing");
                           } else if (currentEvent.kind().equals(ENTRY_DELETE)) {
                                  System.out.println(": shutdown");
                                  fine = true;
                           }
                    }
                          
                    watchKey.reset();  // reset after processing 
             }
       }
      
      
       /**
        * main
        * @param args command line parameters
        */
       public static void main(String[] args) {
             System.out.println("Launching the service...");
              try {
                    launchWatchService("C:/tmp");
             } catch (IOException | InterruptedException e) {
                    System.out.println("Wooops... We have a problem "+e.getMessage());
             }
       }
}

Dopo aver lanciato il programma ed aver aperto una finestra sulla directory C:/tmp, abbiamo creato una nuova directory, attraverso l'opzione new folder presente nel menu contestuale del tasto destro e quindi l'abbiamo ribattezzata in test. Ecco l'output prodotto dal programma:

Launching the service...
Iteration:0
--> Num Event:1
--> Event:ENTRY_CREATE on "New folder" : do nothing
Iteration:0
--> Num Event:2
--> Event:ENTRY_DELETE on "New folder" : shutdown
--> Event:ENTRY_CREATE on "Test" : do nothing

I perche' della classe Files

Java dispone di una classe per lavorare con i file sin dalla prima versione: java.io.File. Java SE 7 rilascia una nuova versione completamente rivista. Questo essenzialmente per i motivi che vediamo di seguito.

Prima di tutto, diversi metodi non generavano opportune eccezioni in caso di fallimento. Ciò rendeva impossibile ottenere messaggi di errore utili. L'esempio più eclatante è la cancellazione di un file il cui fallimento risulta in un generico messaggio "delete fail", senza fornire ulteriori spiegazioni sulla natura del problema: file non esistente, insufficienti autorizzazioni, etc.

Un altro problema era dovuto al fatto che il metodo di ridenominazione non presentava un funzionamento coerente sulle varie piattaforme.

Poi, il supporto dei link simbolici era carente; il supporto per i metadati era limitato, mancavano all'appello i permessi dei file, del proprietario del file, degli attributi di sicurezza; l'accesso ai metadati del file era inefficiente.

Molti metodi forniti dalla classe File presentavano problemi di scalabilità. Richieste di elenchi di directory di grandi dimensioni ad un sever poteva causarne un blocco. La richiesta della lista dei file di directory di grandi dimensioni spesso causava problemi di risorse di memoria, con conseguente negazione del servizio.

Non era possibile scrivere codice affidabile in grado di traversare ricorsivamente alberi di file e comportarsi adeguatamente in caso di presenza di link simbolici circolari (p.e. il link A si riferisce al link B che si riferisce al link C che a sua volta si riferisce al link A).

Concetti nuovi nella java.nio.file.Files

La classe java.nio.file.Files, che eredita direttamente da Object, presenta esclusivamente metodi statici: è a tutti gli effetti una utility class. Prima di proseguire nella descrizione, è importante illustrare due concetti abbastanza nuovi nelle libreria Java: link simbolici e interfaccia Path.

Link simbolici

Tradizionalmente, tutti gli oggetti presenti nel file system erano o directory of file. Tuttavia, molti sistemi operativi supportano la nozione di link simbolici (symbolic links, detti anche symlink o soft link). Si tratta di file speciali che in realtà sono dei riferimenti ad altri file, un po' come alcuni collegamenti che si creano sui desktop windows. La nuova libreria NIO è in grado di gestire i link simbolici o automaticamente (risolvendo i vari link a raggiungere il file/directory), o richiedendo al client della libreria di specificare il comportamento da seguire (p.e. NOFOLLOW). Da notare che molti sistemi operativi supportano anche il concetto di hard link, ossia di soft link con ulteriori restrizioni, come per esempio: la destinazione del link deve esistere, non è possibile creare hard link relativi a directory, "volumi", partizioni e file system condivisi, e così via.

Interfaccia Path

Si tratta di una dei principali entry point del package java.nio.file ed è una rappresentazione programmatica di un percorso nel file system. Un oggetto Path contiene il nome del file/directory e della lista delle directory utilizzate per costruire il percorso, ed è usato per esaminare, individuare e manipolare file. Gli oggetti Path riflettono le piattaforme sottostanti. Nel sistema operativo Solaris, un percorso utilizza la sintassi Solaris (/home/joe/foo) e in Microsoft Windows, un percorso utilizza la sintassi di Windows (C:homejoefoo). Un percorso non è indipendente dal sistema. Il file o la directory corrispondente al percorso potrebbe non esistere. È possibile creare un'istanza di Path e manipolarla in vari modi: è possibile aggiungere o estrarne parti, confrontarla con un altro percorso, etc. Quando necessario, è possibile utilizzare i metodi della classe File per verificare l'esistenza del file corrispondente al percorso, creare il file, aprirlo, cancellarlo, modificarne i permessi, e così via. Esiste anche la classe java.nio.file.Paths che include solo due metodi statici che si occupano di convertire URI e sequenze di stringhe in corrispondenti oggetti di Path.

La classe Files

Come scritto in precedenza, la classe Files contiene esclusivamente metodi statici la cui implementazione, nella quasi totalità dei casi è delegata al provider. Di seguito sono riportate delle brevi descrizioni dei metodi più interessanti forniti dalla classe Files. Da notare che il termine file, secondo buone tradizione della teoria del file system, è spesso utilizzato per indicare sia files, sia directory indistintamente, giacche' al livello di file system sono tutti comunque dei file.

copy

Questo metodo permette di eseguire la copia dei file da un path iniziale a uno finale. Oltre alle versioni attese, come per esempio l'overloading con InputStream, ne è presente una molto interessante

copy(Path source, Path target, CopyOption... options)

Questa prevede come parametri formali il path di partenza, quello di destinazione e una serie di opzioni, quali: REPLACE_EXISTING, COPY_ATTRIBUTES e NOFOLLOW_LINK (se il file è link simbolico non viene seguito e quindi ciò che viene copiato è il link al file e non il file stesso) che permettono di specificare il comportamento da seguire nei vari casi.

Vediamo subito un esempio con un semplice programma che mostra la copia di file da un URI a una directory locale:

public static void main(String[] args) {
       Path path = null;
      
       try {
             path = Files.createTempDirectory("mypdf");
       } catch (IOException e1) {
             System.out.print("Woops. We have a problem "+e1);
       }
 
       if (path != null) {
      
             System.out.println("Create new temp directory:""+path+""");
             URI uri = URI.create( "http://www2.mokabyte.it/cms/attachmentproviderservlet?attachId
             =16S-UNO-VKU-51T_7f000001_29536129_9cd098d4_JavaVersionsHistory.pdf");
             try (InputStream in = uri.toURL().openStream()) {
                    Files.copy(in, path, StandardCopyOption.REPLACE_EXISTING);
             } catch (IOException e) {
                    System.out.print("Woops. We have a problem "+e);
             }
       }
}

createDirectory/createDirectories

Permette di creare, rispettivamente, una directory e una directory in modo ricorsivo, ossia creando anche eventuali directory genitore non ancora presenti. Questi metodi richiedono di specificare gli attributi del "file" (inteso come oggetto directory). FileAttribute è una nuova interfaccia che permette di specificare questi attributi come coppie (nome, valore). Da notare che, in caso si vogliano specificare attributi che però non è possibile specificare automaticamente durante la creazione della directory, il metodo genera una UnsupportedOperationException.

createFile

Tenta di generare un nuovo file vuoto, e termina con insuccesso nel caso in cui il file già esista. Come nel caso di createDirectory, anche in questo caso è possibile specificare i gli attributi file, con le medesime modalità e avvertenze.

createLink

Tenta di creare un link a un file esistente.

createTempDirectory/createTempFile

Crea una directory temporanea o un file temporaneo combinando il path della directory e una stringa da utilizzare come prefisso.

delete

Cerca di eliminare il file o la directory specificata. Se il path specificato è un link simbolico, allora viene eliminato il link e non il file stesso. Se invece si tratta di una directory, allora, come di consueto, l'implementazione si assicura che questa sia vuota per poter procedere all'eliminazione; in caso contrario genera un'apposita eccezione (DirectoryNotEmptyException).

deleteIfExists

Elimina un file o directory se esiste. La differenza con il metodo precedente è che questo ritorna un valore booleano per specificare se l'operazione ha avuto successo o meno, mentre il metodo precedente restituisce un'apposita eccezione (NoSuchFileException) nel caso in cui il metodo specificato non esista. Da notare che questo metodo, al contrario del precedente, restituisce un valore booleano che indica l'esito dell'operazione.

exists

Verifica se il file (o directory) specificato esiste o meno. In questo caso è possibile specificare delle opzioni di link per istruire il metodo su come comportarsi in caso di link. Per default, il metodo segue i vari link fino a raggiungere l'elemento puntato. Tuttavia, è possibile specificare opzioni quali NOFOLLOW_LINKS con ovvio significato.

getAttribute/getAttributes

Restituisce il valore dell'attributo specificato. Il comportamento in caso di link simbolici è esattamente lo stesso specificato per il metodo exists. Nel caso di getAttributes, questi (in maniera abbastanza inattesa) vanno specificati per mezzo di una stringa suddivisi per mezzo di una virgola.

getFileStore

Restituisce un oggetto di tipo FileStore che rappresenta il luogo (store, "magazzino", "luogo di conservazione") dove il file è ubicato. Da notare che la classe astratta java.nio.file.FileStore è anch'essa una nuova feature introdotta con Java SE 7. Istanze delle specializzazioni di questa classe rappresentano uno storage pool, un dispositivo, una partizione, un volume, un file system o altre implementazioni che costituiscano un file store.

getLastModifiedTime, getOwner, getPosixFilePermissions

Questi metodi restituiscono rispettivamente:

  • il timestamp dell'ultima modifica avvenuta sul file/directory specificati attraverso un'istanza della una nuova classe final: java.nio.file.attribute.FileTime;
  • il possessore del file (specificato per mezzo di implementazioni della nuova interfaccia java.nio.file.attribute.UserPrincipal, che specializza l'interfaccia java.nio.file.attribute;
  • l'insieme dei permessi POSIX (Portable Operating System Interface, interfaccia portatile del sistema operativo) del file. Si tratta di una famiglia di standard specificati e gestiti dalla IEEE (IEEE Std 1003.1-1988, rilasciata nel 1988 cfr.[4]) per mantenere la compatibilità tra i sistemi operativi. Ogni elemento di questo insieme è specificato per mezzo del nuovo tipo enumerato: java.nio.file.attribute.PosixFilePermission i cui elementi sono: GROUP_EXECUTE, GROUP_READ, GROUP_WRITE, OTHERS_EXECUTE, OTHERS_READ, OTHERS_WRITE, OWNER_EXECUTE, OWNER_READ, OWNER_WRITE.

setLastModifiedTime, setOwner, setPosixFilePermissions

Si tratta dei metodi di impostazione dei valori corrispondenti ai metodi get succitati.

isDirectory, isExecutable, isHidden, isReadable, isRegularFile, isSymbolicLink, isWritable

Questi metodi permettono di capire se un determinato file (o directory) possiede o meno la proprietà specificata dal metodo.

isSameFile

Permette di comparare i file/directory specificati dai rispettivi path. Restituisce un valore true se si tratta degli stessi oggetti.

move

Permette di spostare o rinominare il file (o directory) specificato nel path di destinazione. Questo metodo fallisce nel caso in cui esista già un file target differente da quello che si intendeva "muovere". Se invece il file è un link simbolico, ne' il link e ne' il file puntato vengono mossi. In questo caso è possibile specificare le seguenti opzioni REPLACE_EXISTING e ATOMIC_MOVE con la semantica attesa.

probeContentType

Sonda il contenuto del file per identificarne il tipo, che viene restituito secondo lo standard MIME (Multipurpose Internet Mail Extension). Questo metodo utilizza le implementazioni della classe astratta java.nio.file.spi.FileTypeDetector (sempre introdotta con Java SE7) fornita dal service provider per sondare il file dato per determinare il tipo. Chiaramente, i vari provider possono utilizzare diverse strategie, come leggere le estensioni dei file, individuare determinati pattern nel file, etc. Il funzione prevede l'invocazione di ciascun rivelatore di tipo di file che si occupa di sondare se il file è del "proprio" tipo o meno (una implementazione del pattern Chain of Responsibility). Nel caso in cui il test abbia esito affermativo, allora restituisce il tipo (stringa MIME), altrimenti si prosegue. Se alla fine di questo ciclo, il file non è stato riconosciuto da nessuno dei rivelatori installati, allora viene invocato il detector di default per cercare di indovinare il tipo di contenuto.

readAllBytes, readAllLines

Questi metodi di occupano di caricare in memoria l'intero file memorizzandolo, rispettivamente, in un array di byte e in una lista di stringhe. Da notare che questi metodi possono lanciare un OutOfMemoryError.

size

Restituisce le dimensioni del file specificato in termini di byte.

walkFileTree

Si tratta di un metodo molto potente che permette di effettuare la navigazione di una parte (albero) del file system a partire dal nodo (file) dato. L'albero è attraversato secondo l'algoritmo depth-first (profondità per prima, continua a visitare i nodi prima di tornare sui propri passi e tentare le altre direzioni). Importante parametro di questo metodo è un'implementazione dell'interfaccia java.nio.file.FileVisitor che permette di ricevere notifiche ogni volta che viene individuato un nuovo elemento dell'albero.

Le notifiche sono relative ai seguenti eventi:

  • in procinto di visitare una directory (preVisitDirectory);
  • appena terminata la visita di una directory (postVisitDirectory);
  • individuazione di un nuovo file (visitFile);
  • fallimento di visita di un file (visitFileFailed).

I metodi invocati della classe visitor sono tipicamente dotati di una serie di attributi base (BasicFileAttribute) e permettono di stabilire se proseguire o meno la visita dell'albero. In particolare, possono restituire uno dei seguenti elementi dell'enumeration java.nio.file.FileVisitResultCONTINUE, SKIP_SIBLINGS, SKIP_SUBTREE, TERMINATE i cui significati sono ovvi.

Da notare che, se durante la traversata della struttura qualcosa va storto e quindi vi è una IOException, viene invocato il metodo visitFileFailed del visitor, il quale deve restituire un elemento del FileVisitResult e quindi stabilire se proseguire o terminare.

write

Permette di scrivere dei byte o delle linee di testo (a seconda dello overloading del metodo utilizzato) nel file specificato. In questo caso è possibile specificare dei parametri di scrittura del file, come per esempio APPEND, CREATE_NEW, e così via.

 

Oltre a questi metodi, ve ne è un'intera una serie per ottenere oggetti BufferedReader/Writer, Channel, Stream per poter manipolare i file.

E anche qui, la cosa migliore è vedere un esempio di utilizzo di alcuni metodi dell'interfaccia Files:

public static void main(String[] args) {   
             final String THIS_SOURCE_FILE =
                       "C:/prj/java_se_7/src/resource/excel_worksheet.xlsx";
            
             Path path = FileSystems.getDefault().getPath(THIS_SOURCE_FILE);
             try {
                    System.out.println("Last modified time:"
                                  +Files.getLastModifiedTime(path));
                    System.out.println("Owner:"+Files.getOwner(path));
                    System.out.println("Size:"+Files.size(path));
                    System.out.println("Type:"+Files.probeContentType(path));
             } catch (IOException e) {
                    System.out.println("Wooops we have a problem:"
                                        +e.getMessage());
             }
}

DirectoryStream e SecureDirectoryStream

Java SE 7 fornisce le due interfacce java.nio.file.DirectoryStream e la sua specializzazione java.nio.file.SecureDirectoryStream.

DirectoryStream

DirectoryStream definisce il comportamento di classi che si occupano di scorrere le voci in una directory utilizzabile in un costrutto for-each. Un oggetto di tipo DirectoryStream viene aperto al momento della creazione ed è chiuso richiamando il metodo close. Ciò si può fare anche automaticamente per mezzo del nuovo costrutto try-with-resource. L'interfaccia DirectoryStream, estende le interfacce AutoCloseable e Closeable (oltre ovviamente Iterable). La chiusura di un oggetto DirectoryStream genera il rilascio di tutte le risorse associate allo stream. La mancata chiusura può causare un leak.

Le motivazioni alla base dell'implementazione di questa interfaccia e delle corrispondenti implementazioni è di consentire di implementare oggetti in grado di presentare un buon livello di scalabilità lavorando su directory di grandi dimensioni, dando luogo ad un utilizzo parsimonioso delle risorse, con tempi di risposta accettabili. Inoltre, questa interfaccia include la possibilità di eseguire il filtro delle voci della directory. Istanze di questa interfaccia si ottengono invocando il metodo Files.newDirectoryStream.

Vediamo di seguito un semplice esempio di utilizzo di DirectoryStream:

public static List listSourceFiles(String dir) throws IOException {
   Path dirPath = FileSystems.getDefault().getPath(dir);
   List result = new ArrayList<>();
   try (DirectoryStream stream = Files.newDirectoryStream(dirPath,"*.{c,java}")) {
       for (Path entry : stream) {
          result.add(entry);
       }
   }
   return result;
}
 
public static void main(String[] args) {
   List paths;
   try {
       paths =
          listSourceFiles("C:/prj/java_se_7/src/main/java/com/lvt/javalessons/javase7");
       for (Path currPath : paths) {
          System.out.println(currPath);
       }
   } catch (IOException e) {
       System.out.println("Wooops... We have a problem " + e.getMessage());
   }
}

SecureDirectoryStream

Oggetti di tipo SecureDirectoryStream sono pensati per un utilizzo da applicazioni sofisticate o sensibili dal punto di vista della sicurezza che necessitano di operare su alberi di file o operare su directory in assenza di race-condition (condizioni di competizione). In questo contesto, race condition insorgono per via del fatto che, tipicamente, sequenze di operazioni su file non avvengo atomicamente. Tutte le operazioni sui file definite da questa interfaccia richiedono di specificare un percorso relativo alla directory aperta.

L'implementazione di SecureDirectoryStream richiede il supporto del sistema operativo sottostante. Qualora un implementazione è in grado di supportare questa funzionalità il DirectoryStream restituito dal metodo newDirectoryStream è un SecureDirectoryStream che quindi richiede un cast esplicito per poter richiamare i metodi definiti da questa interfaccia.

SCTP

Un altro aggiornamento della libreria NIO è dato dal supporto dello Stream Control Transmission Procotol (SCTP, Protocollo di trasmissione su stream controllato, cfr. [3]). Si tratta di un protocollo di trasporto che rappresenta un ibrido tra i popolari protocolli TCP (Transmission Control Protocol, protocollo a controllo di trasmissione) e UDP (User Datagram Protocol, protocollo di datagrammi utente). In particolare è orientato al messaggio come UDP e, allo stesso tempo, assicura un trasporto affidabile e nella giusta sequenza come con controllo di congestione come il TCP e Sockets Direct Protocol. Le caratteristiche fondamentali di questo protocollo sono quelle descritte di seguito.

  • Message Framing (definizione/circoscrizione di messaggio) durante la trasmissione. Ciò significa che la dimensione dei messaggi è preservata nell'invio nel canale. Pertanto se un client invia due messaggi a un sever, uno di dimensione X ed uno di dimensione Y (entro certi limiti), questi messaggi resteranno identici nel canale, e quindi il server eseguirà due letture di dimensioni X e Y rispettivamente. Ciò in effetti è quello che avviene con il protocollo UDP. Mentre in TCP, orientato alla sessione, funziona su uno streaming di byte.
  • Servizio di trasporto affidabile. Questa è una delle caratteristiche molto interessanti del protocollo, in quanto, sebbene la strategia di funzionamento sia più simile a UDP, STPC esegue una trasmissione affidabile proprio come TCP. Questo è ottenuto tramite la definizione di opportuni servizi che si occupano di garantire l'affidabilità alla comunicazione orientata al messaggio.
  • Mantenimento dell'ordine. Possibilità di richiedere la consegna dei messaggi nella stessa sequenza di emissione.
  • Multi-homing. Questa caratteristica fornisce alle applicazioni una maggiore disponibilità di quelle che utilizzano il protocollo TCP. Un server funziona multi-homing quando è dotato di più interfacce di rete e quindi più indirizzi IP che possono essere utilizzati per indirizzarlo. In TCP, una connessione si riferisce a un canale tra due punti (socket tra le interfacce dei due host). SCTP introduce il concetto di associazione che esiste tra due host (associazione tra due end-point), ma che è in grado di collaborare con interfacce multiple ad ogni host (ogni endpoint può essere rappresentato da più indirizzi). Ciò fa sì che SCTP possa lavorare in configurazioni ridondanti e quindi sia in grado di assicurare un failover trasparente. In particolare, SCTP controlla i percorsi delle associazioni con un predefinito servizio di heartbeat (controllo del battito cardiaco), ed è quindi in grado di  dirottare il traffico su un percorso alternativo qualora rilevi un errore di percorso. Tutto ciò è ovviamente trasparente per le applicazioni.
  • Multi-Streaming. Le associazioni SCTP sono simili alle connessioni TCP con la differenza che SCTP è in grado di supportare diversi stream (flussi) all'interno di una medesima associazione. Quindi è possibile partizionare i dati in diversi stream indipendenti. Questo è ottenuto facendo sì che a ciascun stream sia assegnato un numero di identificazione univoco riportato all'interno dei corrispondenti pacchetti trasmessi. Multi-streaming è importante perche' fa sì che un flusso bloccato (per esempio, uno stream in attesa di ritrasmissione per via della perdita di un pacchetto) non influisca su gli altri flussi presenti sulla medesima associazione. Questo problema, presente in TCP, è tipicamente indicato con il nome di head-of-line blocking (blocco di inizio linea) che quindi non si manifesta in SCTP. Si consideri un multi-streaming server HTTP. Questo potrebbe portare a una migliore interattività giacche' richieste multiple potrebbero venir risolte attraverso diversi stream indipendenti di una medesima associazione. Questa funzionalità permetterebbe di parallelizzare le risposte, e anche qualora non dovesse portare a migliori performance, potrebbe sicuramente migliorare l'eseprienza utente: il browser potrebbe contemporaneamente caricare il codice HTML e le immagini grafiche.

Come è lecito attendersi, la API Java si basa sull'architettura NIO: in questo modo le applicazioni che necessitano di utilizzare il protocollo possono sfruttare servizio di I/O non bloccanti in multi-plexing. Le nuove classi ed interfacce atte a implementare il supporto del protocollo SCTP secondo l'architettura NIO sono state incluse nel package com.sun.nio.sctp invece che in un package del tipo java.nio.channels.sctp. Ciò perche', sebbene l'API e l'implementazione siano pienamente supportate e pubblicamente accessibili, ancora non fanno ancora parte della piattaforma Java SE. In altre parole, sono in attesa di raggiungere un grado di maturità superiore.

La parte fondamentale del nuovo package consiste nei tre nuovi tipi di canale (SctpChannel, SctpServerChannel, SctpMultiChannel) che possono essere suddivisi nei seguenti due gruppi logici.

Il primo presenta una semantica simile a TCP, SctpChannel e SctpServerChannel. Un SctpChannel può controllare solo una singola associazione: invio e ricezione di dati da e verso un singolo endpoint. SctpServerChannel ascolta e accetta nuove associazioni richieste al proprio socket.

Il secondo è composto di soli SctpMultiChannel. Le istanze di questo tipo di canale possono controllare più associazioni, pertanto, rendono possibile inviare e ricevere dati da e verso svariati endpoint diversi.

Unicode 6.0.0

Java SE 7 include il supporto per Unicode 6.0 (cfr. [4]). La storia tra Java e Unicode è basta sui seguenti rilasci:

  • le versioni precedenti alla 1.1 supportavano Unicode 1.1.5
  • con JDK 1.1 si è passati a Unicode 2.0
  • JDK 1.1.7 ha aggiunto il supporto a Unicode 2.1
  • con Java SE 1.4 si è passati a Unicode 3.0
  • Java SE 5.0 supporta Unicode 4.0
  • Java SE 7 supporta Unicode 6.0 (release iniziali di Java SE 7 supportavano la versione 5.1.0)

La versione Unicode 6.0 è una vera e propria major release che include numerosi cambiamenti che vediamo di seguito.

Aggiunta caratteri

Unicode 6.0.0 aggiunge 2.088 caratteri. Tra questi ricordiamo oltre 1.000 simboli aggiuntivi tra i quali i simboli emoji (emoji è il termine Giapponese per indicare caratteri "immagine", tipicamente 12x12pixel, ossia emoticons largamente utilizzati nei messaggi elettronici giapponesi e nelle pagine web), che sono particolarmente importanti per i telefoni cellulari; il nuovo simbolo ufficiale indiano per il indicare la valuta Rupia indiana; 222 ulteriori ideogrammi unificati CJK di uso comune in Cina, Taiwan e Giappone; 603 caratteri supplementari per il il sostegno ad alcune lingue africane, comprese le estensioni al Tifinagh, alla scrittura etiopica, e al Bamum; tre script aggiuntivi (Mandaic, Batak, e Brahmi. Poi c'è l'aggiunta di nuove proprietà e file di dati: EmojiSources.txt associa i simboli emoji alla loro sorgente telco originale giapponese; ci sono due proprietà provvisorie per il supporto di script indiani (IndicMatraCategory e IndicSyllabicCategory); abbiamo inoltre l'estensione per i dati provvisori di script per l'uso nella segmentazione, espressioni regolari, e rilevamento spoof; non manca la correzione delle proprietà dei caratteri per i caratteri esistenti; i valori delle proprietà sono stati aggiornati a 36 caratteri non-CJK; ci sono numerosi miglioramenti alle proprietà provvisorie per ideogrammi CJK Unified; vengono apportati aggiornamenti di formato per molti tag sorgenti della normativa IRG, per sincronizzare meglio con ISO / IEC 10646 (vedi UAX # 38, Unicode Database Han, per i dettagli); e in generale si fornisce miglioramenti al formato, inclusi grafici per ideogrammi di compatibilità CJK.

Supporto estensibile per i codice standard delle valute

L'ISO 4217 (cfr. [5]) è uno standard della International Standards Organization (organizzazione internazionale degli standard), che delinea i designatori di valuta, i codici dei Paesi (alfa e numerico), e riferimenti alle unità minori nelle seguenti tre tabelle:

  • Tabella A.1: codici delle valute correnti e della lista dei fondi (in questa tabella si possono trovare elementi come ITALY               Euro      EUR        978         2 e ZZ08_Gold    Gold      XAU       959         N.A.);
  • Tabella A.2: codici dei fondi correnti (valori che si possono trovare qui sono UNITED STATES, US Dollar, Same Day, USS, 998, 2);
  • Tabella A.3: elenco dei codici relativi alle denominazioni storiche delle valute e dei fondi.

Il supporto è estensibile in quanto i codici standard possono essere sovrascritti. A tal fine è necessario creare id file di proprietà currency.properties e memorizzarlo nella directory: /lib/currency.properties. Il fine deve organizzato in forma di coppie key/value relativi allo standard ISO 3166 dei codici nazioni, e allo standard ISO 4217 delle valute. L'elemento valore consiste in tre elementi separate per mezzo di una virgola dei valori ISO 4217 delle valute: codice alfabetico, a codice numerico, e numero di unità minori. Per esempio per il Giappone si potrebbe avere JP=JPZ,999,0

Anche la classe java.util.Currency è stata aggiornata includendo i seguenti metodi:

 

getAvailableCurrencies

restituisce l'insieme delle valute disponibili.

 

getNumericCode

restituisce il codice numerico ISO 4217 della valuta

 

getDisplayName

restituisce il nome idoneo per visualizzare la valuta in base al "locale" impostato

 

getDisplayName(Locale)

restituisce il nome idoneo per visualizzare la valuta in base al "locale" specificato

 

E vediamo di seguito l'utilizzo del metodo getAvailableCurrencies, quantomai atteso da chi lavora in finanza:

public class CurrencyExample {
 
   public static void main(String[] args) {
       Set currencies = Currency.getAvailableCurrencies();
      
       for (Currency ccy : currencies) {
          System.out.println(  ccy.getCurrencyCode()+" "+
                               ccy.getNumericCode()+" "+ccy.getDisplayName());
       }
   }
}

Cambiamenti al "locale"

Con Java SE7 è possibile impostare il locale di default in modo indipendente per i seguenti diversi tipi di usi:

  • format setting (impostazione di formato) utilizzato per formattare le risorse;
  • display setting (impostazione di visualizzazione) utilizzato dai menu e dalle finestre di dialogo.

Il nuovo metodo public static Locale getDefault(Locale.Category category) della classe java.util.Locale prende come unico parametro Locale.Category: si tratta di un nuovo enumeratore che prevede i due literal FORMAT e DISPLAY. Passando il FORMAT, il metodo restituisce le impostazioni internazionali predefinite per le risorse di formattazione. Analogamente, fornendo il valore DISPLAY restituisce il locale predefinito utilizzato dall'interfaccia utente. Il metodo setDefault(Locale.Category category, Locale newLocale) è utilizzato per impostare la localizzazione per la categoria specificata.-

Aggiornamenti API UI

Ci sono anche dei miglioramenti alle API che si occupano di gestire le interfacce frafiche utenti. Vediamole nei prossimi paragrafi.

Nimbus

Con il JDK 7, il look-and-feel di Swing Nimbus (cfr. [7]), introdotto nel JDK 6u10 come sostituzione per il vecchio Metal LoF, è stato spostato dalle estensioni proprietarie di Oracle (com.sun.java.swing) alla libreria standard Swing (javax.swing) facendolo diventare un vero e proprio cittadino di prima classe della libreria Swing. Sebbene presenti tutta una serie di vantaggi, inclusi migliore look&feel cross-platform e implementazione più scalabile della parte Java 2D, non è stato impostato come default proprio per non creare problemi alle applicazioni esistenti.

JLayer

Con Java SE 7 è stata introdotta una nuova classe Swing: JLayer. Si tratta di un decoratore universale per componenti Swing che permette sia di realizzare vari sofisticati effetti di paint, sia di ricevere le notifiche di tutti gli AWTEvents generati al suo interno. JLayer delega la gestione del disegno e degli eventi input a un oggetto LayerUI (javax.swing.plaf.LayerUI), il quale esegue la decorazione vera e propria. Il paint personalizzato implementato nel LayerUI e la notifica eventi esegue il lavoro per il JLayer stesso e tutte le sue sotto-componenti. Questa combinazione consente di arricchire i componenti esistenti con l'aggiunta di nuove funzionalità avanzate come la chiusura temporanea di una gerarchia, consigli dati per i componenti composti, un maggiore scorrimento del mouse e così via.

JLayer è una soluzione valida quando si ha bisogno di eseguire il painting customizzato per componente complesso formato da sotto-componenti e per intercettare eventi per i vari sotto-componenti.

Standardizzazione delle finestre traslucenti.

Java 7 consente di definire il grado di trasparenza di una finestra, ad esempio un JFrame. Una finestra 90% traslucida è 10% opaco (quindi non si vede per il 10%). Il grado di trasparenza per una finestra può essere definita utilizzando il valore alfa della componente di colore di un pixel. È possibile definire il valore alfa di un colore utilizzando i costruttori della classe colore come illustrato di seguito.

Color (int red, int green, int blue, int alfa)
Color (float red, float verde, blu float, float alpha)

Il valore per l'argomento alpha viene specificato tra 0 e 255, quando i componenti del colore sono specificati come valori interi. Quando invece gli argomenti sono di tipo float, il valore è compreso tra 0,0 e 1,0. Il valore alpha di 0 o 0,0 significa "modo trasparente" (si vede attraverso). Il valore di alfa 255 o 1,0 è "opaco" (quindi non si vede attraverso).

Java 7 supporta le seguenti tre forme di trasparenza di una finestra, rappresentate dalle seguenti tre costanti incluse nella enumeration java.awt.GraphicsDevice.WindowTranslucency.

  • PERPIXEL_TRANSPARENT: in questa forma di traslucidità, un pixel in una finestra è o opaco o trasparente: il valore alfa per un pixel è 0,0 o 1,0.
  • TRANSLUCENT: in questa forma di traslucidità, tutti i pixel in una finestra hanno la stessa traslucenza, che può essere definita da un valore di alfa tra 0,0 e 1,0.
  • PERPIXEL_TRANSLUCENT: in questa forma di trasparenza, ogni pixel in una finestra può avere un proprio valore alpha compreso tra 0,0 e 1,0. Questo modulo permette di definire la traslucenza in una finestra su una base per pixel.

OpenType/CFF font

Java SE 7 introduce il supporto per il Compact Font Format OpenType/CFF (font dall'estensione .otf) e TrueType (font dall'estensione .ttf): si tratta di due formati di font moderni disponibili per l'uso su desktop. Pur essendo formati distinti, OT/ CFF e TTF in realtà hanno molte similitudini e le differenze risiedono principalmente nei differenti formati outline (di struttura) e nei diversi approcci impiegati per eseguire il rastering dei contorni. In particolare, il font OT/CFF utilizza una strategia basata sulla curve cubiche di Bezier mentre in caratteri TTF utilizzano i percorsi basati sulle curve quadratiche di Beziers.

NumericShaper

La classe java.awt.font.NumericShaper è utilizzata per convertire cifre in formato europeo (Latin-1) in altre rappresentazioni Unicode. La strategia consiste nel presentare numeri secondo i formati locali gestendoli internamente in formato europeo (Latin-1). Questa classe non è in grado di interpretare il carattere numerico obsoleto (U+206 E). Le istanze di tipo NumericShaper sono generalmente applicate come attributi al testo con l'attributo NUMERIC_SHAPING della classe TextAttribute.

XRender per Java 2D

Altro elemento da menzionare è l'introduzione di una pipeline grafica XRender per Java 2D, atta a migliorare la gestione delle funzionalità specifiche delle GPU moderne (X11-based desktop)

JDBC 4.1

Java SE 7 include la nuova versione JDBC (4.1, cfr. [8]) con le seguenti caratteristiche:

  • la possibilità di utilizzare le risorse Connection, ResultSet, e Statement in costrutti try-with-resource introdotti con il progetto Coin;
  • l'introduzione dell'interfaccia RowSetFactory e della classe RowSetProvider, che consentono di creare tutti i tipi di RowSet supportati dal driver JDBC; in particolare, l'interfaccia RowSetFactory contiene i seguenti metodi che permettono di creare i corrispondenti tipi di RowSet: createCachedRowSet, createFilteredRowSet, createJdbcRowSet, createJoinRowSet e createWebRowSet.

Elliptic Curve Cryptography (ECC)

Con Java SE 7 è stata introdotta l'implementazione dell'algoritmo Elliptic Curve Cryptography (ECC, crittografia basata sulle curve ellittiche), In particolare, Sun fornisce SunEC come provider dei servizi ECC. Si tratta di un approccio di crittografia per chiavi pubbliche basato sulla struttura algebrica delle curve ellittiche su campo finito. ECC è ideale per l'utilizzo in ambienti con scarse risorse come pager (cercapersone), PDA, telefoni cellulari e smart card. Rispetto ai tradizionali sistemi di crittografia come RSA, ECC offre un equivalente livello di sicurezza con ridotte dimensioni delle chiavi, in che si traduce in calcoli più efficienti e minore consumo energetico, di risorse e di risparmio di larghezza di banda. L'implementazione Java è stata denominata JECC. L'utilizzo di questo algoritmo per la crittografia fu suggerito in maniera indipendente sia da Neal Koblitz sia da Victor S. Miller nel 1985.

Conclusione

Con questo articolo abbiamo terminato l'analisi dedicata a Java7. In particolare, abbiamo presentato una serie di cambiamenti abbastanza importanti apportati alla API NIO tanto che la nuova versione è stata ribattezzata NIO2. Questi sono condensati nei nuovi package: java.nio.file, java.nio.file.attribute e java.nio.file.spi. Gli obiettivi primari di questa release consistevano nell'aumentare l'indipendenza dalla piattaforma di esecuzione, fornire ulteriori servizi di alto livello e aggiungere il supporto per metadati e collegamenti simbolici. Le nuove classi (soprattutto Files) forniscono una serie di servizi molto utili per la gestione dei file, inclusi "macro" servizi come lo spostamento dei file, la copia, etc. Ora molte applicazioni esistenti prevedono codice "legacy" per l'ottenimento di questi servizi con livelli di scalabilità e performance sicuramente inferiori a quelli ottenibili dalle nuove classi che sfruttano pesantemente i servizi offerti dai sistemi operativi di produzione ed implementazioni macchina.

Una domanda interessante da porsi è se valga la pena migrare un codice legacy, magari non ottimale ma ben testato, sulla nuova libreria IO o restare ancorati alla classe java.io.File. Chiaramente non esiste una risposta univoca e la valutazione va eseguita caso per caso. Tuttavia può essere molto sensato mettere in programma questa migrazione qualora l'utilizzo dei file sia una componente importante dell'applicazione e vi sia la necessità di migliorare uno o più dei seguenti fattori: robustezza, performance e scalabilità. La migrazione verso NIO2 dovrebbe, tra l'altro, eliminare importanti porzioni di codice.

Oltre alla nuova API NIO2, in questo articolo abbiamo presentato tutta una serie di migliorie relative al passaggio alla versione 6 di Unicode, al supporto estensibile per il codice standard delle valute, le variazioni alle impostazioni della localizzazione (Local), le nuove feature della GUI (come Nimbus, JLayer, Traslucent Window), e così via.

L'appuntamento è al prossimo numero con un articolo dedicato a quanto si sa di un cambiamento rivoluzionario del linguaggio Java: le Lamdba Expression che dovrebbero essere rese (finalmente) disponibili con Java SE 8, release programmata comunque fra più di un anno, per il quarto trimestre del 2013.

Riferimenti

[1] JSR 51: New I/O APIs for the Java™ Platform

http://www.jcp.org/en/jsr/detail?id=51

 

[2] Doug Lea, "Scalable IO in Java"

http://gee.cs.oswego.edu/dl/cpjslides/nio.pdf

 

[3] Stream Control Transmission Protocol (SCTP)

http://www.sctp.org/

 

[4] Unicode 6.0.0

http://unicode.org/versions/Unicode6.0.0/

 

[5] ISO 4217

http://www.iso.org/iso/iso_catalogue/catalogue_tc/catalogue_detail.htm?csnumber=46121

 

[6] The Java Tutorials, File Operations

http://docs.oracle.com/javase/tutorial/essential/io/fileOps.html#glob

 

[7] Nimbus look and feel

http://docs.oracle.com/javase/tutorial/uiswing/lookandfeel/nimbus.html

 

[8] JDBC 4.1

http://docs.oracle.com/javase/7/docs/technotes/guides/jdbc/jdbc_41.html

 

 

 

 

Condividi

Pubblicato nel numero
175 luglio 2012
Luca Vetti Tagliati ha conseguito il PhD presso il Birkbeck College (University of London) e, negli anni, ha applicato la progettazione/programmazione OO, Component Based e SOA in diversi settori che variano dal mondo delle smart card a quello del trading bancario, prevalentemente in ambienti Java EE. Dopo aver lavorato a…
Articoli nella stessa serie
Ti potrebbe interessare anche