source: gs2-extensions/tdb/trunk/src/java/org/greenstone/tdbjava/TDBJava.java@ 30193

Last change on this file since 30193 was 30193, checked in by jmt12, 9 years ago

Initial checkin of Java->JNI->TDB wrapper

File size: 12.7 KB
Line 
1/*
2 * Copyright (C) 2015 Greenstone Digital Libraries, University of Waikato
3 * $Id$
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18 */
19
20package org.greenstone.tdbjava;
21
22import java.io.Closeable;
23import java.lang.System;
24import java.util.Enumeration;
25
26import org.greenstone.tdbjava.TDBJavaException;
27import org.greenstone.tdbjava.TDBJavaKeyEnumeration;
28
29/** Java interface to a TDB database table.
30 *
31 * <P>This database is a simple on-disk hash table.
32 *
33 * <P>Both the hash keys and values are binary strings of any length. They
34 * are converted to and from Java objects using customizable packing
35 * strategies.
36 *
37 * <P>The implementation of this class consists of two levels: a Java-level
38 * interface, and a set of private native functions implemented in C. As much
39 * functionality as possible is implemented in Java: the C functions generally
40 * just marshal the information for presentation to the native GDBM library.
41 *
42 * <P>The native library <CODE>tdbjava</CODE> must be available for dynamic
43 * loading in a system-dependant manner.
44 *
45 * <p>See the <A HREF="https://tdb.samba.org">TDB home page</A> for more
46 * information.
47 *
48 * @author John Thompson ([email protected])
49 * @author Martin Pool (original author - GdbmJava)
50 */
51public class TDBJava
52 implements Closeable
53{
54 static
55 {
56 // Load native library mapping at runtime (libTDBJava.so)
57 System.loadLibrary("TDBJava");
58 }
59
60 /* ===== Declare mappings to native methods ===== */
61
62 /**
63 * @brief Determine the version number of the TDB library.
64 */
65 private static native String tdbGetVersion();
66
67 /**
68 * @brief Determine the version number of the JNI TDB warapper.
69 */
70 private static native String tdbWrapperVersion();
71
72 /**
73 * @brief Open the database and creating it if necessary.
74 */
75 private native long tdbOpen(String filename,
76 int tdb_flags,
77 int open_flags)
78 throws TDBJavaException;
79
80 /**
81 * @brief Close a database.
82 */
83 private native void tdbClose(long dbf);
84
85 /**
86 * @brief Store an element in the database.
87 */
88 private native void tdbStore(long dbf,
89 byte[] key,
90 byte[] content,
91 boolean replace);
92
93 /**
94 * @brief Fetch an entry in the database given a key.
95 */
96 private native byte[] tdbFetch(long dbf,
97 byte[] key);
98
99 /**
100 * @brief Check if an entry in the database exists.
101 */
102 private native boolean tdbExists(long dbf,
103 byte[] key);
104
105 /**
106 * @brief Delete an entry in the database given a key.
107 */
108 private native void tdbDelete(long dbf,
109 byte[] key);
110
111 /**
112 * @brief Find the first entry in the database and return its key.
113 */
114 private native byte[] tdbFirstKey(long dbf)
115 throws TDBJavaException;
116
117 /**
118 * @brief Find the next entry in the database, returning its key.
119 */
120 private native byte[] tdbNextKey(long dbf,
121 byte[] key)
122 throws TDBJavaException;
123
124 /* ===== Normal variable and function declarations ===== */
125 /* (should match GdbmFile) */
126
127 /* TDB flags */
128 /** When set, default behaviour (included for readability) */
129 public final static int TDB_DEFAULT = 0;
130 /** When set, causes any existing TDB table to be cleared on first open */
131 public final static int TDB_CLEAR_IF_FIRST = 1;
132 /** When set, causes TDB table to be in-memory (no file access) */
133 public final static int TDB_INTERNAL = 2;
134 /** When set, suppresses file locking (flock) */
135 public final static int TDB_NOLOCK = 4;
136
137 /** Open database possibilities **/
138 public final static int O_DEFAULT = 0;
139 public final static int O_READONLY = 1;
140
141 /** The parameters used to open the database. */
142 private String filename;
143 private int tdb_flags;
144 private int open_flags;
145
146 /** The TDB handle for the database file, or 0 if the database has
147 * been closed. Java doesn't understand it, but it stores it and
148 * passes it to the C routines. */
149 private transient volatile long dbf;
150
151 /** Create a new TDBJava object which automatically wraps a connection to
152 * an underlying TDB database (using the TDB library via the JNI wrapper).
153 *
154 * @param filename the disk filename in which the data will be stored
155 * @param flags any of TDB_DEFAULT, TDB_CLEAR_IF_FIRST, TDB_INTERNAL, and
156 * TDB_NOLOCK
157 *
158 */
159 public TDBJava(String filename)
160 throws TDBJavaException
161 {
162 this(filename, TDBJava.TDB_DEFAULT, TDBJava.O_DEFAULT);
163 }
164
165 public TDBJava(String filename, int tdb_flags)
166 throws TDBJavaException
167 {
168 this(filename, tdb_flags, TDBJava.O_DEFAULT);
169 }
170
171 public TDBJava(String filename, int tdb_flags, int open_flags)
172 throws TDBJavaException
173 {
174 this.filename = filename;
175 this.tdb_flags = tdb_flags;
176 this.open_flags = open_flags;
177 this.dbf = this.tdbOpen(this.filename, this.tdb_flags, this.open_flags);
178 // Replacement for System.runFinalizersOnExit(true);
179 Thread shutdown_thread = new TDBJavaShutdownThread(this);
180 Runtime.getRuntime().addShutdownHook(shutdown_thread);
181 }
182
183 /** Close the database file if it is still open.
184 */
185 public synchronized void close()
186 throws TDBJavaException
187 {
188 if (this.dbf != 0) {
189 this.tdbClose(this.dbf);
190 }
191 // opened or created
192 this.dbf = 0;
193 }
194
195 /** Close the database when the TDBJava is garbage-collected.
196 */
197 public void finalize()
198 throws TDBJavaException
199 {
200 this.close();
201 }
202
203 /** Returns a string indicating the version of the underlying TDB
204 * library.
205 *
206 * @return the version of the native TDB library.
207 *
208 **/
209 public static String getLibraryVersion()
210 {
211 return TDBJava.tdbGetVersion();
212 }
213
214 /** Return a string indicating the version of the JNI TDB library
215 * wrapper.
216 *
217 * @return the release number of the wrapper
218 *
219 **/
220 public static String getWrapperVersion()
221 {
222 return TDBJava.tdbWrapperVersion();
223 }
224
225 /** Indicate whether the database is writable.
226 *
227 * @return true if the database may be written; otherwise false.
228 *
229 */
230 public boolean isWritable()
231 {
232 return (TDBJava.O_READONLY != this.open_flags);
233 }
234
235 /** Indicate whether the database is open or not.
236 *
237 * @return false if the database has been closed; otherwise true.
238 *
239 */
240 public boolean isOpen()
241 {
242 return (this.dbf != 0);
243 }
244
245 /** Compact the database file - not necessary for TDB.
246 */
247 public void reorganize()
248 throws TDBJavaException
249 {
250 // Not applicable
251 }
252
253 /** Was used to define key Packing mechanism - no longer used as TDB always
254 * uses String to byte array.
255 */
256 public void setKeyPacking(Object packing)
257 {
258 // Not applicable
259 }
260
261 /** Was used to define value Packing mechanism - no longer used as TDB
262 * always uses byte array to String
263 */
264 public void setValuePacking(Object packing)
265 {
266 // Not applicable
267 }
268
269 /** Flush changes to disk - not necessary for TDB.
270 */
271 public void sync()
272 throws TDBJavaException
273 {
274 // Not applicable
275 }
276
277 /** Retrieve the value corresponding to a particular key.
278 *
279 * @param key the string identifying the record to be retrieved
280 *
281 * @return the value of the record with the specified key; or
282 * null if the key is not present in the database.
283 */
284 public String fetch(String key)
285 throws TDBJavaException
286 {
287 if (!this.isOpen()) {
288 throw new TDBJavaException("Database is not open: " + this.filename);
289 }
290 byte[] key_bytes = this.toBytes(key);
291 byte[] value_bytes = this.tdbFetch(this.dbf, key_bytes);
292 if (null == value_bytes) {
293 throw new TDBJavaException("Key not found: \"" + key + "\"");
294 }
295 String value = this.fromBytes(value_bytes);
296 return value;
297 }
298
299 /** Indicate whether the specified key is in the database,
300 * without returning the value.
301 *
302 * @param key the key of the record to be retrieved
303 *
304 * @return true if a record with the specified key is present;
305 * otherwise false.
306 */
307 public boolean exists(String key)
308 throws TDBJavaException
309 {
310 if (!this.isOpen()) {
311 throw new TDBJavaException("Database is not open: " + this.filename);
312 }
313 byte[] key_bytes = this.toBytes(key);
314 return this.tdbExists(this.dbf, key_bytes);
315 }
316
317 /** Store a value in the database, replacing any existing value with the
318 * same key.
319 *
320 * @param key key under which to store the value
321 *
322 * @param value value to be stored
323 *
324 * @exception TDBJavaException if an IO error occurs
325 */
326 public void store(String key, String value)
327 throws TDBJavaException
328 {
329 if (!this.isOpen()) {
330 throw new TDBJavaException("Database is not open: " + this.filename);
331 }
332 if (!this.isWritable()) {
333 throw new TDBJavaException("Database is read only: " + this.filename);
334 }
335 byte[] key_bytes = this.toBytes(key);
336 byte[] value_bytes = this.toBytes(value);
337 this.tdbStore(this.dbf, key_bytes, value_bytes, true);
338 }
339
340 /** Remove a record from the database.
341 *
342 * @param key key of the record to be removed
343 *
344 * @exception TDBJavaException if there is no record with the specified key; or
345 * if an IO error occurs.
346 */
347 public void delete(String key)
348 throws TDBJavaException
349 {
350 if (!this.isOpen()) {
351 throw new TDBJavaException("Database is not open: " + this.filename);
352 }
353 if (!this.isWritable()) {
354 throw new TDBJavaException("Database is read only: " + this.filename);
355 }
356 byte[] key_bytes = this.toBytes(key);
357 this.tdbDelete(this.dbf, key_bytes);
358 }
359
360 /** Return an enumeration which will return all of the keys for the
361 * database of the file in (apparently) random order.
362 *
363 * <P><B>Caution:</B> If the database is modified while
364 * an enumeration is in progress,
365 * then changes in the hash table may cause the enumeration to
366 * miss some records.
367 */
368 public Enumeration keys()
369 throws TDBJavaException
370 {
371 if (!this.isOpen()) {
372 throw new TDBJavaException("Database is not open: " + this.filename);
373 }
374 // Return an inner class which will do the iteration in the
375 // context of this GdbmFile object.
376 Enumeration keys = new TDBJavaKeyEnumeration(this);
377 return keys;
378 }
379
380 /** Start a visit to all keys in the hashtable.
381 *
382 * @return the first key in the hash table as a String; or null if
383 * the database is empty. */
384 String getFirstKey()
385 throws TDBJavaException
386 {
387 if (!this.isOpen()) {
388 throw new TDBJavaException("Database is not open: " + this.filename);
389 }
390 byte[] first_key_bytes = this.tdbFirstKey(this.dbf);
391 String first_key = null;
392 if (null != first_key_bytes) {
393 first_key = this.fromBytes(first_key_bytes);
394 }
395 return first_key;
396 }
397 /** getFirstKey() **/
398
399
400 /** Find and read the next key in the hashtable.
401 *
402 * @return the next key; or null if given the last key in the hashtable.
403 */
404 String getNextKey(String current_key)
405 throws TDBJavaException
406 {
407 if (!this.isOpen()) {
408 throw new TDBJavaException("Database is not open: " + this.filename);
409 }
410 byte[] current_key_bytes = this.toBytes(current_key);
411 byte[] next_key_bytes = this.tdbNextKey(this.dbf, current_key_bytes);
412 String next_key = null;
413 if (null != next_key_bytes) {
414 next_key = this.fromBytes(next_key_bytes);
415 }
416 return next_key;
417 }
418 /** getNextKey(String) **/
419
420
421 /* ===== Private and Protected Functions ===== */
422
423 /** Convert String to byte array - simplified replacement for Packing
424 * mechanism in GDBMJava.
425 */
426 protected byte[] toBytes(String input_string)
427 {
428 byte[] output_bytes;
429 try {
430 output_bytes = input_string.getBytes("UTF-8");
431 }
432 catch (Exception e) {
433 System.err.println("TDBJava::toBytes() - UTF-8 encoding not supported");
434 output_bytes = input_string.getBytes();
435 }
436 return output_bytes;
437 }
438
439 /** Convert bytes array to String - simplified replacement for Packing
440 * mechanism in GDBMJava.
441 */
442 protected String fromBytes(byte[] input_bytes)
443 {
444 String output_string;
445 try {
446 output_string = new String(input_bytes, "UTF-8");
447 }
448 catch (Exception e) {
449 System.err.println("TDBJava::fromBytes() - UTF-8 encoding not supported");
450 output_string = new String(input_bytes);
451 }
452 return output_string;
453 }
454}
Note: See TracBrowser for help on using the repository browser.