MokaByte Numero 14 - Dicembre 1997
Foto
Le Specifice Glasgow
II parte

 


 

di 
Daniela
Ruggeri
JavaBeans Activation Framework

Il 5 Novembre 1997 JavaSoft ha rilasciato la prima versione delle API relative alle JavaBeans Activation Framework che permettono di manipolare i dati da/a oggetti Java mediante l'associazione ai tipi MIME, indipendentemente dal fatto che i dati provengano da un File System, Web, Ftp, ecc. Una volta individuato il tipo MIME, è possibile individuare le operazioni possibili su questo MIME e per ogni operazione, il Bean gestore di questa operazione. Per esempio è possibile associare dati contenente un formato txt al tipo MIME text/plain, e quindi per esempio alle operazioni di View e Edit che saranno associate ai beans uno visualizzatore dei dati e l'altro editor dei dati.

Introduzione.

Questo pacchetto fornisce un'implementazione Java per una struttura che fornisca i seguenti servizi:

  • Un servizio che determini il tipo di dati.
  • Un servizio che incapsuli l'accesso ai dati.
  • Un servizio che scopra le operazioni che sono possibili su quel particolare tipo di dati.
  • Un servizio che istanzi il corretto componente di software che corrisponde alla desiderata operazione su quel particolare insieme di dati.
Questa funzionalità è compatibile con la versione del JDK 1.1.x.

La struttura proposta è rappresentata in figura 1.
 


 Figura 1.

Descrizione struttura API

Gli oggetti proposti nella nuova interfaccia sono:

  • DataHandler

  • L'oggetto al centro del diagramma è una classe chiamata DataHandler.

    In pratica questa classe fornisce una struttura che permette di

    • Caricare i dati in memoria (tramite l'oggetto DataSource).
    • Riconoscerne la tipologia associandola ad un tipo MIME tra quelli registrati in un file che di solito viene chiamato mimetype.
    • Individuare tutte le operazioni che è possibile effettuare sul quel particolare tipo di dati (tramite l'oggetto CommandMap)
    • Individuare per ogni tipo di operazione il Bean apposito per la sua gestione (tramite l'oggetto CommandObject). Di solito le operazioni possibili per ogni oggetto MIME con i possibili Bean associati, si trovano in un file di nome mailcap.
    • Convertire i data objects in e da uno stream in formato external byte (tramite l'oggetto DataContentHandler).

    Il DataHandler implementa l'interfaccia Interface java.awt.datatransfer.Transferable che è utilizzata per gestire dati in un'operazione di trasferimento. Questo permette alle applicazioni e agli oggetti che definiscono i comandi di recuperare rappresentazioni alternative (nella forma di oggetti Java) dei dati in input da qualsiasi parte vengano.

    Struttura della classe:

    public Class DataHandler implements Transferable { 
    /* Inizializza la classe DataHandler */ 
       public DataHandler(DataSource ds);

    /* Inizializza la classe DataHandler. Questo costruttore è usato quando 
        l'applicazione ha già in memoria una rappresentazione dei dati nella forma
        di un oggetto Java */
     
       public DataHandler(Object obj, String mime_type);

    /*Costruisce un da un oggetto URL*/ 
       public DataHandler(URL url);

    /* Ritorna il DataSource associato ad esso. */ 
       public DataSource getDataSource() throws IOException;

    /* Ritorna il tipo MIME associato ai dati */ 
       public String getContentType();

    /* Ritorna un InputStream.
        Nel caso di DataHandlers  creati con DataSources e URL ritornerà semplicemente l'InputStream;
        Nel caso di Oggetto, cercherà di individuare il tipo dati utilizzando un DataContentHandler e 
        userà and use il metodo writeTo per trasformarlo in InputStream. */
     
       public InputStream getInputStream() throws IOException;

    /* Ritorna un OutputStream */ 
       public OutputStream getOutputStream() throw IOException;

    /* Ottiene il nome dei dati rappresentati dal DataHandler. */ 
       public String getName();

    /* Ritorna i MIMETypes (DataFlavor) di questi dati
        Il valore di ritorno di questo metodo è derivato dai tipi originali di questi dati esattamente come i  
        possibili tipi Oggetti sono ritornati utilizzando i disponibili DataContentHanders. */
     
       public synchonized DataFlavor[] getTransferDataFlavors();

       public boolean isDataFlavorSupported(DataFlavor flavor); 
       public Object getTransferData(DataFlavor flavor) throws IOException, UnSupportedFlavorException; 

    /* Imposta il CommandMap da usare, il DataHandler usa ilCommandMap dal 
        metodo statico getDefaultCommandMap nel  CommandMap per default. */
     
       public void setCommandMap(CommandMap cmdmap);

    /* Imposta il  DataContentHandlerFactory per il DataHandlers */ 
       public static synchronized setDataContentHandlerFactory( DataContentHandlerFactory factory);

    /* Scrive i dati del DataHandler in un OutputStream */ 
       public void writeTo(OutputStream os) throws IOException;

    /* Ritorna il contenuto del DataHandler in formato oggetto. */ 
       public Object getContent() throws IOException;

    /* Ritorna la lista delle operazioni possibili sui dati in esame
        Ritorna un array di classi BeanInfo che descrivono i preferiti beans che corrispondono 
        ai tipi MIME da trattare.In genere questo metodo ritorna un BeanInfo per ciascuna 
        operazione possibile per il tipo MIME. (chiama direttamente il CommandMap) */
     
       BeanInfo[] getPreferredCommands();

    /* Ritorna un array di classi BeanInfo che descrivono tutti i beans che corrispondono 
        ai tipi MIME da trattare.In genere questo metodo ritorna un BeanInfo per ciascuna 
        operazione possibile per il tipo MIME. (chiama direttamente il CommandMap) */ 
       BeanInfo[] getAllCommands();

    /* Ritorna il BeanInfo associato alla particolare operazione sul tipo di dato */ 
       BeanInfo getCommand(String cmd);

    /* Ritorna il Bean a partire da un BeanInfo */ 
       Object getBean(BeanInfo binfo);

    }

     

DataSource
    Il DataSource fornisce un meccanismo per caricare in memoria i dati

    La classe DataSource è usata dal DataHandler (e da ogni altra classe alla quale serva) per accedere ai dati in esame.

    L'oggetto DataSource incapsula i dati in esame in una classe che astrae la rappresentazione dei dati in memoria e astrae il meccanismo di rappresentazione in tipi MIME, e presenta all'utilizzatore una comune interfaccia di accesso ai dati. L'oggetto DataSource può essere utilizzato all'interno di comuni applicazioni (file systems e istanze di URL) o ogni altro tipo di applicazione che voglia implementare dei loro propri DataSources per cose come servers IMAP, oggetti databases, ecc. Vi è una corrispondenza uno a uno tra i dati in esame (files per istanza) e gli oggetti DataSource. Notiamo anche che l'oggetto DataSource è responsabile dell'individuazione del tipo dei dati. Nel caso di un file system un DataSource potrebbe usare un semplice meccanismo come l'estensione del file per individuare il tipo di dato (esempio un'estensione txt corrisponde al tipo MIME text/plain); nel caso invece che un DataSource debba supportare dei dati in input su web è in grado di esaminare il data stream e determinare il suo tipo MIME.
    Struttura dell'interfaccia:

    public interface DataSource { 

    /* Ritorna una rappresentazione dei dati in formato InputStream 
       Questo metodo solleverà un'eccezione se non potrà creare un (nel in cui il DataSource fu 
        creato con un oggetto che non era un InputStream */

       public InputStream getInputStream() throws IOException;

    /* Ritorna un OutputStream */ 
       public OutputStream getOutputStream() throws IOException;

    /* Ritorna il tipo MIME associato ai dati */ 

       public String getContentType() throws IOException;

    /* Ritorna il nome di uno specifico dominio. Per esempio nel caso di un file, ritorna il nome del file. */ 

       public String getName();
    }

     

  • DataContentHandler

  • E' un meccanismo per convertire i data objects in e da uno stream in formato external byte.
    L'interfaccia DataContentHandler è usata per trasformare in OutputStreams oggetti java creati da DataHandler a partire da InputStreams. Il DataHandler usa questo meccanismo per implementare l'interfaccia Transferable. Gli oggetti DataContentHander servono per riflettere i tipi MIME dei dati provenienti da un InputStream di un DataSource.
    I DataFlavors sono utilizzati per rappresentare i tipi dati accessibili da un DataContentHandler. Per esempio in un'applicazione potremmo avere un oggetto image che si desidera vedere come un file gif (DataFlavor). Un DataContentHandler appropriato dovrebbe essere usato per convertire un oggetto Image in un byte stream di tipo gif.
    Struttura dell'interfaccia:

    public interface DataContentHandler { 

    /* Ritorna i DataFlavors questo DCH o un array di lunghezza zero */ 
       public DataFlavors[] getTransferDataFlavors();

    /* Ritorna l'oggetto trasferito */ 
       public Object getTransferData(DataFlavor df, DataSource ds) throws UnsupportedDataFlavorException,IOException;

    /* Ritorna un oggetto rappresentante il contenuto del DataSource. */
       public Object getContent(DataSource ds) throws IOException;

    /* trasforma un oggetto in un OutputStream */ 
       public void writeTo(Object obj, OutputStream os) throws IOException;

    }


 
  • CommandMap

  • E' un meccanismo per localizzare componenti visuali che lavorano sui data objects. Il CommandMap fornisce un servizio che permette agli utilizzatori della loro interfaccia di determinare i comandi disponibili in quel particolare tipo MIME che funzionano esattamente nello stesso modo in cui funzionano quando vengono utilizzati in un'interfaccia che normalmente li usa (Web, gestore di file, ecc.) . Il CommandMap può generare e mantenere una lista di possibilità di un particolare tipo di dato mediante un meccanismo definito dall'implementazione di una particolare istanza del CommandMap. Il modello di programmazione per il software del componente che utilizza i comandi dovrebbe essere il JavaBeans. Questi beans potrebbero usare la serializzazione, esternalizzazione o implementare l'interfaccia 'CommandObject' per permettere al dato di essere elaborato da loro.
    L'obiettivo è fornire una flessibile ed estensibile struttura per il CommandMap.
    L'interfaccia CommandMap permette ai programmatori di sviluppare soluzioni per scoprire dal sistema quali comandi sono accessibili. Una possibile implementazione potrebbe essere accedere ai tipi registrati sulla piattaforma o usare soluzioni basate su un server.
    Per il momento viene fornita una semplice soluzione di default basata sy RFC 1343 (.mailcap) come funzionalità.
    Struttura della classe:
     

    public abstract class CommandMap { 

    /* Ottiene il CommandMap di default */ 

       
    public static CommandMap getDefaultCommandMap();

    /* Imposta il CommandMap di default */ 
        public static void setDefaultCommandMap(CommandMap);

    /* Ritorna un array di classi BeanInfo che descrivono i preferiti beans che 

    corrispondono ai al particolare mimeType. */ 
       abstract public BeanInfo[] getPreferredCommands(String mimeType); 

    /* Ritorna un array di classi BeanInfo che descrivono tutti i comandi 

    conosciuti dal CommandMap che accettono questo tipo MIME */ 
       abstract public BeanInfo[] getAllCommands(String mimeType);

    /* Ritorna la classe BeanInfo associata al particolare tipo MIME 

    e al particolare comando*/ 
       abstract public BeanInfo getCommand(String mimeType, String cmdName);
    }

  •  CommandObject
    I CommandObjects sono JavaBeans che implementano l'interfaccia CommandObject. L'interfaccia CommandObject permette ai beans di essere usati nelle strutture di accesso ai loro oggetti DataSource e DataHandler direttamente.
    Struttura dell'interfaccia:
    public interface CommandObject { 
    
    /*Imposta il DatHandler */ 
       public void setDataHandler( DataHandler dh );
    
    }
  • DataContentHandlerFactory
    Come il ContentHandlerFactory nel package java.net, il DataContentHandlerFactory è un'interfaccia che permette agli sviluppatori di creare come vogliono (ridefenendo il metodo dell'interfaccia) di creare un particolare DataContentHandler associato al tipo MIME in esame.
    public interface DataContentHandlerFactory { 
    
    /*Crea un DataContentHandler per il particolare tipo MIME */ 
       public DataContentHandler createDataContentHandler( String mimeType);
    
    }

Analisi del pacchetto JAF.

Il pacchetto del JavaBeans Activation Framework, contiene i seguenti files:

  • activation.jar

  • Si tratta di un file jar contenente
    • Il package javax.activation corrispondente alle API JAF cioè le classi

    • DataSource.class
      MailcapCommandMap.class
      CommandMap.class
      DataContentHandler.class
      CommandObject.class
      DataHandler.class
      DataHandlerDataSource.class
      DataSourceDataContentHandler.class
      ObjectDataContentHandler.class
      URLConnectionDataSource.class
      DataContentHandlerFactory.class
      MimeType.class
      MimeTypeParseException.class
      ActivationDataFlavor.class
      MimeTypeParameterList.class
      FileDataSource.class
       
    • I packages com.sun.activation.registries e com.sun.activation.viewers che servono a far funzionare la demo contenuta nel pacchetto e servono rispettivamente una per la gestione e registrazione dei file mailcap e dei tipi MIME, e l'altra come libreria dei Beans che gestiscono i tipi MIME (sono presenti un visualizzatore di Immagini, un visualizzatore di Canvas, un visualizzatore di testi e un editor di testi):

    • registries/MailcapFile.class
      registries/MailcapParseException.class
      registries/MailcapEntry.class
      registries/MailcapTokenizer.class
      registries/MimeTypeFile.class
      registries/MimeTypeEntry.class
      viewers/ImageViewer.class
      viewers/ImageViewerCanvas.class
      viewers/TextEditor.class
      viewers/TextViewer.class
       
    • Il file mailcap META-INF/ mimetypes che contiene tutti i tipi MIME previsti. In particolare troviamo:

    • text/html html
      text/plain txt text
      image/gif gif GIF
      image/ief ief
      image/jpeg jpeg jpg jpe JPG
      image/tiff tiff tif
      image/x-xwindowdump xwd
      application/postscript ai eps ps
      application/rtf rtf
      application/x-tex tex
      application/x-texinfo texinfo texi
      application/x-troff t tr roff
      audio/basic au
      audio/midi midi mid
      audio/x-aifc aifc
      audio/x-aiff aif aiff
      audio/x-mpeg mpeg mpg
      audio/x-wav wav type=audio/x-wav desc="WAV Audio" exts="wav"
      video/mpeg mpeg mpg mpe
      video/quicktime qt mov
      video/x-msvideo avi

    • Il file mailcap META-INF/mailcap che contiene per ogni tipo MIME e ogni comando trattato .il Bean di gestione dati associato. In particolare troviamo:

    • image/gif;; x-java-view=com.sun.activation.viewers.ImageViewer
      image/jpeg;; x-java-view=com.sun.activation.viewers.ImageViewer
      text/*;; x-java-view=com.sun.activation.viewers.TextViewer
      text/*;; x-java-edit=com.sun.activation.viewers.TextEditor

    • Il file indice META-INF/Manifest.mf che contiene l'indice di tutti i files contenuti nel .jar.
  1. Documentazione HTML. Si tratta della documentazione del package javax.activation
  1. Demo. Vi è una serie di programmi che permettono di testare il pacchetto.

  2. In particolare:

    • JAFApp. Emula un File System. Dopo aver visualizzato il contenuto della directory dove è presente il programma (figura 2.), associa ad ogni possibile estensione registrata (per esempio txt, gif, ecc.) il tipo MIME corrispondente e un elenco di possibili Bean gestori dei dati.



     Figura 2.


      Se l'estensione non viene riconosciuta (per esempio nelle estensioni .class), e il tipo MIME diviene unknown. Per esempio in figura possiamo vedere come selezionando il file README.txt, venga individuato il tipo MIME text/plan e associato ai 2 possibili bean TextViewer (comando view) e TextEditor (comando edit).
      Selezionando poi per esempio TextEditor e cliccando su "Launch!" viene eseguito il bean su questo file (figura 3.)
       


       Figura 3.



    • FileView. Associa ad ogni estensione riconosciuta, il bean corrispondente al comando view.
      In figura 4. Si può vedere come il lancio del comando

                   java FileView imma.gif

      consenta il lancio del Bean ImageViewer per la visualizzazione dell'immagine gif.


       Figura 4.

    • DCHTest. E' un programma che permette di generare un particolare DataContextHandler per un determinato file di tipo txt.  Per esempio lanciando da DOS il comando java DCHTest README.txt abbiamo il risultato della figura 5:


    •  Figura 5.




      Come si può vedere viene creato il FileDataSource, il DataHandler, e viene associato il DataContextHandler PlainDCH (si ricorda che la classe DataContextHandler associa ad ogni MIME uno o più trattamenti di stream byte), che restituisce 2 DataFlavors (ciascuna istanza di questa classe appartenente al package java.awt.datatransfer, gestisce un formato dati così come dovrebbe apparire in un clipboard,, durante un drag and drop o in un file system.
       
       

    • DCHTest2. E' un programma che permette di gestire un file txt e un mailcap e di risalire ai dataflavor. In realtà è solo uno scheletro da implementare. Infatti se creiamo un file mailcap contenente la dicitura

    •         text/plain;; qualsiasicosa

      e lanciamo da DOS il comando java DCHTest README.txt mailcap abbiamo il risultato della figura 6 indipendentemente da chi si qualsiasicosa. Esegue solo un controllo sulla sintassi del mailcap.


       Figura 6.


Esempio di utilizzo Bean in una struttura JAF

Queste API sono state realizzate avendo in mente di limitare al massimo le modifiche da apportare ad un generico JavaBean.
In questa prima versione i viewers/editors sono associati ai dati attraverso un semplice meccanismo di registrazione simile ai file mailcap. Nelle future versioni si farà uso della classe ClassLoader che permetterà di conoscere la configurazione dei files sul sistema. Questo permetterà agli sviluppatori di spedire supplementari file registro da aggiungere al sistema, e quindi di caricare packages aggiuntivi in runtime.  In questa versione tutto ciò che devono fare i Componenti Beans è implementare l'interfaccia CommandObject. In questo modo sarà possibile per loro comunicare con i loro DataHandler e DataSource. Dopo di che i Beans possono ancora usare i tradizionale metodi di Serialization e Externalization disponibili nel JDK 1.1 e più.
Quindi un'applicazione che usa questa struttura potrebbe gestire un oggetto del genere:

    ObjectOutputStream oos = new ObjectOutputStream( 
    
    try { 
    
       data_handler.getOutputStream()); 
    
    } catch(IOException e) {} 
    
    //scrive l'oggetto externalizable 
    
    my_externalizable_bean.writeExternal(oos);
L'uso di oggetti serializzati è ancora in fase di sviluppo.
Cercando ora di chiarire agli sviluppatori di Beans come possano integrare le JavaBeans Activation Framework, presenteremo alcuni scenari di esempio.
Per primo rivediamo i componenti principali del JavaBeans Activation Framework:
  • DataSource. Un meccanismo per caricare in memoria i dati
  • DataContentHandler. Un meccanismo per convertire i data objects in e da uno stream in formato external byte.
  • CommandMap. Un meccanismo per localizzare componenti visuali che lavorano sui data objects.
  • Beans. I componenti visuali che operano sui data objects.
Lo sviluppatore di Beans è improbabile che abbia bisogno di sviluppare un nuovo DataSource o CommandMap. Piuttosto potrebbe invece aver bisogno di sviluppare un DataContentHandler e naturalmente di costruire i Beans visuali.
Supponiamo di costruire un nuovo editor di nome MokaEditor corrispondente al nuovo formato file Moka. Il MokaEditor sarà esso stesso un Bean, e con questo prodotto sarà possibile editare, stampare, visualizzare e salvare i file di formato Moka.
Si può definire un file di formato Moka in un modo indipendente dal linguaggio scegliendo di utilizzare il tipo MIME "application/x-moka" per descrivere il file, e associarlo all'estensione ".mok" utilizzato dai files contenenti dati moka.
Per integrare questo nella struttura, c'è bisogno di semplici estensioni della classe MokaBean (bean di riferimento) una per ciascun comando che si desidera gestire.
Per esempio per il comando Print si potrebbe scrivere una cosa del genere:
    public class MokaPrintBean extends MokaBean { 
       public MokaPrintBean() { 
          super(); 
          initPrinting(); 
       } 
    }

A questo punto bisogna creare un file mailcap che listi per il tipo MIME "application/x-moka" tutti i comandi che sono supportati con i relativi Beans di gestione.
Potrebbe essere una cosa del genere:

application/x-moka; ; x-java-View=com.dany.MokaViewBean
application/x-moka; ; x-java-Print=com.dany.MokaPrintBean
application/x-moka; ; x-java-Edit=com.dany.MokaEditBean

Questo perché il prodotto supporta i comandi View, Print e Edit.
Dopo di che bisogna creare il file mimetypes file con la descrizione del tipo MIME:

type=application/x-moka desc="Moka" exts=mok

Tutti questi componenti saranno poi impacchettati in un file JAR:

META-INF/mailcap
META-INF/mimetypes
com/dany/MokaBean.class
com/dany/MokaEditBean.class
com/dany/MokaViewBean.class
 

Poiché tutto è costruito sulla base di un solo Bean e poiché non sono richiesti accessi dall'esterno agli oggetti Moka, Non vi è necessità di un DataContentHandler (che gestisce il meccanismo per convertire i data objects in e da uno stream in formato external byte).  Il MokaBean potrebbe invece implementare l'interfaccia Externalizable e usare i suoi metodi per leggere e scrivere i files di formato Moka.. Il DataHandler provvederà a chiamare i metodi dell'interfaccia Externalizable methods al momento opportuno.  Se invece si ha necessità di manipolare sistematicamente i files moka senza necessariamente di visualizzatori o editor Beans, occorre creare un DataContentHandler che converta un byte stream in o da un oggetto Moka, potrebbe chiamarsi per esempio MokaDataContentHandler estensione di DataContentHandler. In fase di lettura, il MokaDataContentHandler legge il byte stream e ritorna un nuovo oggetto Moka. In fase di scrittura, il MokaDataContentHandler prende l'oggetto Moka e produce il corrispondente byte stream.   Il MokaDataContentHandler è considerato come una classe ed è considerato come un DataContentHandler che può trattare gli oggetti Moka. E' descritto nel file mailcap incluso nel file JAR. Il mailcap diventa:

application/x-moka; ; x-java-View=com.dany.MokaViewBean
application/x-moka; ; x-java-Print=com.dany.MokaPrintBean
application/x-moka; ; x-java-Edit=com.dany.MokaEditBean
application/x-moka; ; x-java-ContentHandler=com.dany.MokaDataContentHandler

I MokaBean possono continuare ad implementare l'iterfaccia Externalizable, e quindi leggere e scrivere byte streams di formato Moka, oppure più facilmente potrebbero lavorare con gli oggetti Moka direttamente grazie al MokaDataContentHandler in questo modo:

  • Dovranno implementare l'interfaccia CommandObject.
  • Dovranno usare il metodo setDataHandler impostando il DataHandler opportuno
  • Dovranno invocare il metodo getContent della classe DataHandler che ritornerà un oggetto moka (prodotto dal MokaDataContentHandler).
Conclusione

Questo pacchetto rappresenta la prima realizzazione delle specifiche Glasgow. Questa prima versione mi è sembrata abbastanza promettente. Staremo a vedere se la tecnologia dei JavaBeans con queste nuove implementazione prenderà talmente piede da facilitarci veramente la vita nella costruzione del software e nella portabilità dello stesso.
Nel prossimo capitolo parleremo delle specifiche per l'uso di un sottosistema Drag and Drop subsystem nelle Java Foundation Classes.
 

 
 
 
 
 

MokaByte rivista web su Java

MokaByte ricerca nuovi collaboratori
Chi volesse mettersi in contatto con noi può farlo scrivendo a mokainfo@mokabyte.it