/*************************************************************************************************
* Java binding of Kyoto Cabinet.
* Copyright (C) 2009-2011 FAL Labs
* This file is part of Kyoto Cabinet.
* This program is free software: you can redistribute it and/or modify it under the terms of
* the GNU General Public License as published by the Free Software Foundation, either version
* 3 of the License, or any later version.
* This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
* without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details.
* You should have received a copy of the GNU General Public License along with this program.
* If not, see <http://www.gnu.org/licenses/>.
*************************************************************************************************/
package kyotocabinet;
import java.util.*;
import java.io.*;
import java.net.*;
/**
* Interface of database abstraction.
*/
public class DB {
//----------------------------------------------------------------
// static initializer
//----------------------------------------------------------------
static {
Loader.load();
}
//----------------------------------------------------------------
// public constants
//----------------------------------------------------------------
/** generic mode: exceptional mode */
public static final int GEXCEPTIONAL = 1 << 0;
/** open mode: open as a reader */
public static final int OREADER = 1 << 0;
/** open mode: open as a writer */
public static final int OWRITER = 1 << 1;
/** open mode: writer creating */
public static final int OCREATE = 1 << 2;
/** open mode: writer truncating */
public static final int OTRUNCATE = 1 << 3;
/** open mode: auto transaction */
public static final int OAUTOTRAN = 1 << 4;
/** open mode: auto synchronization */
public static final int OAUTOSYNC = 1 << 5;
/** open mode: open without locking */
public static final int ONOLOCK = 1 << 6;
/** open mode: lock without blocking */
public static final int OTRYLOCK = 1 << 7;
/** open mode: open without auto repair */
public static final int ONOREPAIR = 1 << 8;
/** merge mode: overwrite the existing value */
public static final int MSET = 0;
/** merge mode: keep the existing value */
public static final int MADD = 1;
/** merge mode: modify the existing record only */
public static final int MREPLACE = 2;
/** merge mode: append the new value */
public static final int MAPPEND = 3;
//----------------------------------------------------------------
// constructors and finalizer
//----------------------------------------------------------------
/**
* Create an instance.
*/
public DB() {
initialize(0);
}
/**
* Create an instance with options.
* @param opts the optional features by bitwise-or: DB.GEXCEPTIONAL for the exceptional mode.
* @note The exceptional mode means that fatal errors caused by methods are reported by
* exceptions thrown.
*/
public DB(int opts) {
initialize(opts);
}
/**
* Release resources.
*/
protected void finalize() {
destruct();
}
//----------------------------------------------------------------
// public methods
//---------------------------------------------------------------
/**
* Get the last happened error.
* @return the last happened error.
*/
public native Error error();
/**
* Open a database file.
* @param path the path of a database file. If it is "-", the database will be a prototype
* hash database. If it is "+", the database will be a prototype tree database. If it is ":",
* the database will be a stash database. If it is "*", the database will be a cache hash
* database. If it is "%", the database will be a cache tree database. If its suffix is
* ".kch", the database will be a file hash database. If its suffix is ".kct", the database
* will be a file tree database. If its suffix is ".kcd", the database will be a directory
* hash database. If its suffix is ".kcf", the database will be a directory tree database.
* Otherwise, this method fails. Tuning parameters can trail the name, separated by "#".
* Each parameter is composed of the name and the value, separated by "=". If the "type"
* parameter is specified, the database type is determined by the value in "-", "+", ":", "*",
* "%", "kch", "kct", "kcd", and "kcf". All database types support the logging parameters of
* "log", "logkinds", and "logpx". The prototype hash database and the prototype tree
* database do not support any other tuning parameter. The stash database supports "bnum".
* The cache hash database supports "opts", "bnum", "zcomp", "capcnt", "capsiz", and "zkey".
* The cache tree database supports all parameters of the cache hash database except for
* capacity limitation, and supports "psiz", "rcomp", "pccap" in addition. The file hash
* database supports "apow", "fpow", "opts", "bnum", "msiz", "dfunit", "zcomp", and "zkey".
* The file tree database supports all parameters of the file hash database and "psiz",
* "rcomp", "pccap" in addition. The directory hash database supports "opts", "zcomp", and
* "zkey". The directory tree database supports all parameters of the directory hash database
* and "psiz", "rcomp", "pccap" in addition.
* @param mode the connection mode. DB.OWRITER as a writer, DB.OREADER as a
* reader. The following may be added to the writer mode by bitwise-or: DB.OCREATE,
* which means it creates a new database if the file does not exist, DB.OTRUNCATE, which
* means it creates a new database regardless if the file exists, DB.OAUTOTRAN, which
* means each updating operation is performed in implicit transaction, DB.OAUTOSYNC,
* which means each updating operation is followed by implicit synchronization with the file
* system. The following may be added to both of the reader mode and the writer mode by
* bitwise-or: DB.ONOLOCK, which means it opens the database file without file locking,
* DB.OTRYLOCK, which means locking is performed without blocking, DB.ONOREPAIR,
* which means the database file is not repaired implicitly even if file destruction is
* detected.
* @return true on success, or false on failure.
* @note The tuning parameter "log" is for the original "tune_logger" and the value specifies
* the path of the log file, or "-" for the standard output, or "+" for the standard error.
* "logkinds" specifies kinds of logged messages and the value can be "debug", "info", "warn",
* or "error". "logpx" specifies the prefix of each log message. "opts" is for "tune_options"
* and the value can contain "s" for the small option, "l" for the linear option, and "c" for
* the compress option. "bnum" corresponds to "tune_bucket". "zcomp" is for "tune_compressor"
* and the value can be "zlib" for the ZLIB raw compressor, "def" for the ZLIB deflate
* compressor, "gz" for the ZLIB gzip compressor, "lzo" for the LZO compressor, "lzma" for the
* LZMA compressor, or "arc" for the Arcfour cipher. "zkey" specifies the cipher key of the
* compressor. "capcnt" is for "cap_count". "capsiz" is for "cap_size". "psiz" is for
* "tune_page". "rcomp" is for "tune_comparator" and the value can be "lex" for the lexical
* comparator, "dec" for the decimal comparator, "lexdesc" for the lexical descending
* comparator, or "decdesc" for the decimal descending comparator. "pccap" is for
* "tune_page_cache". "apow" is for "tune_alignment". "fpow" is for "tune_fbp". "msiz" is
* for "tune_map". "dfunit" is for "tune_defrag". Every opened database must be closed by
* the DB.close method when it is no longer in use. It is not allowed for two or more
* database objects in the same process to keep their connections to the same database file at
* the same time.
*/
public native boolean open(String path, int mode);
/**
* Close the database file.
* @return true on success, or false on failure.
*/
public native boolean close();
/**
* Accept a visitor to a record.
* @param key the key.
* @param visitor a visitor object which implements the Visitor interface.
* @param writable true for writable operation, or false for read-only operation.
* @return true on success, or false on failure.
* @note The operation for each record is performed atomically and other threads accessing the
* same record are blocked. To avoid deadlock, any explicit database operation must not be
* performed in this method.
*/
public native boolean accept(byte[] key, Visitor visitor, boolean writable);
/**
* Accept a visitor to multiple records at once.
* @param keys specifies an array of the keys.
* @param visitor a visitor object.
* @param writable true for writable operation, or false for read-only operation.
* @return true on success, or false on failure.
* @note The operations for specified records are performed atomically and other threads
* accessing the same records are blocked. To avoid deadlock, any explicit database operation
* must not be performed in this method.
*/
public native boolean accept_bulk(byte[][] keys, Visitor visitor, boolean writable);
/**
* Iterate to accept a visitor for each record.
* @param visitor a visitor object which implements the Visitor interface.
* @param writable true for writable operation, or false for read-only operation.
* @return true on success, or false on failure.
* @note The whole iteration is performed atomically and other threads are blocked. To avoid
* deadlock, any explicit database operation must not be performed in this method.
*/
public native boolean iterate(Visitor visitor, boolean writable);
/**
* Set the value of a record.
* @param key the key.
* @param value the value.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the value is overwritten.
*/
public native boolean set(byte[] key, byte[] value);
/**
* Set the value of a record.
* @note Equal to the original DB.set method except that the parameters are String.
* @see #set(byte[], byte[])
*/
public boolean set(String key, String value) {
return set(str_to_ary(key), str_to_ary(value));
}
/**
* Add a record.
* @param key the key.
* @param value the value.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the record is not modified and false is returned.
*/
public native boolean add(byte[] key, byte[] value);
/**
* Add a record.
* @note Equal to the original DB.add method except that the parameters are String.
* @see #add(byte[], byte[])
*/
public boolean add(String key, String value) {
return add(str_to_ary(key), str_to_ary(value));
}
/**
* Replace the value of a record.
* @param key the key.
* @param value the value.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, no new record is created and false is returned.
* If the corresponding record exists, the value is modified.
*/
public native boolean replace(byte[] key, byte[] value);
/**
* Replace the value of a record.
* @note Equal to the original DB.replace method except that the parameters are String.
* @see #add(byte[], byte[])
*/
public boolean replace(String key, String value) {
return replace(str_to_ary(key), str_to_ary(value));
}
/**
* Append the value of a record.
* @param key the key.
* @param value the value.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, a new record is created. If the corresponding
* record exists, the given value is appended at the end of the existing value.
*/
public native boolean append(byte[] key, byte[] value);
/**
* Append the value of a record.
* @note Equal to the original DB.append method except that the parameters are String.
* @see #append(byte[], byte[])
*/
public boolean append(String key, String value) {
return append(str_to_ary(key), str_to_ary(value));
}
/**
* Add a number to the numeric integer value of a record.
* @param key the key.
* @param num the additional number.
* @param orig the origin number if no record corresponds to the key. If it is Long.MIN_VALUE
* and no record corresponds, this method fails. If it is Long.MAX_VALUE, the value is set as
* the additional number regardless of the current value.
* @return the result value, or Long.MIN_VALUE on failure.
* @note The value is serialized as an 8-byte binary integer in big-endian order, not a decimal
* string. If existing value is not 8-byte, this method fails.
*/
public native long increment(byte[] key, long num, long orig);
/**
* Add a number to the numeric integer value of a record.
* @note Equal to the original DB.increment method except that the parameter is String.
* @see #increment(byte[], long, long)
*/
public long increment(String key, long num, long orig) {
return increment(str_to_ary(key), num, orig);
}
/**
* Add a number to the numeric double value of a record.
* @param key the key.
* @param num the additional number.
* @param orig the origin number if no record corresponds to the key. If it is negative
* infinity and no record corresponds, this method fails. If it is positive infinity, the
* value is set as the additional number regardless of the current value.
* @return the result value, or Not-a-number on failure.
*/
public native double increment_double(byte[] key, double num, double orig);
/**
* Add a number to the numeric double value of a record.
* @note Equal to the original DB.increment method except that the parameter is String.
* @see #increment_double(byte[], double, double)
*/
public double increment_double(String key, double num, double orig) {
return increment_double(str_to_ary(key), num, orig);
}
/**
* Perform compare-and-swap.
* @param key the key.
* @param oval the old value. null means that no record corresponds.
* @param nval the new value. null means that the record is removed.
* @return true on success, or false on failure.
*/
public native boolean cas(byte[] key, byte[] oval, byte[] nval);
/**
* Perform compare-and-swap.
* @note Equal to the original DB.cas method except that the parameters are String.
* @see #cas(byte[], byte[], byte[])
*/
public boolean cas(String key, String oval, String nval) {
byte[] oary = oval != null ? str_to_ary(oval) : null;
byte[] nary = oval != null ? str_to_ary(nval) : null;
return cas(str_to_ary(key), oary, nary);
}
/**
* Remove a record.
* @param key the key.
* @return true on success, or false on failure.
* @note If no record corresponds to the key, false is returned.
*/
public native boolean remove(byte[] key);
/**
* @note Equal to the original DB.remove method except that the parameter is String.
* @see #remove(byte[])
*/
public boolean remove(String key) {
return remove(str_to_ary(key));
}
/**
* Retrieve the value of a record.
* @param key the key.
* @return the value of the corresponding record, or null on failure.
*/
public native byte[] get(byte[] key);
/**
* Retrieve the value of a record.
* @note Equal to the original DB.get method except that the parameter and the return value
* are String.
* @see #get(byte[])
*/
public String get(String key) {
return ary_to_str(get(str_to_ary(key)));
}
/**
* Retrieve the value of a record and remove it atomically.
* @param key the key.
* @return the value of the corresponding record, or null on failure.
*/
public native byte[] seize(byte[] key);
/**
* Retrieve the value of a record and remove it atomically.
* @note Equal to the original DB.seize method except that the parameter and the return value
* are String.
* @see #seize(byte[])
*/
public String seize(String key) {
return ary_to_str(seize(str_to_ary(key)));
}
/**
* Store records at once.
* @param recs the records to store. Each key and each value must be placed alternately.
* @param atomic true to perform all operations atomically, or false for non-atomic operations.
* @return the number of stored records, or -1 on failure.
*/
public native long set_bulk(byte[][] recs, boolean atomic);
/**
* Store records at once.
* @note Equal to the original DB.set_bulk method except that the parameter is Map.
* @see #set_bulk(byte[][], boolean)
*/
public long set_bulk(Map<String, String> recs, boolean atomic) {
byte[][] recary = new byte[recs.size()*2][];
int ridx = 0;
for (Map.Entry<String, String> rec : recs.entrySet()) {
recary[ridx++] = ((String)rec.getKey()).getBytes();
recary[ridx++] = ((String)rec.getValue()).getBytes();
}
return set_bulk(recary, atomic);
}
/**
* Remove records at once.
* @param keys the keys of the records to remove.
* @param atomic true to perform all operations atomically, or false for non-atomic operations.
* @return the number of removed records, or -1 on failure.
*/
public native long remove_bulk(byte[][] keys, boolean atomic);
/**
* Remove records at once.
* @note Equal to the original DB.remove_bulk method except that the parameter is List.
* @see #remove_bulk(byte[][], boolean)
*/
public long remove_bulk(List<String> keys, boolean atomic) {
byte[][] keyary = new byte[keys.size()][];
int kidx = 0;
for (String key : keys) {
keyary[kidx++] = key.getBytes();
}
return remove_bulk(keyary, atomic);
}
/**
* Retrieve records at once.
* @param keys the keys of the records to retrieve.
* @param atomic true to perform all operations atomically, or false for non-atomic operations.
* @return an array of retrieved records, or null on failure. Each key and each value is
* placed alternately.
*/
public native byte[][] get_bulk(byte[][] keys, boolean atomic);
/**
* Retrieve records at once.
* @note Equal to the original DB.get_bulk method except that the parameter is List and the
* return value is Map.
* @see #get_bulk(byte[][], boolean)
*/
public Map<String, String> get_bulk(List<String> keys, boolean atomic) {
byte[][] keyary = new byte[keys.size()][];
int kidx = 0;
for (String key : keys) {
keyary[kidx++] = key.getBytes();
}
byte[][] recary = get_bulk(keyary, atomic);
Map<String, String> recs = new HashMap<String, String>();
for (int i = 0; i + 1 < recary.length; i += 2) {
recs.put(new String(recary[i]), new String(recary[i+1]));
}
return recs;
}
/**
* Remove all records.
* @return true on success, or false on failure.
*/
public native boolean clear();
/**
* Synchronize updated contents with the file and the device.
* @param hard true for physical synchronization with the device, or false for logical
* synchronization with the file system.
* @param proc a postprocessor object which implements the FileProcessor interface. If it is
* null, no postprocessing is performed.
* @return true on success, or false on failure.
* @note The operation of the postprocessor is performed atomically and other threads accessing
* the same record are blocked. To avoid deadlock, any explicit database operation must not
* be performed in this method.
*/
public native boolean synchronize(boolean hard, FileProcessor proc);
/**
* Occupy database by locking and do something meanwhile.
* @param writable true to use writer lock, or false to use reader lock.
* @param proc a processor object which implements the FileProcessor interface. If it is null,
* no processing is performed.
* @return true on success, or false on failure.
* @note The operation of the processor is performed atomically and other threads accessing the
* same record are blocked. To avoid deadlock, any explicit database operation must not be
* performed in this method.
*/
public native boolean occupy(boolean writable, FileProcessor proc);
/**
* Create a copy of the database file.
* @param dest the path of the destination file.
* @return true on success, or false on failure.
*/
public native boolean copy(String dest);
/**
* Begin transaction.
* @param hard true for physical synchronization with the device, or false for logical
* synchronization with the file system.
* @return true on success, or false on failure.
*/
public native boolean begin_transaction(boolean hard);
/**
* End transaction.
* @param commit true to commit the transaction, or false to abort the transaction.
* @return true on success, or false on failure.
*/
public native boolean end_transaction(boolean commit);
/**
* Dump records into a snapshot file.
* @param dest the name of the destination file.
* @return true on success, or false on failure.
*/
public native boolean dump_snapshot(String dest);
/**
* Load records from a snapshot file.
* @param src the name of the source file.
* @return true on success, or false on failure.
*/
public native boolean load_snapshot(String src);
/**
* Get the number of records.
* @return the number of records, or -1 on failure.
*/
public native long count();
/**
* Get the size of the database file.
* @return the size of the database file in bytes, or -1 on failure.
*/
public native long size();
/**
* Get the path of the database file.
* @return the path of the database file, or null on failure.
*/
public native String path();
/**
* Get the miscellaneous status information.
* @return a map object of the status information, or null on failure.
*/
public native Map<String, String> status();
/**
* Get keys matching a prefix string.
* @param prefix the prefix string.
* @param max the maximum number to retrieve. If it is negative, no limit is specified.
* @return a list object of matching keys, or null on failure.
*/
public native List<String> match_prefix(String prefix, long max);
/**
* Get keys matching a regular expression string.
* @param regex the regular expression string.
* @param max the maximum number to retrieve. If it is negative, no limit is specified.
* @return a list object of matching keys, or null on failure.
*/
public native List<String> match_regex(String regex, long max);
/**
* Merge records from other databases.
* @param srcary an array of the source detabase objects.
* @param mode the merge mode. DB.MSET to overwrite the existing value, DB.MADD to keep the
* existing value, DB.MAPPEND to append the new value.
* @return true on success, or false on failure.
*/
public native boolean merge(DB[] srcary, int mode);
/**
* Create a cursor object.
* @return the return value is the created cursor object. Each cursor should be disabled
* with the Cursor#disable method when it is no longer in use.
*/
public native Cursor cursor();
/**
* Set the rule about throwing exception.
* @param codes an array of error codes. If each method occurs an error corresponding to one
* of the specified codes, the error is thrown as an exception.
* @return true on success, or false on failure.
*/
public boolean tune_exception_rule(int[] codes) {
int exbits = 0;
for (int i = 0; i < codes.length; i++) {
int code = codes[i];
if (code <= Error.MISC) exbits |= 1 << code;
}
exbits_ = exbits;
return true;
}
/**
* Set the encoding of external strings.
* @param encname the name of the encoding.
* @note The default encoding of external strings is UTF-8.
* @return true on success, or false on failure.
*/
public boolean tune_encoding(String encname) {
try {
Utility.VERSION.getBytes(encname);
} catch (UnsupportedEncodingException e) {
return false;
}
encname_ = encname;
return true;
}
/**
* Get the string expression.
* @return the string expression.
*/
public String toString() {
String tpath = path();
if (tpath == null) tpath = "(null)";
return tpath + ": " + count() + ": " + size();
}
//----------------------------------------------------------------
// package methods
//----------------------------------------------------------------
/**
* Get a UTF-8 byte array of a string.
* @param str the string.
* @return the UTF-8 byte array.
*/
byte[] str_to_ary(String str) {
if (str == null) return null;
try {
return str.getBytes(encname_);
} catch (UnsupportedEncodingException e) {
return str.getBytes();
}
}
/**
* Get a string from a UTF-8 byte array.
* @param ary the UTF-8 byte array.
* @return the string.
*/
String ary_to_str(byte[] ary) {
if (ary == null) return null;
try {
return new String(ary, encname_);
} catch (UnsupportedEncodingException e) {
return new String(ary);
}
}
//----------------------------------------------------------------
// private methods
//----------------------------------------------------------------
/**
* Initialize the object.
*/
private native void initialize(int opts);
/**
* Release resources.
*/
private native void destruct();
//----------------------------------------------------------------
// package fields
//----------------------------------------------------------------
/** The default encoding. */
String encname_ = "UTF-8";
//----------------------------------------------------------------
// private fields
//----------------------------------------------------------------
/** The pointer to the native object */
private long ptr_ = 0;
/** The bitfields for exceptional errors. */
private int exbits_ = 0;
}
// END OF FILE