ProblemYou need to access a DBM file. SolutionUse my code, or SleepyCat's code, to interface DBM from Java. DiscussionUnix 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?
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.
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.javaimport 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.javapackage 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 AlsoSleepyCat 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. |