Specifications of QDBM for Java

Copyright (C) 2000-2006 Mikio Hirabayashi
Last Update: Thu, 26 Oct 2006 15:00:20 +0900

Table of Contents

  1. Overview
  2. Installation
  3. Setting
  4. Examples
  5. Bugs

Overview

QDBM provides API for Java. This encapsulates the basic API, the extended API, and the advanced API of QDBM, and make them thread-safe. These APIs are implemented with the APIs for C called with Java Native Interface.

The basic API for Java realizes a hash database with a file. Constructors of the class `Depot' open a database file. The method `close' is used in order to close the database. Although the finalizer also try to close the database, do not rely on it. The method `put' is used in order to store a record. The method `out' is used in order to delete a record. The method `get' is used in order to retrieve a record. Besides, most operations like ones of the basic API for C are available. Each methods throws an instance of the class `DepotException' if an error occurs.

The extended API for Java realizes a hash database with a directory and multiple files. Constructors of the class `Curia' open a database directory. The method `close' is used in order to close the database. Although the finalizer also try to close the database, do not rely on it. The method `put' is used in order to store a record. The method `out' is used in order to delete a record. The method `get' is used in order to retrieve a record. Operations for managing large objects are also provided. Besides, most operations like ones of the extended API for C are available. Each methods throws an instance of the class `CuriaException' if an error occurs.

The advanced API for Java realizes a B+ tree database with a file. Constructors of the class `Villa' open a database file. The method `close' is used in order to close the database. Although the finalizer also try to close the database, do not rely on it. The method `put' is used in order to store a record. The method `out' is used in order to delete a record. The method `get' is used in order to retrieve a record. Besides, most operations like ones of the advanced API for C are available. Each methods throws an instance of the class `VillaException' if an error occurs.

`Depot', `Curia', and `Villa' implement the interface `ADBM' which is abstraction of database managers compatible with DBM of UNIX standard. Each methods throws an instance of the class `DBMException'. When you choose the one of four APIs, `Depot' is suggested if performance is weighted, `Curia' is suggested if scalability is weighted, `Villa' is suggested if ordering access is required, `ADBM' is suggested if elegance and maintenance are weighted. Besides, a database file is not compatible with each API.

Each class is packaged in `qdbm'. You can import the package in source files of application.

While APIs for C are thread-safe unless plural threads do not share a database handle, APIs for Java are thread-safe even if plural threads share a handle.

When `put' overwriting an existing record is cancelled or `get' retrieving a missing record, failure of the operation is noticed by exception. If you dislike such behavior, set the `silent' flag to be true. Then, failure of the operation is noticed by the return value.

For more information about the APIs, read documents in the sub directory `japidoc'.


Installation

Preparation

Make sure that JDK of 1.2 or later version is installed, the environment variable `JAVA_HOME' is set appropriately. And make sure that QDBM is installed under `/usr/local'.

Change the current working directory to the sub directory named `java'.

cd java

Usual Steps

Run the configuration script. If you use GCC for Java compilation, append the option `--with-gcj'.

./configure

Build programs.

make

Perform self-diagnostic test.

make check

Install programs. This operation must be carried out by the root user.

make install

When a series of work finishes, a Java archive `qdbm.jar' is installed under the `/usr/local/lib'. And, such native libraries as `libjqdbm.so' are installed under `/usr/local/lib'.

To uninstall them, execute the following command after `./configure'. This operation must be carried out by the root user.

make uninstall

For Windows

On Windows (Cygwin), you should follow the procedures below for installation.

Run the configuration script.

./configure

Build programs.

make win

Perform self-diagnostic test.

make check-win

Install programs. As well, perform `make uninstall-win' to uninstall them.

make install-win

On Windows, an import library `libjqdbm.dll.a' is created, and a dynamic linking library `jqdbm.dll' is created instead of such a shared libraries as `libjqdbm.so'. `jqdbm.dll' is installed into `/usr/local/bin'.

In order to build QDBM using MinGW on Cygwin, you should perform `make mingw' instead of `make win'. With the UNIX emulation layer of Cygwin, generated programs depend on `cygwin1.dll'. This problem is solved by linking them to the Win32 native DLL with MinGW.

For Mac OS X

On Mac OS X (Darwin), you should follow the procedures below for installation.

Run the configuration script.

./configure

Build programs.

make mac

Perform self-diagnostic test.

make check-mac

Install programs. As well, perform `make uninstall-mac' to uninstall them.

make install-mac

On Mac OS X, `libjqdbm.dylib', `libqdbm.jnilib' and so on are created instead of `libjqdbm.so' and so on.

For HP-UX

On HP-UX, you should follow the procedures below for installation.

Run the configuration script.

./configure

Build programs.

make hpux

Perform self-diagnostic test.

make check-hpux

Install programs. As well, perform `make uninstall-hpux' to uninstall them.

make install-hpux

On HP-UX, `libjqdbm.sl' is created instead of `libjqdbm.so' and so on.


Setting

To build and execute programs using QDBM, the following environment variable should be set.

Set the class path, which the environment variable `CLASSPATH' defines, to include the full path of `qdbm.jar'.

CLASSPATH=$CLASSPATH:/usr/local/lib/qdbm.jar
export CLASSPATH

Set the library path, which the environment variable `LD_LIBRARY_PATH' defines, to include `/usr/local/lib'. As well, this setting is not wanted on Windows, and Mac OS X uses the environment variable `DYLD_LIBRARY_PATH', and HP-UX uses the environment variable `SHLIB_PATH'.

LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
export LD_LIBRARY_PATH

Examples

The following example stores and retrieves a phone number, using the name as the key.

import qdbm.*;

public class Sample {

  static final String NAME = "mikio";
  static final String NUMBER = "000-1234-5678";
  static final String DBNAME = "book";

  public static void main(String[] args){
    Depot depot = null;
    try {

      // open the database
      depot = new Depot(DBNAME, Depot.OWRITER | Depot.OCREAT, -1);

      // store the record
      depot.put(NAME.getBytes(), NUMBER.getBytes());

      // retrieve the record
      byte[] res = depot.get(NAME.getBytes());
      System.out.println("Name: " + NAME);
      System.out.println("Number: " + new String(res));

    } catch(DepotException e){
      e.printStackTrace();
    } finally {

      // close the database
      if(depot != null){
        try {
          depot.close();
        } catch(DepotException e){
          e.printStackTrace();
        }
      }

    }
  }

}

The following example is a transcription of the one above, using the interface `ADBM'.

import qdbm.*;

public class Sample {

  static final String NAME = "mikio";
  static final String NUMBER = "000-1234-5678";
  static final String DBNAME = "book";

  public static void main(String[] args){
    ADBM dbm = null;
    try {

      // open the database
      dbm = new Depot(DBNAME, Depot.OWRITER | Depot.OCREAT, -1);

      // store the record
      dbm.store(NAME.getBytes(), NUMBER.getBytes(), true);

      // retrieve the record
      byte[] res = dbm.fetch(NAME.getBytes());
      System.out.println("Name: " + NAME);
      System.out.println("Number: " + new String(res));

    } catch(DBMException e){
      e.printStackTrace();
    } finally {

      // close the database
      if(dbm != null){
        try {
          dbm.close();
        } catch(DBMException e){
          e.printStackTrace();
        }
      }

    }
  }

}

The following example performs forward matching search for strings, using the class `Villa'.

import qdbm.*;

public class Sample {

  static final String DBNAME = "words";
  static final String PREFIX = "apple";

  public static void main(String[] args){
    Villa villa = null;
    try {

      // open the database
      villa = new Villa(DBNAME, Villa.OWRITER | Villa.OCREAT, Villa.CMPOBJ);

      // store records
      villa.putobj("applet", "little application", Villa.DDUP);
      villa.putobj("aurora", "polar wonderwork", Villa.DDUP);
      villa.putobj("apple", "delicious fruit", Villa.DDUP);
      villa.putobj("amigo", "good friend", Villa.DDUP);
      villa.putobj("apple", "big city", Villa.DDUP);

      try {

        // set the cursor at the top of candidates
        villa.curjumpobj(PREFIX, Villa.JFORWARD);

        // scan with the cursor
        for(;;){
          String key = (String)villa.curkeyobj();
          if(!key.startsWith(PREFIX)) break;
          String val = (String)villa.curvalobj();
          System.out.println(key + ": " + val);
          villa.curnext();
        }

      } catch(VillaException e){
        if(e.ecode != Villa.ENOITEM) throw e;
      }
    } catch(VillaException e){
      e.printStackTrace();
    } finally {

      // close the database
      if(villa != null){
        try {
          villa.close();
        } catch(VillaException e){
          e.printStackTrace();
        }
      }

    }
  }

}

For building a program using Java API of QDBM, set the environment variables and then perform `javac'. For example, the following command is executed to build `Sample.class' from `Sample.java'.

javac Sample.java

Bugs

QDBM has restrictions that two or more handles of the same database file should not be used by a process at the same time. So, when a database is used by two or more threads, open the database in the main thread and pass the handle to each thread.

Although methodology to store a serialized object is useful, object serialization is inefficient in time and space. So, if there is a method to get the byte array from an object, you should store the byte arrays. Besides, comparing keys in hash database is performed to serialized data even if they were objects. That is, if two objects are not according completely in serialized condition, even if the value of `equals' is true, they are not qualified as corresponding keys. This problem is not for B+ tree database, because the comparing function can be assigned appropriately.