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);
}
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:
-
Il file indice META-INF/Manifest.mf che contiene
l'indice di tutti i files contenuti nel .jar.
-
Documentazione
HTML. Si tratta della documentazione del package javax.activation
-
Demo.
Vi è una serie di programmi che permettono di testare il pacchetto.
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.
-
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.
|