Nel mio articolo del mese precedente spiegavo come poteva essere realizzato un semplice JavaBean. Ora partirò da quell'esempio e userò le API del ponte JavaBean/ActiveX (<http://splash.javasoft.com/beans/bridge/>) per realizzare un ActiveX contenitore del JavaBean. Il passo successivo sarà usare tale ActiveX in un form VB5

Introduzione.

Questa è una dimostrazione di come JavaBeans possa girare in un contenitore ActiveX.

I passi da seguire sono:

1. Costruzione di un JavaBeans. 2. Registrazione a Windows tramite l'esecuzione di una classe java appartenente alle API del ponte JavaBeans/ActiveX. 3. Test in un form VB5.

Costruzione di un JavaBeans.

Nel numero precedente abbiamo spiegato come realizzare un JavaBeans. Partiremo da quell'esempio (su [](../../05/index.htm)).

Brevemente riassumiamo in cosa consiste il JavaBeans realizzato: si tratta di un bottone con un'immagine che cambia al verificarsi dei due eventi MOUSEUP e MOUSEDOWN.

Utilizzeremo per la dimostrazione il ponte JavaBeans-ActiveX versione beta 2 il jdk 1.1., dal momento che questa versione del ponte non è ancora compatibile con il jdk 1.1.1. che è l'ultima versione.

Sarà necessario apportare alcune modifiche al programma MioBottone.java a causa di alcuni bug presenti nel jdk 1.1., tra i quali quelli di non aver serializzato alcune classi, e in particolare la classe Image di cui facciamo uso. Infatti per far sì che un oggetto sia serializzabile (cioè sia possibile mantenere il suo stato mediante memorizzazione su disco, ciò che rende l'oggetto "persistente") è necessario che nelle istruzioni dove definisco la classe si faccia utilizzo di classi serializzabili (V. articolo di Massimo Carli sulla Serializzazione su [](../../04/index.htm)).

Se si cercasse di utilizzare il ponte JavaBeans-ActiveX per registrare il JavaBeans a Windows sulla base dei programmi realizzati non otterremmo errori né al momento della registrazione, né al momento del trascinamento dell'oggetto sul form VB5; ma al momento dell'esecuzione della procedura VB5 otterremmo il messaggio di errore "Bean cannot stored to persistance stream" in seguito al verificarsi dell'eccezione "java.io.NotSerializableException : sun.awt.windows.Wimage" ciò che rende evidente che la procedura non riesce a memorizzare l'oggetto Image.

Una soluzione è quella suggerita da Massimo Carli e cioè quella di costruire una classe che estenda la classe Image e sia serializzabile. Riportiamo di seguito il sorgente proposto da lui:

package dany.Bottoni;

// **************************************************************

// *     Class : ImageSer                                    *

// **************************************************************

// * Questa classe   definisce l'oggetto Image  serializzabile. *

// *                                                            *

// * Author: Massimo Carli         Release: 0.01    Date: 12/01/97  *

// * Environment: JDK 1.1                                       *

// **************************************************************

ü Questa classe   definisce l'oggetto Image  serializzabile  

*@author Massimo Carli

*/

import java.net.URL;

import java.awt.*;      // Importiamo gli oggetti

import java.util.*;     // Per il vector

import java.io.*;               // Per la serializzazione

import java.awt.image.*;

public class ImageSer  implements Serializable,ImageObserver {

        private Image img;              // E' l'immagine

        // Costruttori

        

        ü Costruttore vuoto*/

        public ImageSer(){

                img = null;

        }

        

        ü Costruttore con immagine

        *@param imm immagine

        */

        public ImageSer(Image imm){

                img = imm;

        }

        public ImageSer(String name){

                try {

                        URL url = getClass().getResource(name);

                        Toolkit tk = Toolkit.getDefaultToolkit();

                        img =  tk.createImage((ImageProducer) url.getContent());

                } catch (Exception ex) {

                        System.err.println("Errore di caricamento immagini");    

                } 

        }

        

        ü Setta l'immagine

        *@param imm immagine

        */

        public void setImage(Image imm){

                img = imm;

        }

        

        

        

        ü Ritorna l'immagine

        *@return immagine memorizzata

        */

        public Image getImage(){

                return img;

        }

        // Dobbiamo gestire la serializzazione 

        // METODI PROVVISORI DA ELIMINARE QUANDO VERRA' FATTA LA SERIALIZZAZIONE

        private void readObject(ObjectInputStream stream) throws IOException, ClassNotFoundException{

        // Determiniamo le dimensioni dell'immagine

            Dimension dim = (Dimension)stream.readObject(); 

            int dimh = dim.height;  // Prendiamo l'altezza

            int dimw = dim.width;   // Prendiamo la larghezza           

            if ((dimh!=0) && (dimw!=0)){        // Se l'immagine esiste la leggiamo  

                  int numpix = dimh*dimw;       // Calcoliamo il numero di punti

                  int pix[] = new int[numpix];          // Creiamo il vettore di pixel

                  // Leggiamo i pixel

                  for (int i=0;i
new MemoryImageSource(dimw,dimh,pix,0,dimw);

                  Toolkit tk = Toolkit.getDefaultToolkit();

                  img = tk.createImage(mis);            

            } else img=null;

      }

  

      private void writeObject(ObjectOutputStream stream) throws IOException {

            // Determiniamo le dimensioni dell'immagine

            int dimh=0; // Altezza immagine

            int dimw=0; // Larghezza immagine

            if (img!=null){

                  dimh = img.getHeight(this);   // Prendiamo l'altezza

                  dimw = img.getWidth(this);    // Prendiamo la larghezza    

                  int numpix = dimh*dimw;             // Calcoliamo il numero di punti

                  int pix[] = new int[numpix];  // Creiamo il vettore di pixel

                  PixelGrabber pg = new PixelGrabber(img,0,0,dimw,dimh,pix,0,dimw);

                  try{

                       pg.grabPixels(); // Otteniamo i pixels

                  } catch(InterruptedException ie){}

                  Dimension dim = new Dimension(dimw,dimh);

                  // Iniziamo a scrivere

                  stream.writeObject(dim);

                  for (int i=0;i < numpix;i++){

                      stream.writeInt(pix[i]);

                  }     // fine for

            }  // fine if       

            else

                { 

                  Dimension dim = new Dimension(dimw,dimh); // Scriviamo le dim nulle

                  stream.writeObject(dim);                  // sullo stream

            } // fine else              

    }   // fine writeObject

    public  boolean imageUpdate(Image img,

                                int infoflags,

                                int x,

                                int y,

                                int width,

                                int height){

        return true;                                    

    }                                    

}

Nel corso della costruzione dell'ActiveX contenitore di questo javabean sono state notate delle incongruenze nel caricamento delle immagini. Questo ci ha obbligato a modificare il sorgente per renderlo adatto alla dimostrazione.

Le modifiche che sono quindi state apportate al sorgente MioBottone.java riguardano:

Dobbiamo sostituire Image con la nuova classe ImageSer. Le classi Image imm e Image imm1 diventano quindi ImageSer imm e ImageSer imm1. Di conseguenza nel metodo paint() bisogna sostituire le variabili imm e imm1 nel metodo g.drawImage con imm.getImage() e imm1.getImage().

Togliere dal costruttore MioBottone il caricamento dell'immagine tramite Toolkit e cioè le istruzioni che abbiamo inserito nella classe ImageSer

try {

         URL url = getClass().getResource(name); 

         Toolkit tk = Toolkit.getDefaultToolkit(); 

         img = tk.createImage((ImageProducer) url.getContent()); 

     } catch (Exception ex) {

         System.err.println("Errore di caricamento immagini"); 

}

e sostituirle
con l'istanziazione delle classi ImageSer, cioè :

 

this.imm = new ImageSer("imma.gif");

this.imm1 = new ImageSer("imma1.gif");

L'inserimento dei nuovi metodi getFont() e setfont() in questo modo :

public Font getFont() {

         return font;

     }

e

public void setfont(Font nuovofont) {

         Font vecchiofont = font;

         font = nuovofont;

         changes.firePropertyChange("font", vecchiofont, nuovofont); 

}

Questa e' la prima incongruenza mostrata. Prima non avevamo bisogno di definirli e ora invece e' come se VB5 introducesse le sue proprietà di default, impostando quindi un suo font di default. Infatti nel nostro programma avevamo impostato un font Times New Roman Normale 36 e senza questi metodi ci ritroviamo in VB5 con un font MS Sans Serif Normale 8. A quanto pare impostando questi 2 metodi (e sono necessari entrambi) è come se venisse chiamato il primo per ottenere il font definito nel programma e richiamato il secondo per impostarlo nuovamente.

Seconda incongruenza riguardante il metodo seguente :

public synchronized void paint(Graphics g) {

   if (Immagine == 1)

   {

        g.drawImage(imm.getImage(),x,y,this); 

        setEnabled(true); 

   }

   else {

        g.drawImage(imm1.getImage(),x,y,this); 

   } 

}

In questo metodo la seconda immagine non viene mostrata quando la variabile booleana Immagine passa da 1 a 2. L'unica soluzione che e' stata trovata e' disegnare la prima volta entrambe le immagini in questo modo :

public synchronized void paint(Graphics g) {

         if (Immagine == 1)

         {

             g.drawImage(imm1.getImage(),x,y,this); 

             g.drawImage(imm.getImage(),x,y,this); 

             setEnabled(true); 

         }

         else {

             g.drawImage(imm1.getImage(),x,y,this); 

         } 

}

Il programma finale è mostrato qui

A questo punto dopo aver compilato di nuovo aggiungiamo la classe ImageSer.class nel file manifest.mf che diventa:

Name:
dany/Bottoni/MioBottone.class

Java-Bean:
True

Name:
dany/Bottoni/ImageSer.class

Name:
dany/Bottoni/MioBottoneBeanInfo.class

Name:
dany/Bottoni/imma.gif

Name:
dany/Bottoni/imma1.gif

Utilizziamo
il comando jar in questo modo:

jar
cfm MioBottone.jar manifest.mf dany\Bottoni\MioBottone.class dany\Bottoni\MioBottone.class
dany\Bottoni\MioBottoneBeanInfo.class dany\Bottoni\ImageSer.class dany\Bottoni\imma.gif
dany\Bottoni\imma1.gif

Abbiamo così MioBottone.jar da registrare.

Registrazione a Windows tramite l'esecuzione di una classe java appartentente alle API del ponte JavaBeans/ActiveX.

Per usare un bean come oggetto incorporato all'interno di applicazioni Microsoft (Microsoft Office, Internet Explorer e Visual Basic), si costruisce un package riguardante il Bean e lo si usa come un componente ActiveX.

Al momendo della costruzione del packager in un passo intermedio viene generato un text file contenente l'identificativo dell'oggetto, il path di esecuzione per il componente, le informazioni sul ponte e il path dove si trova il file TypeLib generato, dopo di che viene mandato in esecuzione il programma REGEDIT.EXE con questo file in input. Il bean viene così registrato a Windows.

Il Packager genera anche dei files stub (sorgenti java e compilati .class) che costituiscono il ponte tra il Bean e il Packager ActiveX.

Quindi riepilogando, al termine del packaging viene generato :

1. Il file di registrazione. 2. I files stub. 3. Il file TypeLib (.tlb) che è un file binario che descrive i metodi, gli eventi e le proprietà del componente. I sorgenti sono cancellati al momento del completamento del packaging e i files .class sono aggiunti al file jar che originalmente conteneva il JavaBean. Questo file jar è completamente portabile se le classi *sun.beans.ole.\** , appartenenti alle API del ponte JavaBeans/ActiveX, sono accessibili al momento del caricamento del jar.

A questo punto da DOS lanciamo il seguente comando :

> java sun.beans.ole.Packager

Verrà visualizzata la maschera "Ole Bean Packager". A questo punto cliccando sul bottone browse possiamo selezionare dalla directory il JavaBean MioBottone.jar appena creato. Avremo quindi la figura 1.

Figura 1
Figura 1

Pigiando su Next abbiamo la figura 2. dove troviamo l'elenco del beans contenuti nel .jar.

Nel nostro caso ne abbiamo solo uno, quindi possiamo scegliere solo questo per la conversione.

Figura 2
Figura 2

Pigiando su Next abbiamo la figura 3. dove l'applicazione ci chiede di digitare un nome per l'ActiveX corrispondende, e ci propone lo stesso nome del bean.

Figura 3
Figura 3

Pigiando su Next abbiamo la figura 4. dove l'applicazione ci chiede di digitare un path dove scrivere il TypeLib e il File Registro.

Figura 4
Figura 4

Pigiando su Next abbiamo la figura 5. dove cliccando sul bottone start generation possiamo far partire la generazione dell'ActiveX.

Figura 5 - Alla fine dopo opportune avvertimenti della creazione dei files stub, file TypeLib e file registro viene emesso un avvertimento di avvenuta registrazione, e un successivo avvertimento di conclusione positiva del packaging ("packaging succeeded" figura 6)
Figura 5 - Alla fine dopo opportune avvertimenti della creazione dei files stub, file TypeLib e file registro viene emesso un avvertimento di avvenuta registrazione, e un successivo avvertimento di conclusione positiva del packaging ("packaging succeeded" figura 6)
Figura 6
Figura 6

Se guardiamo nel file MioBottone.jar con il comando jar tvf ci accorgiamo che e' stato arricchito dei seguenti file stubs :

1. dany/Bottoni/MioBottoneStubs/java/awt/event/OleActionListener.class. Che ci permette di intercettare gli eventi Action. 2. dany/Bottoni/MioBottoneStubs/java/beans/OlePropertyChangeListener.class. Che ci permette di intercettare il cambiamento delle proprietà. 3. dany/Bottoni/OleMioBottoneInterface.class

Come ricorderete dal mio precedente articolo nel sorgente MioBottoneBeanInfo.java avevamo dato le specifiche proprio per gli eventi actionPerformed e propertyChange corrispondenti ai punti 1. e 2.

Osserviamo ora il file registro MioBottone.reg usato in fase di packaging per registrare l'ActiveX nel registro di Windows :

REGEDIT

HKEY_CLASSES_ROOT\MioBottone.Bean
= MioBottone Bean Control

HKEY_CLASSES_ROOT\MioBottone.Bean\CLSID
= {05463000-AA17-11D0-A8E2-444553540000}

HKEY_CLASSES_ROOT\MioBottone.Bean\CurVer
= 1

HKEY_CLASSES_ROOT\MioBottone.Bean.1
= MioBottone Bean Control

HKEY_CLASSES_ROOT\MioBottone.Bean.1\Insertable
=

HKEY_CLASSES_ROOT\MioBottone.Bean.1\CLSID
= {05463000-AA17-11D0-A8E2-444553540000}

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}
= MioBottone Bean Control

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\InprocServer32
= d:\bdk\bridge\classes\beans.ocx

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\TypeLib
= {05463001-AA17-11D0-A8E2-444553540000}

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\ProgID
= MioBottone.Bean.1

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\VersionIndependentProgID
= MioBottone.Bean

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\JarFileName
= D:\MioBottone.jar

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\JavaClass
= dany.Bottoni.MioBottone

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\InterfaceClass
= sun/beans/ole/OleBeanInterface

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\Control
=

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\Insertable
=

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\MiscStatus
= 0

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\MiscStatus\1
= 18833

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\ToolboxBitmap32
= d:\bdk\bridge\classes\beans.ocx,1

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\DefaultIcon
= d:\bdk\bridge\classes\hotjava.ico

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\Version
= 1.0

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\DataFormats\GetSet\0
= 1,1,1,1

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\DataFormats\GetSet\1
= 2,1,16,1

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\DataFormats\GetSet\2
= 3,1,32,1

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\verb\0
= &Edit,0,2

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\verb\-1
= Show,0,0

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\verb\-2
= Open,0,0

HKEY_CLASSES_ROOT\CLSID\{05463000-AA17-11D0-A8E2-444553540000}\verb\-3
= Hide,0,1

HKEY_CLASSES_ROOT\TypeLib\{05463001-AA17-11D0-A8E2-444553540000}
= MioBottone Bean Control Type Library

HKEY_CLASSES_ROOT\TypeLib\{05463001-AA17-11D0-A8E2-444553540000}\1.0
= MioBottone Bean Control

HKEY_CLASSES_ROOT\TypeLib\{05463001-AA17-11D0-A8E2-444553540000}\1.0\0\win32
= d:\bdk\bridge\classes\MioBottone.tlb

HKEY_CLASSES_ROOT\TypeLib\{05463001-AA17-11D0-A8E2-444553540000}\1.0\FLAGS
= 2

HKEY_CLASSES_ROOT\TypeLib\{05463001-AA17-11D0-A8E2-444553540000}\1.0\HELPDIR
= d:\bdk\bridge\classes

Come possiamo osservare ad ogni ActiveX generato corrisponde un CLSID specifico. In questo file ritroviamo tutte le informazioni che abbiamo dato alla procedura di packaging compresi i path dove andare a trovare i vari .jar, .tlb e cosi' via

Test in un form VB5.

Siamo finalmente arrivati allo scopo centrale di questo articolo : il test del nuovo ActiveX contenente il vecchio JavaBeans, in un form VB5.

A tale proposito, dopo aver lanciato VB5, selezionare la voce Components... dal menù Project (oppure cliccare con il bottone destro del mouse sulla toolbar e scegliere la voce Components... del popup menù). Apparirà una finestra con tutti gli ActiveX gestiti, tra i quali MioBottone Bean Control come si vede nella figura 7.

Figura 7 - Selezionare il componente e premere OK ; il componente verrà riportato nella toolbar (figura 8.)
Figura 7 - Selezionare il componente e premere OK ; il componente verrà riportato nella toolbar (figura 8.)
Figura 8 - Lo scopo di questo esercizio e' di usare il componente MioBottone per cambiare una proprietà di un componente VB5. Quindi al clic del MioBottone cambieremo la proprietà foreground di un textfield cambiandola da nera a rossa e viceversa
Figura 8 - Lo scopo di questo esercizio e' di usare il componente MioBottone per cambiare una proprietà di un componente VB5. Quindi al clic del MioBottone cambieremo la proprietà foreground di un textfield cambiandola da nera a rossa e viceversa

Trascinare il componente MioBottone e il componente textfield sul Form (figura 9.)

Figura 9 - Come possiamo vedere nelle proprietà di MioBottone (figura 10.) ritroviamo le proprietà che avevamo definite nel sorgente java (per esempio Posx e Posy)
Figura 9 - Come possiamo vedere nelle proprietà di MioBottone (figura 10.) ritroviamo le proprietà che avevamo definite nel sorgente java (per esempio Posx e Posy)
Figura 10
Figura 10

Ora in corrispondenza dell'evento actionPerformed dell'istanza MioBottone1 di MioBottone scriviamo le seguenti righe di codice :

> Private Sub MioBottone1_actionPerformed(ByVal modifiers As Long, ByVal actionCommand As String, ByVal ID As Long, ByVal source As Object) > > If MioTesto.ForeColor = vbRed Then > > MioTesto.ForeColor = vbBlack > > Else > > MioTesto.ForeColor = vbRed > > End If > > End Sub

Dove MioTesto è il nome del textfield.

Questo farà in modo che al clic di MioBottone il testo cambierà colore da nero a rosso e viceversa.

Il risultato è in figura 11.

Figura 11 - Notare come oltre all'evento esterno che cambia il colore del testo, vengano intercettati anche gli eventi definiti internamente al JavaBean che fanno cambiare l'immagine
Figura 11 - Notare come oltre all'evento esterno che cambia il colore del testo, vengano intercettati anche gli eventi definiti internamente al JavaBean che fanno cambiare l'immagine

Concludiamo l'esempio qui sperando che possa esservi stato di aiuto per la comprensione di come integrare codice java in un contenitore ActiveX. E' interessante vedere come si cerca di realizzare sempre dei ponti di collegamento tra le varie tecnologie. Accenniamo anche al fatto che l'IBM sta mettendo a punto assieme alla società Taligent un set di API che permette proprio di realizzare l'esatto contrario : la trasformazione di un ActiveX in JavaBean (indirizzo <http://ftp-products2.taligent.com/MigrateTool/prodinfo.html>).