In questo articolo ho deciso di illustrarvi un progetto in Java per l'importazione dei database nei documenti HTML, con la possibilitα di consultare, modificare e aggiungere record all'archivio stesso direttamente dall'ipertesto. Recentemente mi sono giα occupato di questa problematica, risolvendola con l'uso del complesso C++ o di alcuni scripts lato server. Adesso, invece, come giα menzionato ho voluto affrontare il "porting" con l'utilizzo di Java.
Preciso subito che nell'interno dell'aticolo troverete i listati dei file che costituiscono il progetto (.java, ecc.): si riferiscono ad un database d'esempio (molto semplice con pochissimi campi, realizzato con Microsoft Access 97. Nulla vieta l'uso di una base di dati molto complessa!) ed hanno una valenza "generale", nel senso che possono , o meglio, devono essere personalizzati dal programmatore secondo le sue esigenze. Uno dei vantaggi pi∙ interessanti del progetto risiede nella sua facilitα d'implementazione: si tratta di un'applet facilmente inseribile all'interno di qualunque documento HTML con l'uso di semplici tags. Per realizzare il progetto potete utilizzare un qualunque software di sviluppo in Java (Borland JBuilder, Sun JDK, Microsoft Visual J++, ecc.). A questo punto vi invito ad andare alla sezione sottostante per l'analisi e l'illustrazione del project. Il lettore ideale Φ il programmatore con medie conoscenze in ambito Java.1. RUBRICA.JAVA (LISTATO)
import java.applet.*;
import java.awt.*;
import java.util.*;
import dao350.*;
import com.ms.com.*;
import Alert;
class DBField
{
String strName;
String strType;
short vt;
Label label;
TextField field;
int attributes;
String strValidation;
public DBField(String strName, String strType, short vt)
{
this.strName = strName;
this.strType = strType;
this.vt = vt;
}
}
class DBFrame extends Frame
{
Rubrica applet;
public DBFrame(Rubrica appletRubrica, String strTitle)
{
super(strTitle);
this.applet = appletRubrica;
}
public synchronized boolean handleEvent(Event evt)
{
switch (evt.id)
{
case Event.WINDOW_DESTROY:
applet.exit();
return true;
default:
return super.handleEvent(evt);
}
}
}
public class Rubrica extends Applet
{
static protected Rubrica applet = null;
protected Frame frame;
protected boolean readOnly = false;
protected String strDatabase = "D:\\Articolo\\Rubrica.mdb";
protected String strRecordset = "Indirizzi";
protected int recordCount;
protected static Variant varEmpty;
protected _DBEngine m_IEngine;
protected Database database;
protected Recordset recordset;
static DBField[] field =
{
new DBField( "CAP", "Text", Variant.VariantString ),
new DBField( "Cittα", "Text", Variant.VariantString ),
new DBField( "Cognome", "Text", Variant.VariantString ),
new DBField( "Data di nascita", "Date", Variant.VariantDate ),
new DBField( "IDIndirizzo", "Long", Variant.VariantInt ),
new DBField( "Indirizzo", "Text", Variant.VariantString ),
new DBField( "IndirizzoPostaElettronica", "Text",
Variant.VariantString ),
new DBField( "InternoUfficio", "Text", Variant.VariantString ),
// new DBField( "InviaBiglietto", "Boolean", Variant.VariantBoolean ),
new DBField( "Nome", "Text", Variant.VariantString ),
new DBField( "NomeConiuge", "Text", Variant.VariantString ),
new DBField( "Note", "Memo", Variant.VariantString ),
new DBField( "NumeroFax", "Text", Variant.VariantString ),
new DBField( "Paese", "Text", Variant.VariantString ),
new DBField( "StatoOProvincia", "Text", Variant.VariantString ),
new DBField( "TelefonoAbitazione", "Text", Variant.VariantString ),
new DBField( "TelefonoUfficio", "Text", Variant.VariantString ),
};
static Hashtable hashField = new Hashtable();
static
{
for (int i = 0; i < field.length; i++)
{
hashField.put(field[i].strName, field[i]);
}
}
protected int columns = 1;
protected int textFieldWidth = 25;
Variant vName;
Variant vValue;
Variant vOriginalValue;
protected Panel db = new Panel();
protected Panel dbcolumn[] = new Panel[columns * 2];
protected Panel tools = new Panel();
protected Panel toolbar = new Panel();
protected Button buttonFirst = new Button("<< First");
protected Button buttonPrev = new Button("< Prev");
protected Button buttonNext = new Button("Next >");
protected Button buttonLast = new Button("Last >>");
protected Panel updatetools = new Panel();
protected Button buttonUpdate = new Button("Save!");
protected Button buttonAdd = new Button("Add!");
protected Button buttonDelete = new Button("Delete!");
protected Button buttonReread = new Button("Re-read");
protected Button buttonExit = new Button("Exit");
protected Panel feedback = new Panel();
protected Label labelDatabase = new Label("", Label.CENTER);
protected Label labelRecordset = new Label("", Label.CENTER);
protected Label labelPosition = new Label("", Label.CENTER);
protected Label labelStatus = new Label(" ");
protected void makeFrame()
{
frame = new DBFrame(this, "Rubrica");
}
public static void main(String[] args)
{
applet = new Rubrica();
applet.makeFrame();
applet.frame.add("Center", applet);
applet.init();
applet.start();
applet.frame.pack();
applet.frame.show();
applet.frame.setResizable(false);
}
public String getAppletInfo()
{
return "Name: Rubrica\n" +
"Author: Unknown\n" +
"Created with Microsoft Visual J++ Version 1.1";
}
public void init()
{
varEmpty = new Variant();
varEmpty.putEmpty();
ILicenseMgr mgr = new LicenseMgr();
m_IEngine = (_DBEngine)mgr.createWithLic
(
"mbmabptebkjcdlgtjmskjwtsdhjbmkmwtrak",
"{00000010-0000-0010-8000-00AA006D2EA4}",
null,
ComContext.INPROC_SERVER
);
Variant vExclusive = new Variant();
Variant vReadOnly = new Variant();
Variant vConnect = new Variant();
vExclusive.putBoolean(false);
vReadOnly.putBoolean(readOnly);
vConnect.putString("");
database = m_IEngine.OpenDatabase(strDatabase, vExclusive, vReadOnly, vConnect);
Variant vOpenType = new Variant();
Variant vOptions = new Variant();
Variant vLockType = new Variant();
vOpenType.putShort((short)RecordsetTypeEnum.dbOpenDynaset);
vOptions.putShort((short)0);
vLockType.putInt(readOnly ?
RecordsetOptionEnum.dbReadOnly : LockTypeEnum.dbOptimistic);
recordset = database.OpenRecordset(strRecordset, vOpenType, vOptions, vLockType);
if (recordset.getEOF())
recordCount = 0;
else
{
int nOptions = 0;
recordset.MoveLast(nOptions);
recordCount =
recordset.getRecordCount();
recordset.MoveFirst();
}
vName = new Variant();
vValue = new Variant();
vOriginalValue = new Variant();
columns *= 2;
db.setLayout(new GridLayout(0, columns));
for (int col = 0; col < columns; col++)
{
db.add(dbcolumn[col] = new Panel());
dbcolumn[col].setLayout(new GridLayout(0, 1));
}
int col = 0;
Fields fields = recordset.getFields();
for (int i = 0; i < field.length; i++)
{
DBField df = (DBField)hashField.get(field[i].strName);
vName.putString(field[i].strName);
_Field thisfield = fields.getItem(vName);
df.strValidation = thisfield.getValidationText();
df.attributes = thisfield.getAttributes();
String attribs = new String();
df.field = new TextField(textFieldWidth);
if (!readOnly && ((df.attributes & FieldAttributeEnum.dbUpdatableField) == 0))
{
attribs += " (ReadOnly)";
df.field.disable();
}
if ((df.attributes & FieldAttributeEnum.dbAutoIncrField) != 0)
{
attribs += " (Auto)";
df.field.disable();
}
df.label = new Label
(
df.strName + " (" + df.strType + ")" + attribs + " :",
Label.RIGHT
);
if (readOnly)
{
df.field.disable();
}
dbcolumn[col++].add(df.label);
dbcolumn[col++].add(df.field);
col %= columns;
}
toolbar.setLayout(new GridLayout(1, 0));
toolbar.add(buttonFirst);
toolbar.add(buttonPrev);
toolbar.add(buttonNext);
toolbar.add(buttonLast);
updatetools.setLayout(new GridLayout(1, 0));
updatetools.add(buttonUpdate);
updatetools.add(buttonAdd);
updatetools.add(buttonDelete);
updatetools.add(buttonReread);
tools.setLayout(new FlowLayout());
tools.add(toolbar);
if (!readOnly)
{
tools.add(updatetools);
}
if (frame != null)
tools.add(buttonExit);
feedback.setLayout(new GridLayout(0, 1));
feedback.add(labelDatabase);
feedback.add(labelRecordset);
feedback.add(labelPosition);
feedback.add(labelStatus);
GridBagLayout gb = new GridBagLayout();
setLayout(gb);
GridBagConstraints c = new GridBagConstraints();
c.insets = new Insets(20, 30, 20, 30);
c.gridx = 0;
add(feedback);
gb.setConstraints(feedback, c);
add(db);
gb.setConstraints(db, c);
add(tools);
gb.setConstraints(tools, c);
updateUI();
}
public void destroy()
{
}
public void start()
{
}
public synchronized void stop()
{
if (frame != null)
System.exit(0);
}
public synchronized boolean action(Event evt, Object o)
{
labelStatus.setText("");
if (evt.target == buttonFirst)
{
recordset.MoveFirst();
updateUI();
return true;
}
else if (evt.target == buttonPrev)
{
recordset.MovePrevious();
updateUI();
return true;
}
else if (evt.target == buttonNext)
{
recordset.MoveNext();
updateUI();
return true;
}
else if (evt.target == buttonLast)
{
int nOptions = 0;
recordset.MoveLast(nOptions);
updateUI();
return true;
}
else if (evt.target == buttonUpdate)
{
buttonUpdate.disable();
buttonAdd.disable();
buttonUpdate.disable();
updateDatabase(EditModeEnum.dbEditInProgress);
buttonUpdate.enable();
buttonAdd.enable();
buttonDelete.enable();
return true;
}
else if (evt.target == buttonAdd)
{
buttonUpdate.disable();
buttonAdd.disable();
buttonUpdate.disable();
updateDatabase(EditModeEnum.dbEditAdd);
buttonUpdate.enable();
buttonAdd.enable();
buttonDelete.enable();
return true;
}
else if (evt.target == buttonDelete)
{
buttonUpdate.disable();
buttonAdd.disable();
buttonUpdate.disable();
updateDatabase(EditModeEnum.dbEditNone);
buttonUpdate.enable();
buttonAdd.enable();
buttonDelete.enable();
return true;
}
else if (evt.target == buttonReread)
{
updateUI();
return true;
}
else if (evt.target == buttonExit)
{
exit();
}
return false;
}
protected void exit()
{
if (recordset != null)
{
recordset.Close();
recordset = null;
}
stop();
}
void updateTextField(DBField f)
{
if (recordset != null)
{
String strValue;
try
{
vName.putString(f.strName);
Variant vValue;
vValue = recordset.getCollect(vName);
if (f.vt == Variant.VariantBoolean)
{
Boolean b = new Boolean(vValue.getBoolean());
strValue = b.toString();
}
else
{
strValue = vValue.toString();
}
}
catch (ClassCastException c)
{
strValue = "";
}
f.field.setText(strValue);
}
}
void updateDatabaseField(DBField df) throws Exception
{
if (recordset != null)
{
try
{
String strValue = df.field.getText();
vValue.putString(strValue);
vName.putString(df.strName);
if (df.vt == Variant.VariantBoolean)
{
if (strValue.equals("true"))
{
vValue.putBoolean(true);
}
else
if (strValue.equals("false"))
{
vValue.putBoolean(false);
}
else
{
throw new Exception(df.strName + " must be either 'true' or 'false' in
lowercase.");
}
}
else
{
vValue.changeType(df.vt);
}
vOriginalValue = recordset.getCollect(vName);
if (!((vOriginalValue.getvt() == Variant.VariantNull) &&
(strValue.equals(""))))
{
recordset.putCollect(vName, vValue);
}
}
catch (ClassCastException c)
{
}
}
}
protected String getExceptionMessage(Exception e, DBField f)
{
if (e instanceof ComFailException)
{
int h = ((ComFailException)e).getHResult();
switch (h)
{
case 0x800a0c5b:
return "Field value is too long";
case 0x800a0cf4:
if ((f == null) || f.strValidation.equals(""))
{
return "Validation rule failed";
}
else
{
return f.strValidation;
}
default:
return "DAO COM Exception " + e.getMessage();
}
}
return e.getMessage();
}
protected void updateDatabase(int nMode)
{
try
{
if (nMode ==
EditModeEnum.dbEditNone)
{
recordset.Delete();
recordset.MoveNext();
if (recordset.getEOF())
recordset.MovePrevious();
recordCount--;
}
else
{
if (nMode == EditModeEnum.dbEditAdd)
recordset.AddNew();
else
recordset.Edit();
int i;
for (i = 0; i < field.length; i++)
{
if (((field[i].attributes &
FieldAttributeEnum.dbUpdatableField) != 0) &&
((field[i].attributes
& FieldAttributeEnum.dbAutoIncrField) == 0))
{
try
{
updateDatabaseField(field[i]);
}
catch (Exception e)
{
String strError;
strError = "Error updating " + field[i].strName + " : " +
getExceptionMessage(e, field[i]);
if (frame != null)
new Alert(frame, "Modification Failed!", strError);
else
labelStatus.setText(strError);
recordset.CancelUpdate(UpdateTypeEnum.dbUpdateRegular);
break;
}
}
}
if (i == field.length)
{
recordset.Update(UpdateTypeEnum.dbUpdateRegular, false);
if (nMode == EditModeEnum.dbEditAdd)
{
recordCount++;
if (recordset.getEOF())
recordset.MoveFirst();
}
}
}
}
catch (ComException eCom)
{
String strError = new
String();
Errors errs =
m_IEngine.getErrors();
Variant var = new
Variant();
for (int n = 0; n <
errs.getCount(); n++)
{
var.putInt(n);
dao350.Error err = errs.getItem(var);
if (nMode == EditModeEnum.dbEditAdd)
strError += "Error adding record: ";
else if (nMode == EditModeEnum.dbEditInProgress)
strError += "Error updating record: ";
else
strError += "Error deleting record: ";
strError += err.getDescription();
}
if (frame != null)
new Alert(frame, "Modification Failed!", strError);
else
labelStatus.setText(strError);
}
catch (Exception e)
{
String strError;
strError = "Error
updating record: " + getExceptionMessage(e, null);
if (frame != null)
new Alert(frame, "Modification Failed!", strError);
else
labelStatus.setText(strError);
}
updateUI();
}
protected void updateUI()
{
int pos = -1;
if (recordCount > 0)
{
for (int i = 0; i <
field.length; i++)
{
try
{
updateTextField(field[i]);
}
catch (Exception e)
{
String strError;
strError = "Error retrieving field value for " + field[i].strName + " :
" + e.getMessage();
if (frame != null)
new Alert(frame, "Data Fetch Failed!", strError);
else
labelStatus.setText(strError);
break;
}
}
pos =
recordset.getAbsolutePosition();
}
buttonFirst.enable(pos != 0 && recordCount > 0);
buttonPrev.enable(pos != 0 && recordCount > 0);
buttonNext.enable(pos != (recordCount - 1));
buttonLast.enable(pos != (recordCount - 1));
labelDatabase.setText("Database: " + strDatabase + (readOnly ? " (Read
Only)" : ""));
labelRecordset.setText("Table: " + strRecordset);
labelPosition.setText("Record: " + (pos + 1) + " of " + recordCount);
if (recordCount == 0)
labelStatus.setText("There are no records in the table!");
}
}
Grazie a questo source file possiamo realizzare un'applet con il supporto DAO, che ci permetterα l'importazione del database nel web. Nell'esempio Φ denominato Rubrica.
La prima cosa da fare Φ l'invocazione della DAO Object Library e delle Classi COM.
Successivamente si procede alla creazione di un classe (DBField) che definisce i campi del database (tipi di campo, label, ecc.). Si tratta di una parte molto semplice del progetto, come del resto la classe successiva che incontriamo nel listato, cioΦ la DBFrame, che non necessitα, a mio parere, di alcun commento, data la sua chiarezza.
La classe successiva Φ denominata Rubrica: notiamo subito le variabili che vengono utilizzate per il supporto DAO e la definizione dei campi (compresa la loro tipologia). Osservate come per evitare l'importazione di un campo specifico nella pagina HTML sia opportuno ricorrere alla seguente riga di codice: // new DBField( "InviaBiglietto", "Boolean", Variant.VariantBoolean ), dove, in questo caso, ci riferisce al booleano "Invia Biglietto". Di seguito abbiamo la parte di codice che si occupa della formattazione delle text box e dei pulsanti che ci appariranno nel Web: specificazione del massimo numero di caratteri da immettere in ogni campo, creazione delle toolbars per la gestione e l'utilizzo della base di dati importata, ecc.
A questo punto direi che Φ giunto il momento di invocare il database e di procedere alla sua apertura; per fare ci≥ utilizziamo una global empty variant, creiamo un nuovo set di record e "contiamo" i records. Disponiamo i singoli campi del database, sia da un punto di vista della formattazione che del layout.
blic void destroy() : qui dobbiamo inserire il "famoso" codice per il cleanup dell'applet.
blic void start() : qui dobbiamo inserire, invece, il codice per lo startup dell'applet.
Il nostro navigatore prima o poi sarα tentato a premere qualche bottone delle toolbars, aspettandosi qualche risultato! Quindi dobbiamo scrivere il codice che gestisce la barre dei buttons. Chiaramente utilizzeremo dei semplici cicli condizionali (if ... else ...) per ogni bottone.
Rimane soltanto la parte di codice necessaria per l'aggiornamento del database e della User Interface che si trova davanti a sΦ l'utente. Verificheremo se vi Φ stata qualche variazione all'interno del record set (usiamo i booleani), scriviamo una semplice routine per l'aggiornamento del DB prima e poi della UI: Φ opportuno che andiate ad analizzare attentamente il listato (di facile comprensione).
Credo che questa "parafrasi" del listato sia sufficiente per il programmatore medio: non bisogna mai sopprimere in lui lo spirito irrazionale della creativitα!
2. ALERT.JAVA (LISTATO)
import java.awt.*;
public class Alert extends Dialog
{
protected Button buttonOK;
public Alert(Frame parent, String title, String text)
{
super(parent, title, true);
GridBagLayout gb = new GridBagLayout();
setLayout(gb);
GridBagConstraints c = new GridBagConstraints();
c.gridx = 0;
c.insets = new Insets(10, 10, 10, 10);
Label l;
add(l = new Label(text));
gb.setConstraints(l, c);
add(buttonOK = new Button("OK"));
gb.setConstraints(buttonOK, c);
setResizable(false);
pack();
Rectangle rcParent = parent.bounds();
Dimension dAlert = size();
move
(
rcParent.x + (rcParent.width - dAlert.width) / 2,
rcParent.y + (rcParent.height - dAlert.height) / 2
);
show();
}
public boolean action(Event e, Object o)
{
if (e.target == buttonOK)
{
dispose();
return true;
}
return false;
}
}
Nel file Rubrica.java possiamo osservare la seguente riga di codice: import Alert; essa si riferisce all'importazione della classe Alert, di cui vi ho appena fornito il listato. Si tratta di una classe molto semplice costituita da un message box di avvertimento.
3. RUBRICA.HTML (LISTATO)
<html>
<head>
<title>"Rubrica</title>
</head>
<body>
<font size=5>"Indirizzi" table of "Rubrica"
database</font>
<hr>
<applet
code=Rubrica.class
id=Rubrica
width=600
height=648>
</applet>
<hr>
<a href="Rubrica.java">The source.</a>
</body>
</html>
Questa pagina HTML serve per visualizzare le nostre
applets. E' chiaro che dovete arricchirla di elementi grafici oppure inserire le applets
del project Rubrica nelle vostre pagine giα esistenti.
Bene, abbiamo visto quali sono gli elementi fondamentali di questo progettino, che ci permette con semplicitα di importare base dati sul Web. Ribadisco che con il presente articolo intendo formire soltanto le fondamenta per la realizzazione dell'applet. Ho optato per questa decisone, poichΦ ritengo che ogni programma debba essere adattato e personalizzato per le proprie esigenze.
Mi auguro di avervi fornito interessanti spunti inerenti la problematica delle base dati sul WWW. Per ogni chiarimento o commento: FABIO ROSSI rossi-f@fileita.it