Recipe 20.3 DBM Databases


Problem

You need to access a DBM file.

Solution

Use my code, or SleepyCat's code, to interface DBM from Java.

Discussion

Unix systems are commonly supplied with some form of DBM or DB[2] data file, often called a database. These are not relational databases but are key/value pairs, rather like a java.util.Hashtable that is automatically persisted to disk whenever you called its put( ) method. This format is also used on Windows by a few programs; for example, the Win32 version of Netscape keeps its history in a history.db or netscape.hst file, which is in this format. Not convinced?

[2] DBM is the original format; DB is a newer, more general format. DBM is actually now a frontend to DB, but because it's a bit simpler, I've used it for this example. GDBM is the FSF's implementation.

daroad.darwinsys.com$ pwd /c/program files/netscape/users/ian daroad.darwinsys.com$ file *.hst      netscape.hst: Berkeley DB Hash file (Version 2, Little Endian, Bucket Size 4096,  Bucket Shift 12, Directory Size 256, Segment Size 256, Segment Shift 8, Overflow  Point 8, Last Freed 36, Max Bucket 184, High Mask 0xff, Low Mask 0x7f, Fill Factor  54, Number of Keys 733) daroad.darwinsys.com$

The Unix file command[3] decodes file types; it's what Unix people rely on instead of (or in addition to) filename extensions.

[3] The version of file(1) in Linux and BSD systems was originally written by your humble scribe.

So the DBM format is a nice, general mapping from keys to values. But how can we use it in Java? There is no publicly defined mapping for Java, so I wrote my own. It uses a fair bit of native code, that is, C code called from Java that in turn calls the DBM library. I'll discuss native code in Recipe 26.5. For now it suffices to know that we can initialize a DBM file by calling the relevant constructor, passing the name of our DB file. We can iterate over all the key/value pairs by calling firstkey( ) once and then nextkey( ) repeatedly until it returns null. Both byte arrays and objects can be stored and retrieved; it is up to the programmer to know which is which (hint: use one or the other within a given DBM file). Objects are serialized using normal Java object serialization (see Recipe 10.18). Here is the API for the DBM class:

public DBM(String fileName) throws IOException; public Object nextkey(Object) throws IOException; public byte[] nextkey(byte[]) throws IOException; public Object firstkeyObject( ) throws IOException; public byte[] firstkey( ) throws IOException; public void store(Object,Object) throws IOException; public void store(byte[],byte[]) throws IOException; public Object fetch(Object) throws IOException; public byte[] fetch(byte[]) throws IOException; public void close( );

A simple program to print out the sites we have visited as listed in our Netscape history is shown in Example 20-4.

Example 20-4. ReadHistNS.java
import java.io.IOException; /** Demonstration of reading the MS-Windows Netscape History  * under UNIX using DBM.java.  */ public class ReadHistNS {     public static void main(String[] unused) throws IOException {         DBM d = new DBM("netscape.hst");         byte[] ba;         for (ba = d.firstkey( ); ba != null; ba = d.nextkey(ba)) {             System.out.println("Key=\"" + new String(ba) + '"');             byte[] val = d.fetch(ba);             for (int i=0; i<16&&i<val.length; i++) {                 System.out.print((short)val[i]);                 System.out.print(' ');             }         }     } }

The DBM format is an emulation of an older format, built on top of the DB library. Because of this, the filename must end in .pag, so I copied the history file to the name shown in the DBM constructor call.

A longer program, which includes both storing and retrieving in a DBM file, is the DBM version of the UserDB class, UserDBDBM . This is shown in Example 20-5.

Example 20-5. UserDBDBM.java
package jabadot; import java.io.*; import java.util.*; import java.sql.SQLException; /** A trivial "database" for User objects, stored in a flat file.  * <P>  * Since this is expected to be used heavily, and to avoid the overhead  * of re-reading the file, the "Singleton" Design Pattern is used  * to ensure that there is only ever one instance of this class.  */ public class UserDBDBM extends UserDB {     protected final static String DEF_NAME =          "/home/ian/src/jabadot/userdb";       // It appends .pag     protected DBM db;     /** Default Constructor */     protected UserDBDBM( ) throws IOException,SQLException {         this(DEF_NAME);     }     /** Constructor */     protected UserDBDBM(String fn) throws IOException,SQLException {         super( );                  db = new DBM(fn);         String k;         Object o;         // Iterate through contents of DBM, adding into list.         for (o=db.firstkeyObject( ); o!=null; o=db.nextkey(o)) {             // firstkey/nextkey give Key as Object, cast to String.             k = (String)o;             o = db.fetch(k);     // Get corresponding Value (a User)             users.add((User)o);     // Add to list.         }     }     /** Add one user to the list, both in-memory and on disk. */     public synchronized void addUser(User nu) throws IOException, SQLException {         // Add it to the in-memory list         super.addUser(nu);         // Add it to the on-disk version: store in DB with         // key = nickname, value = object.         db.store(nu.getName( ), nu);     } }

See Also

SleepyCat software (http://www.sleepycat.com) provides an improved version of Berkeley DBM and includes a Java driver for it. The Free Software Foundation provides GDBM, another DBM-like mechanism.



Java Cookbook
Java Cookbook, Second Edition
ISBN: 0596007019
EAN: 2147483647
Year: 2003
Pages: 409
Authors: Ian F Darwin

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net