diff options
author | jbj <devnull@localhost> | 2004-10-16 01:31:54 +0000 |
---|---|---|
committer | jbj <devnull@localhost> | 2004-10-16 01:31:54 +0000 |
commit | d03f220fde879509cab2ac1c73b71b7efb52b737 (patch) | |
tree | 1e34bfadac0a6618d0e9a7933bad90063a785acf /db/java | |
parent | 2dc699bfe049b9319ea3719f604d25940ff52004 (diff) | |
download | librpm-tizen-d03f220fde879509cab2ac1c73b71b7efb52b737.tar.gz librpm-tizen-d03f220fde879509cab2ac1c73b71b7efb52b737.tar.bz2 librpm-tizen-d03f220fde879509cab2ac1c73b71b7efb52b737.zip |
... and in with the New ...
CVS patchset: 7471
CVS date: 2004/10/16 01:31:54
Diffstat (limited to 'db/java')
153 files changed, 22213 insertions, 3 deletions
diff --git a/db/java/src/com/sleepycat/bind/ByteArrayBinding.java b/db/java/src/com/sleepycat/bind/ByteArrayBinding.java new file mode 100644 index 000000000..e684c1fea --- /dev/null +++ b/db/java/src/com/sleepycat/bind/ByteArrayBinding.java @@ -0,0 +1,43 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ByteArrayBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A pass-through <code>EntryBinding</code> that uses the entry's byte array as + * the key or data object. + * + * @author Mark Hayes + */ +public class ByteArrayBinding implements EntryBinding { + + /** + * Creates a byte array binding. + */ + public ByteArrayBinding() { + } + + // javadoc is inherited + public Object entryToObject(DatabaseEntry entry) { + + byte[] bytes = new byte[entry.getSize()]; + System.arraycopy(entry.getData(), entry.getOffset(), + bytes, 0, bytes.length); + return bytes; + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + byte[] bytes = (byte[]) object; + entry.setData(bytes, 0, bytes.length); + } +} diff --git a/db/java/src/com/sleepycat/bind/EntityBinding.java b/db/java/src/com/sleepycat/bind/EntityBinding.java new file mode 100644 index 000000000..5209af56a --- /dev/null +++ b/db/java/src/com/sleepycat/bind/EntityBinding.java @@ -0,0 +1,49 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: EntityBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A binding between a key-value entry pair and an entity object. + * + * @author Mark Hayes + */ +public interface EntityBinding { + + /** + * Converts key and data entry buffers into an entity Object. + * + * @param key is the source key entry. + * + * @param data is the source data entry. + * + * @return the resulting Object. + */ + Object entryToObject(DatabaseEntry key, DatabaseEntry data); + + /** + * Extracts the key entry from an entity Object. + * + * @param object is the source Object. + * + * @param key is the destination entry buffer. + */ + void objectToKey(Object object, DatabaseEntry key); + + /** + * Extracts the data entry from an entity Object. + * + * @param object is the source Object. + * + * @param data is the destination entry buffer. + */ + void objectToData(Object object, DatabaseEntry data); +} diff --git a/db/java/src/com/sleepycat/bind/EntryBinding.java b/db/java/src/com/sleepycat/bind/EntryBinding.java new file mode 100644 index 000000000..e7ad56fa7 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/EntryBinding.java @@ -0,0 +1,38 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: EntryBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A binding between a key or data entry and a key or data object. + * + * @author Mark Hayes + */ +public interface EntryBinding { + + /** + * Converts a entry buffer into an Object. + * + * @param entry is the source entry buffer. + * + * @return the resulting Object. + */ + Object entryToObject(DatabaseEntry entry); + + /** + * Converts an Object into a entry buffer. + * + * @param object is the source Object. + * + * @param entry is the destination entry buffer. + */ + void objectToEntry(Object object, DatabaseEntry entry); +} diff --git a/db/java/src/com/sleepycat/bind/RecordNumberBinding.java b/db/java/src/com/sleepycat/bind/RecordNumberBinding.java new file mode 100644 index 000000000..7fe3dce2e --- /dev/null +++ b/db/java/src/com/sleepycat/bind/RecordNumberBinding.java @@ -0,0 +1,70 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: RecordNumberBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind; + +import com.sleepycat.compat.DbCompat; +import com.sleepycat.db.DatabaseEntry; + +/** + * An <code>EntryBinding</code> that treats a record number key entry as a + * <code>Long</code> key object. + * + * <p>Record numbers are returned as <code>Long</code> objects, although on + * input any <code>Number</code> object may be used.</p> + * + * @author Mark Hayes + */ +public class RecordNumberBinding implements EntryBinding { + + /** + * Creates a byte array binding. + */ + public RecordNumberBinding() { + } + + // javadoc is inherited + public Object entryToObject(DatabaseEntry entry) { + + return new Long(entryToRecordNumber(entry)); + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + recordNumberToEntry(((Number) object).longValue(), entry); + } + + /** + * Utility method for use by bindings to translate a entry buffer to an + * record number integer. + * + * @param entry the entry buffer. + * + * @return the record number. + */ + public static long entryToRecordNumber(DatabaseEntry entry) { + + return DbCompat.getRecordNumber(entry) & 0xFFFFFFFFL; + } + + /** + * Utility method for use by bindings to translate a record number integer + * to a entry buffer. + * + * @param recordNumber the record number. + * + * @param entry the entry buffer to hold the record number. + */ + public static void recordNumberToEntry(long recordNumber, + DatabaseEntry entry) { + entry.setData(new byte[4], 0, 4); + DbCompat.setRecordNumber(entry, (int) recordNumber); + } +} diff --git a/db/java/src/com/sleepycat/bind/package.html b/db/java/src/com/sleepycat/bind/package.html new file mode 100644 index 000000000..cf824682b --- /dev/null +++ b/db/java/src/com/sleepycat/bind/package.html @@ -0,0 +1,7 @@ +<!-- $Id: package.html,v 1.1 2004/08/02 18:52:04 mjc Exp $ --> +<html> +<body> +Bindings between database entries and Java objects<br> +<a href="{@docRoot}/%2e%2e/ref/bdb/cs_bdb_bind%2ehtml" target="_top">[reference guide]</a>. +</body> +</html> diff --git a/db/java/src/com/sleepycat/bind/serial/ClassCatalog.java b/db/java/src/com/sleepycat/bind/serial/ClassCatalog.java new file mode 100644 index 000000000..5d249b4f8 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/ClassCatalog.java @@ -0,0 +1,72 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ClassCatalog.java,v 1.3 2004/09/01 14:34:20 mark Exp $ + */ + +package com.sleepycat.bind.serial; + +import java.io.ObjectStreamClass; + +import com.sleepycat.db.DatabaseException; + +/** + * A catalog of class description information for use during object + * serialization. + * + * <p>A catalog is used to store class descriptions separately from serialized + * objects, to avoid redundantly stored information with each object. + * When serialized objects are stored in a database, a {@link + * StoredClassCatalog} should be used.</p> + * + * <p>This information is used for serialization of class descriptors or + * java.io.ObjectStreamClass objects, each of which represents a unique class + * format. For each unique format, a unique class ID is assigned by the + * catalog. The class ID can then be used in the serialization stream in place + * of the full class information. When used with {@link SerialInput} and + * {@link SerialOutput} or any of the serial bindings, the use of the catalog + * is transparent to the application.</p> + * + * @author Mark Hayes + */ +public interface ClassCatalog { + + /** + * Close a catalog database and release any cached resources. + */ + public void close() + throws DatabaseException; + + /** + * Return the class ID for the current version of the given class + * description. + * This is used for storing in serialization streams in place of a full + * class descriptor, since it is much more compact. To get back the + * ObjectStreamClass for a class ID, call {@link #getClassFormat(byte[])}. + * This function causes a new class ID to be assigned if the class + * description has changed. + * + * @param classDesc The class description for which to return the + * class ID. + * + * @return The class ID for the current version of the class. + */ + public byte[] getClassID(ObjectStreamClass classDesc) + throws DatabaseException, ClassNotFoundException; + + /** + * Return the ObjectStreamClass for the given class ID. This may or may + * not be the current class format, depending on whether the class has + * changed since the class ID was generated. + * + * @param classID The class ID for which to return the class format. + * + * @return The class format for the given class ID, which may or may not + * represent the current version of the class. + */ + public ObjectStreamClass getClassFormat(byte[] classID) + throws DatabaseException, ClassNotFoundException; +} diff --git a/db/java/src/com/sleepycat/bind/serial/SerialBinding.java b/db/java/src/com/sleepycat/bind/serial/SerialBinding.java new file mode 100644 index 000000000..e2e607c14 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/SerialBinding.java @@ -0,0 +1,130 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: SerialBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind.serial; + +import java.io.IOException; + +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.util.FastInputStream; +import com.sleepycat.util.FastOutputStream; +import com.sleepycat.util.RuntimeExceptionWrapper; + +/** + * A concrete <code>EntryBinding</code> that treats a key or data entry as + * a serialized object. + * + * <p>This binding stores objects in serialized object format. The + * deserialized objects are returned by the binding, and their + * <code>Class</code> must implement the <code>Serializable</code> + * interface.</p> + * + * @author Mark Hayes + */ +public class SerialBinding implements EntryBinding { + + private ClassCatalog classCatalog; + private Class baseClass; + + /** + * Creates a serial binding. + * + * @param classCatalog is the catalog to hold shared class information and + * for a database should be a {@link StoredClassCatalog}. + * + * @param baseClass is the base class for serialized objects stored using + * this binding -- all objects using this binding must be an instance of + * this class. + */ + public SerialBinding(ClassCatalog classCatalog, Class baseClass) { + + if (classCatalog == null) { + throw new NullPointerException("classCatalog must be non-null"); + } + this.classCatalog = classCatalog; + this.baseClass = baseClass; + } + + /** + * Returns the base class for this binding. + * + * @return the base class for this binding. + */ + public final Class getBaseClass() { + + return baseClass; + } + + /** + * Deserialize an object from an entry buffer. May only be called for data + * that was serialized using {@link #objectToEntry}, since the fixed + * serialization header is assumed to not be included in the input data. + * {@link SerialInput} is used to deserialize the object. + * + * @param entry is the input serialized entry. + * + * @return the output deserialized object. + */ + public Object entryToObject(DatabaseEntry entry) { + + int length = entry.getSize(); + byte[] hdr = SerialOutput.getStreamHeader(); + byte[] bufWithHeader = new byte[length + hdr.length]; + + System.arraycopy(hdr, 0, bufWithHeader, 0, hdr.length); + System.arraycopy(entry.getData(), entry.getOffset(), + bufWithHeader, hdr.length, length); + + try { + SerialInput jin = new SerialInput( + new FastInputStream(bufWithHeader, 0, bufWithHeader.length), + classCatalog); + return jin.readObject(); + } catch (IOException e) { + throw new RuntimeExceptionWrapper(e); + } catch (ClassNotFoundException e) { + throw new RuntimeExceptionWrapper(e); + } + } + + /** + * Serialize an object into an entry buffer. The fixed serialization + * header is not included in the output data to save space, and therefore + * to deserialize the data the complementary {@link #entryToObject} method + * must be used. {@link SerialOutput} is used to serialize the object. + * + * @param object is the input deserialized object. + * + * @param entry is the output serialized entry. + * + * @throws IllegalArgumentException if the object is not an instance of the + * base class for this binding. + */ + public void objectToEntry(Object object, DatabaseEntry entry) { + + if (baseClass != null && !baseClass.isInstance(object)) { + throw new IllegalArgumentException( + "Data object class (" + object.getClass() + + ") not an instance of binding's base class (" + + baseClass + ')'); + } + FastOutputStream fo = new FastOutputStream(); + try { + SerialOutput jos = new SerialOutput(fo, classCatalog); + jos.writeObject(object); + } catch (IOException e) { + throw new RuntimeExceptionWrapper(e); + } + + byte[] hdr = SerialOutput.getStreamHeader(); + entry.setData(fo.getBufferBytes(), hdr.length, + fo.getBufferLength() - hdr.length); + } +} diff --git a/db/java/src/com/sleepycat/bind/serial/SerialInput.java b/db/java/src/com/sleepycat/bind/serial/SerialInput.java new file mode 100644 index 000000000..16bfd859f --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/SerialInput.java @@ -0,0 +1,75 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: SerialInput.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind.serial; + +import java.io.IOException; +import java.io.InputStream; +import java.io.ObjectInputStream; +import java.io.ObjectStreamClass; + +import com.sleepycat.db.DatabaseException; +import com.sleepycat.util.RuntimeExceptionWrapper; + +/** + * A specialized <code>ObjectInputStream</code> that gets class description + * information from a <code>ClassCatalog</code>. It is used by + * <code>SerialBinding</code>. + * + * <p>This class is used instead of an {@link ObjectInputStream}, which it + * extends, to read an object stream written by the {@link SerialOutput} class. + * For reading objects from a database normally one of the serial binding + * classes is used. {@link SerialInput} is used when an {@link + * ObjectInputStream} is needed along with compact storage. A {@link + * ClassCatalog} must be supplied, however, to stored shared class + * descriptions.</p> + * + * @author Mark Hayes + */ +public class SerialInput extends ObjectInputStream { + + private ClassCatalog classCatalog; + + /** + * Creates a serial input stream. + * + * @param in is the input stream from which compact serialized objects will + * be read. + * + * @param classCatalog is the catalog containing the class descriptions + * for the serialized objects. + */ + public SerialInput(InputStream in, ClassCatalog classCatalog) + throws IOException { + + super(in); + + this.classCatalog = classCatalog; + } + + // javadoc is inherited + protected ObjectStreamClass readClassDescriptor() + throws IOException, ClassNotFoundException { + + try { + byte len = readByte(); + byte[] id = new byte[len]; + readFully(id); + + return classCatalog.getClassFormat(id); + } catch (DatabaseException e) { + /* + * Do not throw IOException from here since ObjectOutputStream + * will write the exception to the stream, which causes another + * call here, etc. + */ + throw new RuntimeExceptionWrapper(e); + } + } +} diff --git a/db/java/src/com/sleepycat/bind/serial/SerialOutput.java b/db/java/src/com/sleepycat/bind/serial/SerialOutput.java new file mode 100644 index 000000000..22ae8f872 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/SerialOutput.java @@ -0,0 +1,114 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: SerialOutput.java,v 1.3 2004/09/01 14:34:20 mark Exp $ + */ + +package com.sleepycat.bind.serial; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.ObjectStreamConstants; +import java.io.OutputStream; + +import com.sleepycat.db.DatabaseException; +import com.sleepycat.util.RuntimeExceptionWrapper; + +/** + * A specialized <code>ObjectOutputStream</code> that stores class description + * information in a <code>ClassCatalog</code>. It is used by + * <code>SerialBinding</code>. + * + * <p>This class is used instead of an {@link ObjectOutputStream}, which it + * extends, to write a compact object stream. For writing objects to a + * database normally one of the serial binding classes is used. {@link + * SerialOutput} is used when an {@link ObjectOutputStream} is needed along + * with compact storage. A {@link ClassCatalog} must be supplied, however, to + * stored shared class descriptions.</p> + * + * <p>The {@link ClassCatalog} is used to store class definitions rather than + * embedding these into the stream. Instead, a class format identifier is + * embedded into the stream. This identifier is then used by {@link + * SerialInput} to load the class format to deserialize the object.</p> + * + * @author Mark Hayes + */ +public class SerialOutput extends ObjectOutputStream { + + /* Serialization version constants. Instead of hardcoding these + * we get them by creating a SerialOutput, which itself + * guarantees that we'll always use a PROTOCOL_VERSION_2 header. + */ + private final static byte[] STREAM_HEADER; + static { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + try { + SerialOutput oos = new SerialOutput(baos, null); + } catch (IOException e) { + throw new RuntimeExceptionWrapper(e); + } + STREAM_HEADER = baos.toByteArray(); + } + + private ClassCatalog classCatalog; + + /** + * Creates a serial output stream. + * + * @param out is the output stream to which the compact serialized objects + * will be written. + * + * @param classCatalog is the catalog to which the class descriptions for + * the serialized objects will be written. + */ + public SerialOutput(OutputStream out, ClassCatalog classCatalog) + throws IOException { + + super(out); + this.classCatalog = classCatalog; + + /* guarantee that we'll always use the same serialization format */ + + useProtocolVersion(ObjectStreamConstants.PROTOCOL_VERSION_2); + } + + // javadoc is inherited + protected void writeClassDescriptor(ObjectStreamClass classdesc) + throws IOException { + + try { + byte[] id = classCatalog.getClassID(classdesc); + writeByte(id.length); + write(id); + } catch (DatabaseException e) { + /* + * Do not throw IOException from here since ObjectOutputStream + * will write the exception to the stream, which causes another + * call here, etc. + */ + throw new RuntimeExceptionWrapper(e); + } catch (ClassNotFoundException e) { + throw new RuntimeExceptionWrapper(e); + } + } + + /** + * Returns the fixed stream header used for all serialized streams in + * PROTOCOL_VERSION_2 format. To save space this header can be removed and + * serialized streams before storage and inserted before deserializing. + * {@link SerialOutput} always uses PROTOCOL_VERSION_2 serialization format + * to guarantee that this header is fixed. {@link SerialBinding} removes + * this header from serialized streams automatically. + * + * @return the fixed stream header. + */ + public static byte[] getStreamHeader() { + + return STREAM_HEADER; + } +} diff --git a/db/java/src/com/sleepycat/bind/serial/SerialSerialBinding.java b/db/java/src/com/sleepycat/bind/serial/SerialSerialBinding.java new file mode 100644 index 000000000..3fba700bd --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/SerialSerialBinding.java @@ -0,0 +1,117 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: SerialSerialBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind.serial; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.db.DatabaseEntry; + +/** + * An abstract <code>EntityBinding</code> that treats an entity's key entry and + * data entry as serialized objects. + * + * <p>This class takes care of serializing and deserializing the key and + * data entry automatically. Its three abstract methods must be implemented by + * a concrete subclass to convert the deserialized objects to/from an entity + * object.</p> + * <ul> + * <li> {@link #entryToObject(Object,Object)} </li> + * <li> {@link #objectToKey(Object)} </li> + * <li> {@link #objectToData(Object)} </li> + * </ul> + * + * @author Mark Hayes + */ +public abstract class SerialSerialBinding implements EntityBinding { + + private SerialBinding keyBinding; + private SerialBinding dataBinding; + + /** + * Creates a serial-serial entity binding. + * + * @param classCatalog is the catalog to hold shared class information and + * for a database should be a {@link StoredClassCatalog}. + * + * @param keyClass is the key base class. + * + * @param dataClass is the data base class. + */ + public SerialSerialBinding(ClassCatalog classCatalog, + Class keyClass, + Class dataClass) { + + this(new SerialBinding(classCatalog, keyClass), + new SerialBinding(classCatalog, dataClass)); + } + + /** + * Creates a serial-serial entity binding. + * + * @param keyBinding is the key binding. + * + * @param dataBinding is the data binding. + */ + public SerialSerialBinding(SerialBinding keyBinding, + SerialBinding dataBinding) { + + this.keyBinding = keyBinding; + this.dataBinding = dataBinding; + } + + // javadoc is inherited + public Object entryToObject(DatabaseEntry key, DatabaseEntry data) { + + return entryToObject(keyBinding.entryToObject(key), + dataBinding.entryToObject(data)); + } + + // javadoc is inherited + public void objectToKey(Object object, DatabaseEntry key) { + + object = objectToKey(object); + keyBinding.objectToEntry(object, key); + } + + // javadoc is inherited + public void objectToData(Object object, DatabaseEntry data) { + + object = objectToData(object); + dataBinding.objectToEntry(object, data); + } + + /** + * Constructs an entity object from deserialized key and data objects. + * + * @param keyInput is the deserialized key object. + * + * @param dataInput is the deserialized data object. + * + * @return the entity object constructed from the key and data. + */ + public abstract Object entryToObject(Object keyInput, Object dataInput); + + /** + * Extracts a key object from an entity object. + * + * @param object is the entity object. + * + * @return the deserialized key object. + */ + public abstract Object objectToKey(Object object); + + /** + * Extracts a data object from an entity object. + * + * @param object is the entity object. + * + * @return the deserialized data object. + */ + public abstract Object objectToData(Object object); +} diff --git a/db/java/src/com/sleepycat/bind/serial/SerialSerialKeyCreator.java b/db/java/src/com/sleepycat/bind/serial/SerialSerialKeyCreator.java new file mode 100644 index 000000000..eae756f61 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/SerialSerialKeyCreator.java @@ -0,0 +1,143 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: SerialSerialKeyCreator.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.serial; + +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.SecondaryDatabase; +import com.sleepycat.db.SecondaryKeyCreator; + +/** + * A abstract key creator that uses a serial key and a serial data entry. + * This class takes care of serializing and deserializing the key and data + * entry automatically. + * The following abstract method must be implemented by a concrete subclass + * to create the index key using these objects + * <ul> + * <li> {@link #createSecondaryKey(Object,Object)} </li> + * </ul> + * + * @author Mark Hayes + */ +public abstract class SerialSerialKeyCreator + implements SecondaryKeyCreator { + + protected SerialBinding primaryKeyBinding; + protected SerialBinding dataBinding; + protected SerialBinding indexKeyBinding; + + /** + * Creates a serial-serial key creator. + * + * @param classCatalog is the catalog to hold shared class information and + * for a database should be a {@link StoredClassCatalog}. + * + * @param primaryKeyClass is the primary key base class. + * + * @param dataClass is the data base class. + * + * @param indexKeyClass is the index key base class. + */ + public SerialSerialKeyCreator(ClassCatalog classCatalog, + Class primaryKeyClass, + Class dataClass, + Class indexKeyClass) { + + this(new SerialBinding(classCatalog, primaryKeyClass), + new SerialBinding(classCatalog, dataClass), + new SerialBinding(classCatalog, indexKeyClass)); + } + + /** + * Creates a serial-serial entity binding. + * + * @param primaryKeyBinding is the primary key binding. + * + * @param dataBinding is the data binding. + * + * @param indexKeyBinding is the index key binding. + */ + public SerialSerialKeyCreator(SerialBinding primaryKeyBinding, + SerialBinding dataBinding, + SerialBinding indexKeyBinding) { + + this.primaryKeyBinding = primaryKeyBinding; + this.dataBinding = dataBinding; + this.indexKeyBinding = indexKeyBinding; + } + + // javadoc is inherited + public boolean createSecondaryKey(SecondaryDatabase db, + DatabaseEntry primaryKeyEntry, + DatabaseEntry dataEntry, + DatabaseEntry indexKeyEntry) + throws DatabaseException { + + Object primaryKeyInput = + primaryKeyBinding.entryToObject(primaryKeyEntry); + Object dataInput = dataBinding.entryToObject(dataEntry); + Object indexKey = createSecondaryKey(primaryKeyInput, dataInput); + if (indexKey != null) { + indexKeyBinding.objectToEntry(indexKey, indexKeyEntry); + return true; + } else { + return false; + } + } + + // javadoc is inherited + public boolean nullifyForeignKey(SecondaryDatabase db, + DatabaseEntry dataEntry) + throws DatabaseException { + + Object data = dataBinding.entryToObject(dataEntry); + data = nullifyForeignKey(data); + if (data != null) { + dataBinding.objectToEntry(data, dataEntry); + return true; + } else { + return false; + } + } + + /** + * Creates the index key object from primary key and entry objects. + * + * @param primaryKey is the deserialized source primary key entry, or + * null if no primary key entry is used to construct the index key. + * + * @param data is the deserialized source data entry, or null if no + * data entry is used to construct the index key. + * + * @return the destination index key object, or null to indicate that + * the key is not present. + */ + public abstract Object createSecondaryKey(Object primaryKey, Object data); + + /** + * Clears the index key in a data object. + * + * <p>On entry the data parameter contains the index key to be cleared. It + * should be changed by this method such that {@link #createSecondaryKey} + * will return false. Other fields in the data object should remain + * unchanged.</p> + * + * @param data is the source and destination data object. + * + * @return the destination data object, or null to indicate that the + * key is not present and no change is necessary. The data returned may + * be the same object passed as the data parameter or a newly created + * object. + */ + public Object nullifyForeignKey(Object data) { + + return null; + } +} diff --git a/db/java/src/com/sleepycat/bind/serial/StoredClassCatalog.java b/db/java/src/com/sleepycat/bind/serial/StoredClassCatalog.java new file mode 100644 index 000000000..04caeae3b --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/StoredClassCatalog.java @@ -0,0 +1,446 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredClassCatalog.java,v 1.4 2004/09/01 14:34:20 mark Exp $ + */ + +package com.sleepycat.bind.serial; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.ObjectStreamClass; +import java.io.Serializable; +import java.math.BigInteger; +import java.util.HashMap; + +import com.sleepycat.compat.DbCompat; +import com.sleepycat.db.Cursor; +import com.sleepycat.db.CursorConfig; +import com.sleepycat.db.Database; +import com.sleepycat.db.DatabaseConfig; +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.EnvironmentConfig; +import com.sleepycat.db.LockMode; +import com.sleepycat.db.OperationStatus; +import com.sleepycat.db.Transaction; +import com.sleepycat.util.RuntimeExceptionWrapper; +import com.sleepycat.util.UtfOps; + +/** + * A <code>ClassCatalog</code> that is stored in a <code>Database</code>. + * + * <p>A single <code>StoredClassCatalog</code> object is normally used along + * with a set of databases that stored serialized objects.</p> + * + * @author Mark Hayes + */ +public class StoredClassCatalog implements ClassCatalog { + + /* + * Record types ([key] [data]): + * + * [0] [next class ID] + * [1 / class ID] [ObjectStreamClass (class format)] + * [2 / class name] [ClassInfo (has 8 byte class ID)] + */ + private static final byte REC_LAST_CLASS_ID = (byte) 0; + private static final byte REC_CLASS_FORMAT = (byte) 1; + private static final byte REC_CLASS_INFO = (byte) 2; + + private static final byte[] LAST_CLASS_ID_KEY = {REC_LAST_CLASS_ID}; + + private Database db; + private HashMap classMap; + private HashMap formatMap; + private LockMode writeLockMode; + private boolean cdbMode; + private boolean txnMode; + + /** + * Creates a catalog based on a given database. To save resources, only a + * single catalog object should be used for each unique catalog database. + * + * @param database an open database to use as the class catalog. It must + * be a BTREE database and must not allow duplicates. + * + * @throws DatabaseException if an error occurs accessing the database. + * + * @throws IllegalArgumentException if the database is not a BTREE database + * or if it configured to allow duplicates. + */ + public StoredClassCatalog(Database database) + throws DatabaseException, IllegalArgumentException { + + db = database; + DatabaseConfig dbConfig = db.getConfig(); + EnvironmentConfig envConfig = db.getEnvironment().getConfig(); + + writeLockMode = (DbCompat.getInitializeLocking(envConfig) || + envConfig.getTransactional()) ? LockMode.RMW + : LockMode.DEFAULT; + cdbMode = DbCompat.getInitializeCDB(envConfig); + txnMode = dbConfig.getTransactional(); + + if (!DbCompat.isTypeBtree(dbConfig)) { + throw new IllegalArgumentException( + "The class catalog must be a BTREE database."); + } + if (DbCompat.getSortedDuplicates(dbConfig) || + DbCompat.getUnsortedDuplicates(dbConfig)) { + throw new IllegalArgumentException( + "The class catalog database must not allow duplicates."); + } + + /* + * Create the class format and class info maps. Note that these are not + * synchronized, and therefore the methods that use them are + * synchronized. + */ + classMap = new HashMap(); + formatMap = new HashMap(); + + /* + * To avoid phantoms, use putNoOverwrite to ensure that there is always + * a class ID record. + */ + if (!dbConfig.getReadOnly()) { + DatabaseEntry key = new DatabaseEntry(LAST_CLASS_ID_KEY); + DatabaseEntry data = new DatabaseEntry(new byte[1]); // zero ID + db.putNoOverwrite(null, key, data); + } + } + + // javadoc is inherited + public synchronized void close() + throws DatabaseException { + + if (db != null) { + db.close(); + } + db = null; + formatMap = null; + classMap = null; + } + + // javadoc is inherited + public synchronized byte[] getClassID(ObjectStreamClass classFormat) + throws DatabaseException, ClassNotFoundException { + + ClassInfo classInfo = getClassInfo(classFormat); + return classInfo.getClassID(); + } + + // javadoc is inherited + public synchronized ObjectStreamClass getClassFormat(byte[] classID) + throws DatabaseException, ClassNotFoundException { + + return getClassFormat(classID, new DatabaseEntry()); + } + + /** + * Internal function for getting the class format. Allows passing the + * DatabaseEntry object for the data, so the bytes of the class format can + * be examined afterwards. + */ + private synchronized ObjectStreamClass getClassFormat(byte[] classID, + DatabaseEntry data) + throws DatabaseException, ClassNotFoundException { + + /* First check the map and, if found, add class info to the map. */ + + BigInteger classIDObj = new BigInteger(classID); + ObjectStreamClass classFormat = + (ObjectStreamClass) formatMap.get(classIDObj); + if (classFormat == null) { + + /* Make the class format key. */ + + byte[] keyBytes = new byte[classID.length + 1]; + keyBytes[0] = REC_CLASS_FORMAT; + System.arraycopy(classID, 0, keyBytes, 1, classID.length); + DatabaseEntry key = new DatabaseEntry(keyBytes); + + /* Read the class format. */ + + OperationStatus status = db.get(null, key, data, LockMode.DEFAULT); + if (status != OperationStatus.SUCCESS) { + throw new ClassNotFoundException("Catalog class ID not found"); + } + try { + ObjectInputStream ois = + new ObjectInputStream( + new ByteArrayInputStream(data.getData(), + data.getOffset(), + data.getSize())); + classFormat = (ObjectStreamClass) ois.readObject(); + } catch (IOException e) { + throw new RuntimeExceptionWrapper(e); + } + + /* Update the class format map. */ + + formatMap.put(classIDObj, classFormat); + } + return classFormat; + } + + /** + * Get the ClassInfo for a given class name, adding it and its + * ObjectStreamClass to the database if they are not already present, and + * caching both of them using the class info and class format maps. When a + * class is first loaded from the database, the stored ObjectStreamClass is + * compared to the current ObjectStreamClass loaded by the Java class + * loader; if they are different, a new class ID is assigned for the + * current format. + */ + private synchronized ClassInfo getClassInfo(ObjectStreamClass classFormat) + throws DatabaseException, ClassNotFoundException { + + /* + * First check for a cached copy of the class info, which if + * present always contains the class format object + */ + String className = classFormat.getName(); + ClassInfo classInfo = (ClassInfo) classMap.get(className); + if (classInfo != null) { + return classInfo; + } else { + /* Make class info key. */ + char[] nameChars = className.toCharArray(); + byte[] keyBytes = new byte[1 + UtfOps.getByteLength(nameChars)]; + keyBytes[0] = REC_CLASS_INFO; + UtfOps.charsToBytes(nameChars, 0, keyBytes, 1, nameChars.length); + DatabaseEntry key = new DatabaseEntry(keyBytes); + + /* Read class info. */ + DatabaseEntry data = new DatabaseEntry(); + OperationStatus status = db.get(null, key, data, LockMode.DEFAULT); + if (status != OperationStatus.SUCCESS) { + /* + * Not found in the database, write class info and class + * format. + */ + classInfo = putClassInfo(new ClassInfo(), className, key, + classFormat); + } else { + /* + * Read class info to get the class format key, then read class + * format. + */ + classInfo = new ClassInfo(data); + DatabaseEntry formatData = new DatabaseEntry(); + ObjectStreamClass storedClassFormat = + getClassFormat(classInfo.getClassID(), formatData); + + /* + * Compare the stored class format to the current class format, + * and if they are different then generate a new class ID. + */ + if (!areClassFormatsEqual(storedClassFormat, + getBytes(formatData), + classFormat)) { + classInfo = putClassInfo(classInfo, className, key, + classFormat); + } + + /* Update the class info map. */ + classInfo.setClassFormat(classFormat); + classMap.put(className, classInfo); + } + } + return classInfo; + } + + /** + * Assign a new class ID (increment the current ID record), write the + * ObjectStreamClass record for this new ID, and update the ClassInfo + * record with the new ID also. The ClassInfo passed as an argument is the + * one to be updated. + */ + private synchronized ClassInfo putClassInfo(ClassInfo classInfo, + String className, + DatabaseEntry classKey, + ObjectStreamClass classFormat) + throws DatabaseException, ClassNotFoundException { + + /* An intent-to-write cursor is needed for CDB. */ + CursorConfig cursorConfig = null; + if (cdbMode) { + cursorConfig = new CursorConfig(); + DbCompat.setWriteCursor(cursorConfig, true); + } + Cursor cursor = null; + Transaction txn = null; + try { + if (txnMode) { + txn = db.getEnvironment().beginTransaction(null, null); + } + cursor = db.openCursor(txn, cursorConfig); + + /* Get the current class ID. */ + DatabaseEntry key = new DatabaseEntry(LAST_CLASS_ID_KEY); + DatabaseEntry data = new DatabaseEntry(); + OperationStatus status = cursor.getSearchKey(key, data, + writeLockMode); + if (status != OperationStatus.SUCCESS) { + throw new IllegalStateException("Class ID not initialized"); + } + byte[] idBytes = getBytes(data); + + /* Increment the ID by one and write the updated record. */ + idBytes = incrementID(idBytes); + data.setData(idBytes); + cursor.put(key, data); + + /* + * Write the new class format record whose key is the ID just + * assigned. + */ + byte[] keyBytes = new byte[1 + idBytes.length]; + keyBytes[0] = REC_CLASS_FORMAT; + System.arraycopy(idBytes, 0, keyBytes, 1, idBytes.length); + key.setData(keyBytes); + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos; + try { + oos = new ObjectOutputStream(baos); + oos.writeObject(classFormat); + } catch (IOException e) { + throw new RuntimeExceptionWrapper(e); + } + data.setData(baos.toByteArray()); + + cursor.put(key, data); + + /* + * Write the new class info record, using the key passed in; this + * is done last so that a reader who gets the class info record + * first will always find the corresponding class format record. + */ + classInfo.setClassID(idBytes); + classInfo.toDbt(data); + + cursor.put(classKey, data); + + /* + * Update the maps before closing the cursor, so that the cursor + * lock prevents other writers from duplicating this entry. + */ + classInfo.setClassFormat(classFormat); + classMap.put(className, classInfo); + formatMap.put(new BigInteger(idBytes), classFormat); + return classInfo; + } finally { + if (cursor != null) { + cursor.close(); + } + if (txn != null) { + txn.commit(); + } + } + } + + private static byte[] incrementID(byte[] key) { + + BigInteger id = new BigInteger(key); + id = id.add(BigInteger.valueOf(1)); + return id.toByteArray(); + } + + /** + * Holds the class format key for a class, maintains a reference to the + * ObjectStreamClass. Other fields can be added when we need to store more + * information per class. + */ + private static class ClassInfo implements Serializable { + + private byte[] classID; + private transient ObjectStreamClass classFormat; + + ClassInfo() { + } + + ClassInfo(DatabaseEntry dbt) { + + byte[] data = dbt.getData(); + int len = data[0]; + classID = new byte[len]; + System.arraycopy(data, 1, classID, 0, len); + } + + void toDbt(DatabaseEntry dbt) { + + byte[] data = new byte[1 + classID.length]; + data[0] = (byte) classID.length; + System.arraycopy(classID, 0, data, 1, classID.length); + dbt.setData(data); + } + + void setClassID(byte[] classID) { + + this.classID = classID; + } + + byte[] getClassID() { + + return classID; + } + + ObjectStreamClass getClassFormat() { + + return classFormat; + } + + void setClassFormat(ObjectStreamClass classFormat) { + + this.classFormat = classFormat; + } + } + + /** + * Return whether two class formats are equal. This determines whether a + * new class format is needed for an object being serialized. Formats must + * be identical in all respects, or a new format is needed. + */ + private static boolean areClassFormatsEqual(ObjectStreamClass format1, + byte[] format1Bytes, + ObjectStreamClass format2) { + try { + if (format1Bytes == null) { // using cached format1 object + format1Bytes = getObjectBytes(format1); + } + byte[] format2Bytes = getObjectBytes(format2); + return java.util.Arrays.equals(format2Bytes, format1Bytes); + } catch (IOException e) { return false; } + } + + private static byte[] getBytes(DatabaseEntry dbt) { + byte[] b = dbt.getData(); + if (b == null) { + return null; + } + if (dbt.getOffset() == 0 && b.length == dbt.getSize()) { + return b; + } + byte[] t = new byte[dbt.getSize()]; + System.arraycopy(b, dbt.getOffset(), t, 0, t.length); + return t; + } + + private static byte[] getObjectBytes(Object o) + throws IOException { + + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + ObjectOutputStream oos = new ObjectOutputStream(baos); + oos.writeObject(o); + return baos.toByteArray(); + } +} diff --git a/db/java/src/com/sleepycat/bind/serial/TupleSerialBinding.java b/db/java/src/com/sleepycat/bind/serial/TupleSerialBinding.java new file mode 100644 index 000000000..2e30c3f5f --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/TupleSerialBinding.java @@ -0,0 +1,115 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleSerialBinding.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind.serial; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.bind.tuple.TupleBinding; +import com.sleepycat.bind.tuple.TupleInput; +import com.sleepycat.bind.tuple.TupleOutput; +import com.sleepycat.db.DatabaseEntry; + +/** + * An abstract <code>EntityBinding</code> that treats an entity's key entry as + * a tuple and its data entry as a serialized object. + * + * <p>This class takes care of serializing and deserializing the data entry, + * and converting the key entry to/from {@link TupleInput} and {@link + * TupleOutput} objects. Its three abstract methods must be implemented by a + * concrete subclass to convert these objects to/from an entity object.</p> + * <ul> + * <li> {@link #entryToObject(TupleInput,Object)} </li> + * <li> {@link #objectToKey(Object,TupleOutput)} </li> + * <li> {@link #objectToData(Object)} </li> + * </ul> + * + * @author Mark Hayes + */ +public abstract class TupleSerialBinding implements EntityBinding { + + protected SerialBinding dataBinding; + + /** + * Creates a tuple-serial entity binding. + * + * @param classCatalog is the catalog to hold shared class information and + * for a database should be a {@link StoredClassCatalog}. + * + * @param baseClass is the base class. + */ + public TupleSerialBinding(ClassCatalog classCatalog, + Class baseClass) { + + this(new SerialBinding(classCatalog, baseClass)); + } + + /** + * Creates a tuple-serial entity binding. + * + * @param dataBinding is the data binding. + */ + public TupleSerialBinding(SerialBinding dataBinding) { + + this.dataBinding = dataBinding; + } + + // javadoc is inherited + public Object entryToObject(DatabaseEntry key, DatabaseEntry data) { + + return entryToObject(TupleBinding.entryToInput(key), + dataBinding.entryToObject(data)); + } + + // javadoc is inherited + public void objectToKey(Object object, DatabaseEntry key) { + + TupleOutput output = TupleBinding.newOutput(); + objectToKey(object, output); + TupleBinding.outputToEntry(output, key); + } + + // javadoc is inherited + public void objectToData(Object object, DatabaseEntry data) { + + object = objectToData(object); + dataBinding.objectToEntry(object, data); + } + + /** + * Constructs an entity object from {@link TupleInput} key entry and + * deserialized data entry objects. + * + * @param keyInput is the {@link TupleInput} key entry object. + * + * @param dataInput is the deserialized data entry object. + * + * @return the entity object constructed from the key and data. + */ + public abstract Object entryToObject(TupleInput keyInput, + Object dataInput); + + /** + * Extracts a key tuple from an entity object. + * + * @param object is the entity object. + * + * @param keyOutput is the {@link TupleOutput} to which the key should be + * written. + */ + public abstract void objectToKey(Object object, TupleOutput keyOutput); + + /** + * Extracts a data object from an entity object. + * + * @param object is the entity object. + * + * @return the deserialized data object. + */ + public abstract Object objectToData(Object object); +} diff --git a/db/java/src/com/sleepycat/bind/serial/TupleSerialKeyCreator.java b/db/java/src/com/sleepycat/bind/serial/TupleSerialKeyCreator.java new file mode 100644 index 000000000..f81f75a9a --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/TupleSerialKeyCreator.java @@ -0,0 +1,137 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleSerialKeyCreator.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.serial; + +import com.sleepycat.bind.tuple.TupleBinding; +import com.sleepycat.bind.tuple.TupleInput; +import com.sleepycat.bind.tuple.TupleOutput; +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.SecondaryDatabase; +import com.sleepycat.db.SecondaryKeyCreator; + +/** + * A abstract key creator that uses a tuple key and a serial data entry. This + * class takes care of serializing and deserializing the data entry, and + * converting the key entry to/from {@link TupleInput} and {@link TupleOutput} + * objects. + * The following abstract method must be implemented by a concrete subclass + * to create the index key using these objects + * <ul> + * <li> {@link #createSecondaryKey(TupleInput,Object,TupleOutput)} </li> + * </ul> + * + * @author Mark Hayes + */ +public abstract class TupleSerialKeyCreator + implements SecondaryKeyCreator { + + protected SerialBinding dataBinding; + + /** + * Creates a tuple-serial key creator. + * + * @param classCatalog is the catalog to hold shared class information and + * for a database should be a {@link StoredClassCatalog}. + * + * @param dataClass is the data base class. + */ + public TupleSerialKeyCreator(ClassCatalog classCatalog, Class dataClass) { + + this(new SerialBinding(classCatalog, dataClass)); + } + + /** + * Creates a tuple-serial key creator. + * + * @param dataBinding is the data binding. + */ + public TupleSerialKeyCreator(SerialBinding dataBinding) { + + this.dataBinding = dataBinding; + } + + // javadoc is inherited + public boolean createSecondaryKey(SecondaryDatabase db, + DatabaseEntry primaryKeyEntry, + DatabaseEntry dataEntry, + DatabaseEntry indexKeyEntry) + throws DatabaseException { + + TupleOutput output = TupleBinding.newOutput(); + TupleInput primaryKeyInput = + TupleBinding.entryToInput(primaryKeyEntry); + Object dataInput = dataBinding.entryToObject(dataEntry); + if (createSecondaryKey(primaryKeyInput, dataInput, output)) { + TupleBinding.outputToEntry(output, indexKeyEntry); + return true; + } else { + return false; + } + } + + // javadoc is inherited + public boolean nullifyForeignKey(SecondaryDatabase db, + DatabaseEntry dataEntry) + throws DatabaseException { + + Object data = dataBinding.entryToObject(dataEntry); + data = nullifyForeignKey(data); + if (data != null) { + dataBinding.objectToEntry(data, dataEntry); + return true; + } else { + return false; + } + } + + /** + * Creates the index key entry from primary key tuple entry and + * deserialized data entry. + * + * @param primaryKeyInput is the {@link TupleInput} for the primary key + * entry, or null if no primary key entry is used to construct the index + * key. + * + * @param dataInput is the deserialized data entry, or null if no data + * entry is used to construct the index key. + * + * @param indexKeyOutput is the destination index key tuple. For index + * keys which are optionally present, no tuple entry should be output to + * indicate that the key is not present or null. + * + * @return true if a key was created, or false to indicate that the key is + * not present. + */ + public abstract boolean createSecondaryKey(TupleInput primaryKeyInput, + Object dataInput, + TupleOutput indexKeyOutput); + + /** + * Clears the index key in the deserialized data entry. + * + * <p>On entry the data parameter contains the index key to be cleared. It + * should be changed by this method such that {@link #createSecondaryKey} + * will return false. Other fields in the data object should remain + * unchanged.</p> + * + * @param data is the source and destination deserialized data + * entry. + * + * @return the destination data object, or null to indicate that the + * key is not present and no change is necessary. The data returned may + * be the same object passed as the data parameter or a newly created + * object. + */ + public Object nullifyForeignKey(Object data) { + + return null; + } +} diff --git a/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledBinding.java b/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledBinding.java new file mode 100644 index 000000000..85a254e9a --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledBinding.java @@ -0,0 +1,93 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleSerialMarshalledBinding.java,v 1.3 2004/09/22 18:01:01 bostic Exp $ + */ + +package com.sleepycat.bind.serial; + +import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity; +import com.sleepycat.bind.tuple.TupleInput; +import com.sleepycat.bind.tuple.TupleOutput; + +/** + * A concrete <code>TupleSerialBinding</code> that delegates to the + * <code>MarshalledTupleKeyEntity</code> interface of the entity class. + * + * <p>The {@link MarshalledTupleKeyEntity} interface must be implemented by the + * entity class to convert between the key/data entry and entity object.</p> + * + * <p> The binding is "tricky" in that it uses the entity class for both the + * stored data entry and the combined entity object. To do this, the entity's + * key field(s) are transient and are set by the binding after the data object + * has been deserialized. This avoids the use of a "data" class completely. + * </p> + * + * @author Mark Hayes + * @see MarshalledTupleKeyEntity + */ +public class TupleSerialMarshalledBinding extends TupleSerialBinding { + + /** + * Creates a tuple-serial marshalled binding object. + * + * @param classCatalog is the catalog to hold shared class information and + * for a database should be a {@link StoredClassCatalog}. + * + * @param baseClass is the base class for serialized objects stored using + * this binding -- all objects using this binding must be an instance of + * this class. + */ + public TupleSerialMarshalledBinding(ClassCatalog classCatalog, + Class baseClass) { + + this(new SerialBinding(classCatalog, baseClass)); + } + + /** + * Creates a tuple-serial marshalled binding object. + * + * @param dataBinding is the binding used for serializing and deserializing + * the entity object. + */ + public TupleSerialMarshalledBinding(SerialBinding dataBinding) { + + super(dataBinding); + } + + // javadoc is inherited + public Object entryToObject(TupleInput tupleInput, Object javaInput) { + + /* Creates the entity by combining the stored key and data. + * This "tricky" binding returns the stored data as the entity, but + * first it sets the transient key fields from the stored key. + */ + MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) javaInput; + + if (tupleInput != null) { // may be null if not used by key extractor + entity.unmarshalPrimaryKey(tupleInput); + } + return entity; + } + + // javadoc is inherited + public void objectToKey(Object object, TupleOutput output) { + + /* Creates the stored key from the entity. + */ + MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) object; + entity.marshalPrimaryKey(output); + } + + // javadoc is inherited + public Object objectToData(Object object) { + + /* Returns the entity as the stored data. There is nothing to do here + * since the entity's key fields are transient. + */ + return object; + } +} diff --git a/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledKeyCreator.java b/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledKeyCreator.java new file mode 100644 index 000000000..98b8fa637 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/TupleSerialMarshalledKeyCreator.java @@ -0,0 +1,75 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleSerialMarshalledKeyCreator.java,v 1.2 2004/06/04 18:24:49 mark Exp $ + */ + +package com.sleepycat.bind.serial; + +import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity; +import com.sleepycat.bind.tuple.TupleInput; +import com.sleepycat.bind.tuple.TupleOutput; + +/** + * A concrete key creator that works in conjunction with a {@link + * TupleSerialMarshalledBinding}. This key creator works by calling the + * methods of the {@link MarshalledTupleKeyEntity} interface to create and + * clear the index key fields. + * + * @author Mark Hayes + */ +public class TupleSerialMarshalledKeyCreator extends TupleSerialKeyCreator { + + private TupleSerialMarshalledBinding binding; + private String keyName; + + /** + * Creates a tuple-serial marshalled key creator. + * + * @param binding is the binding used for the tuple-serial entity. + * + * @param keyName is the key name passed to the {@link + * MarshalledTupleKeyEntity#marshalSecondaryKey} method to identify the + * index key. + */ + public TupleSerialMarshalledKeyCreator(TupleSerialMarshalledBinding + binding, + String keyName) { + + super(binding.dataBinding); + this.binding = binding; + this.keyName = keyName; + + if (dataBinding == null) { + throw new NullPointerException("dataBinding may not be null"); + } + } + + // javadoc is inherited + public boolean createSecondaryKey(TupleInput primaryKeyInput, + Object dataInput, + TupleOutput indexKeyOutput) { + + /* + * The primary key is unmarshalled before marshalling the index key, to + * account for cases where the index key includes fields taken from the + * primary key. + */ + MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) + binding.entryToObject(primaryKeyInput, dataInput); + + return entity.marshalSecondaryKey(keyName, indexKeyOutput); + } + + // javadoc is inherited + public Object nullifyForeignKey(Object dataInput) { + + MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) + binding.entryToObject(null, dataInput); + + return entity.nullifyForeignKey(keyName) ? dataInput : null; + } +} diff --git a/db/java/src/com/sleepycat/bind/serial/package.html b/db/java/src/com/sleepycat/bind/serial/package.html new file mode 100644 index 000000000..eab1e2151 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/serial/package.html @@ -0,0 +1,6 @@ +<!-- $Id: package.html,v 1.1 2004/08/02 18:52:04 mjc Exp $ --> +<html> +<body> +Bindings that use Java serialization. +</body> +</html> diff --git a/db/java/src/com/sleepycat/bind/tuple/BooleanBinding.java b/db/java/src/com/sleepycat/bind/tuple/BooleanBinding.java new file mode 100644 index 000000000..389b19f25 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/BooleanBinding.java @@ -0,0 +1,75 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: BooleanBinding.java,v 1.5 2004/08/13 15:16:44 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a <code>Boolean</code> primitive + * wrapper or a <code>boolean</code> primitive. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class BooleanBinding extends TupleBinding { + + private static final int BOOLEAN_SIZE = 1; + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return input.readBoolean() ? Boolean.TRUE : Boolean.FALSE; + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + booleanToEntry(((Boolean) object).booleanValue(), entry); + } + + /** + * Converts an entry buffer into a simple <code>boolean</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static boolean entryToBoolean(DatabaseEntry entry) { + + return entryToInput(entry).readBoolean(); + } + + /** + * Converts a simple <code>boolean</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void booleanToEntry(boolean val, DatabaseEntry entry) { + + outputToEntry(newOutput(new byte[BOOLEAN_SIZE]).writeBoolean(val), + entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/ByteBinding.java b/db/java/src/com/sleepycat/bind/tuple/ByteBinding.java new file mode 100644 index 000000000..f227e50c6 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/ByteBinding.java @@ -0,0 +1,74 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ByteBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a <code>Byte</code> primitive + * wrapper or a <code>byte</code> primitive. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class ByteBinding extends TupleBinding { + + private static final int BYTE_SIZE = 1; + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return new Byte(input.readByte()); + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + byteToEntry(((Number) object).byteValue(), entry); + } + + /** + * Converts an entry buffer into a simple <code>byte</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static byte entryToByte(DatabaseEntry entry) { + + return entryToInput(entry).readByte(); + } + + /** + * Converts a simple <code>byte</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void byteToEntry(byte val, DatabaseEntry entry) { + + outputToEntry(newOutput(new byte[BYTE_SIZE]).writeByte(val), entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/CharacterBinding.java b/db/java/src/com/sleepycat/bind/tuple/CharacterBinding.java new file mode 100644 index 000000000..c521a9ac4 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/CharacterBinding.java @@ -0,0 +1,74 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: CharacterBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a <code>Character</code> primitive + * wrapper or a <code>char</code> primitive. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class CharacterBinding extends TupleBinding { + + private static final int CHAR_SIZE = 2; + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return new Character(input.readChar()); + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + charToEntry(((Character) object).charValue(), entry); + } + + /** + * Converts an entry buffer into a simple <code>char</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static char entryToChar(DatabaseEntry entry) { + + return entryToInput(entry).readChar(); + } + + /** + * Converts a simple <code>char</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void charToEntry(char val, DatabaseEntry entry) { + + outputToEntry(newOutput(new byte[CHAR_SIZE]).writeChar(val), entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/DoubleBinding.java b/db/java/src/com/sleepycat/bind/tuple/DoubleBinding.java new file mode 100644 index 000000000..a94c7ecb9 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/DoubleBinding.java @@ -0,0 +1,75 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: DoubleBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a <code>Double</code> primitive + * wrapper or a <code>double</code> primitive. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class DoubleBinding extends TupleBinding { + + private static final int DOUBLE_SIZE = 8; + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return new Double(input.readDouble()); + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + doubleToEntry(((Number) object).doubleValue(), entry); + } + + /** + * Converts an entry buffer into a simple <code>double</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static double entryToDouble(DatabaseEntry entry) { + + return entryToInput(entry).readDouble(); + } + + /** + * Converts a simple <code>double</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void doubleToEntry(double val, DatabaseEntry entry) { + + outputToEntry(newOutput(new byte[DOUBLE_SIZE]).writeDouble(val), + entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/FloatBinding.java b/db/java/src/com/sleepycat/bind/tuple/FloatBinding.java new file mode 100644 index 000000000..c7d45716f --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/FloatBinding.java @@ -0,0 +1,74 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: FloatBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a <code>Float</code> primitive + * wrapper or a <code>float</code> primitive. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class FloatBinding extends TupleBinding { + + private static final int FLOAT_SIZE = 4; + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return new Float(input.readFloat()); + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + floatToEntry(((Number) object).floatValue(), entry); + } + + /** + * Converts an entry buffer into a simple <code>float</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static float entryToFloat(DatabaseEntry entry) { + + return entryToInput(entry).readFloat(); + } + + /** + * Converts a simple <code>float</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void floatToEntry(float val, DatabaseEntry entry) { + + outputToEntry(newOutput(new byte[FLOAT_SIZE]).writeFloat(val), entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/IntegerBinding.java b/db/java/src/com/sleepycat/bind/tuple/IntegerBinding.java new file mode 100644 index 000000000..c2b6391ee --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/IntegerBinding.java @@ -0,0 +1,74 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: IntegerBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a <code>Integer</code> primitive + * wrapper or an <code>int</code> primitive. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class IntegerBinding extends TupleBinding { + + private static final int INT_SIZE = 4; + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return new Integer(input.readInt()); + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + intToEntry(((Number) object).intValue(), entry); + } + + /** + * Converts an entry buffer into a simple <code>int</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static int entryToInt(DatabaseEntry entry) { + + return entryToInput(entry).readInt(); + } + + /** + * Converts a simple <code>int</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void intToEntry(int val, DatabaseEntry entry) { + + outputToEntry(newOutput(new byte[INT_SIZE]).writeInt(val), entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/LongBinding.java b/db/java/src/com/sleepycat/bind/tuple/LongBinding.java new file mode 100644 index 000000000..6dee013b7 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/LongBinding.java @@ -0,0 +1,74 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: LongBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a <code>Long</code> primitive + * wrapper or a <code>long</code> primitive. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class LongBinding extends TupleBinding { + + private static final int LONG_SIZE = 8; + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return new Long(input.readLong()); + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + longToEntry(((Number) object).longValue(), entry); + } + + /** + * Converts an entry buffer into a simple <code>long</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static long entryToLong(DatabaseEntry entry) { + + return entryToInput(entry).readLong(); + } + + /** + * Converts a simple <code>long</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void longToEntry(long val, DatabaseEntry entry) { + + outputToEntry(newOutput(new byte[LONG_SIZE]).writeLong(val), entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleEntry.java b/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleEntry.java new file mode 100644 index 000000000..9665b3c85 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleEntry.java @@ -0,0 +1,45 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: MarshalledTupleEntry.java,v 1.2 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.bind.tuple; + +/** + * A marshalling interface implemented by key, data or entity classes that + * are represented as tuples. + * + * <p>Key classes implement this interface to marshal their key entry. Data or + * entity classes implement this interface to marshal their data entry. + * Implementations of this interface must have a public no arguments + * constructor so that they can be instantiated by a binding, prior to calling + * the {@link #unmarshalEntry} method.</p> + * + * <p>Note that implementing this interface is not necessary when the object is + * a Java simple type, for example: String, Integer, etc. These types can be + * used with built-in bindings returned by {@link + * TupleBinding#getPrimitiveBinding}.</p> + * + * @author Mark Hayes + * @see TupleTupleMarshalledBinding + */ +public interface MarshalledTupleEntry { + + /** + * Construct the key or data tuple entry from the key or data object. + * + * @param dataOutput is the output tuple. + */ + void marshalEntry(TupleOutput dataOutput); + + /** + * Construct the key or data object from the key or data tuple entry. + * + * @param dataInput is the input tuple. + */ + void unmarshalEntry(TupleInput dataInput); +} diff --git a/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleKeyEntity.java b/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleKeyEntity.java new file mode 100644 index 000000000..d7640e9f5 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/MarshalledTupleKeyEntity.java @@ -0,0 +1,71 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: MarshalledTupleKeyEntity.java,v 1.3 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +/** + * A marshalling interface implemented by entity classes that represent keys as + * tuples. Since <code>MarshalledTupleKeyEntity</code> objects are instantiated + * using Java deserialization, no particular constructor is required by classes + * that implement this interface. + * + * <p>Note that a marshalled tuple key extractor is somewhat less efficient + * than a non-marshalled key tuple extractor because more conversions are + * needed. A marshalled key extractor must convert the entry to an object in + * order to extract the key fields, while an unmarshalled key extractor does + * not.</p> + * + * @author Mark Hayes + * @see TupleTupleMarshalledBinding + * @see com.sleepycat.bind.serial.TupleSerialMarshalledBinding + */ +public interface MarshalledTupleKeyEntity { + + /** + * Extracts the entity's primary key and writes it to the key output. + * + * @param keyOutput is the output tuple. + */ + void marshalPrimaryKey(TupleOutput keyOutput); + + /** + * Completes construction of the entity by setting its primary key from the + * stored primary key. + * + * @param keyInput is the input tuple. + */ + void unmarshalPrimaryKey(TupleInput keyInput); + + /** + * Extracts the entity's secondary key and writes it to the key output. + * + * @param keyName identifies the secondary key. + * + * @param keyOutput is the output tuple. + * + * @return true if a key was created, or false to indicate that the key is + * not present. + */ + boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput); + + /** + * Clears the entity's secondary key fields for the given key name. + * + * <p>The specified index key should be changed by this method such that + * {@link #marshalSecondaryKey} for the same key name will return false. + * Other fields in the data object should remain unchanged.</p> + * + * + * @param keyName identifies the secondary key. + * + * @return true if the key was cleared, or false to indicate that the key + * is not present and no change is necessary. + */ + boolean nullifyForeignKey(String keyName); +} diff --git a/db/java/src/com/sleepycat/bind/tuple/ShortBinding.java b/db/java/src/com/sleepycat/bind/tuple/ShortBinding.java new file mode 100644 index 000000000..d330f9b1b --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/ShortBinding.java @@ -0,0 +1,74 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ShortBinding.java,v 1.4 2004/08/02 18:52:04 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a <code>Short</code> primitive + * wrapper or a <code>short</code> primitive. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class ShortBinding extends TupleBinding { + + private static final int SHORT_SIZE = 2; + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return new Short(input.readShort()); + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + shortToEntry(((Number) object).shortValue(), entry); + } + + /** + * Converts an entry buffer into a simple <code>short</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static short entryToShort(DatabaseEntry entry) { + + return entryToInput(entry).readShort(); + } + + /** + * Converts a simple <code>short</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void shortToEntry(short val, DatabaseEntry entry) { + + outputToEntry(newOutput(new byte[SHORT_SIZE]).writeShort(val), entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/StringBinding.java b/db/java/src/com/sleepycat/bind/tuple/StringBinding.java new file mode 100644 index 000000000..257e01f44 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/StringBinding.java @@ -0,0 +1,76 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StringBinding.java,v 1.4 2004/08/02 18:52:05 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.util.UtfOps; +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>TupleBinding</code> for a simple <code>String</code> value. + * + * <p>There are two ways to use this class:</p> + * <ol> + * <li>When using the {@link com.sleepycat.db} package directly, the static + * methods in this class can be used to convert between primitive values and + * {@link DatabaseEntry} objects.</li> + * <li>When using the {@link com.sleepycat.collections} package, an instance of + * this class can be used with any stored collection. The easiest way to + * obtain a binding instance is with the {@link + * TupleBinding#getPrimitiveBinding} method.</li> + * </ol> + */ +public class StringBinding extends TupleBinding { + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + return input.readString(); + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + /* Do nothing. Not called by objectToEntry(Object,DatabaseEntry). */ + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + stringToEntry((String) object, entry); + } + + /** + * Converts an entry buffer into a simple <code>String</code> value. + * + * @param entry is the source entry buffer. + * + * @return the resulting value. + */ + public static String entryToString(DatabaseEntry entry) { + + return entryToInput(entry).readString(); + } + + /** + * Converts a simple <code>String</code> value into an entry buffer. + * + * @param val is the source value. + * + * @param entry is the destination entry buffer. + */ + public static void stringToEntry(String val, DatabaseEntry entry) { + + int stringLength = + (val == null) ? 1 : UtfOps.getByteLength(val.toCharArray()); + stringLength++; // null terminator + outputToEntry(newOutput(new byte[stringLength]).writeString(val), + entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleBinding.java new file mode 100644 index 000000000..5ae95545b --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleBinding.java @@ -0,0 +1,179 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleBinding.java,v 1.4 2004/06/29 06:06:36 mark Exp $ + */ + +package com.sleepycat.bind.tuple; + +import java.util.HashMap; +import java.util.Map; + +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.db.DatabaseEntry; + +/** + * An abstract <code>EntryBinding</code> that treats a key or data entry as a + * tuple; it includes predefined bindings for Java primitive types. + * + * <p>This class takes care of converting the entries to/from {@link + * TupleInput} and {@link TupleOutput} objects. Its two abstract methods must + * be implemented by a concrete subclass to convert between tuples and key or + * data objects.</p> + * <ul> + * <li> {@link #entryToObject(TupleInput)} </li> + * <li> {@link #objectToEntry(Object,TupleOutput)} </li> + * </ul> + * + * <p>For key or data entries which are Java primitive classes (String, + * Integer, etc) {@link #getPrimitiveBinding} may be used to return a builtin + * tuple binding. A custom tuple binding for these types is not needed.</p> + * + * @author Mark Hayes + */ +public abstract class TupleBinding implements EntryBinding { + + private static final Map primitives = new HashMap(); + static { + primitives.put(String.class, new StringBinding()); + primitives.put(Character.class, new CharacterBinding()); + primitives.put(Boolean.class, new BooleanBinding()); + primitives.put(Byte.class, new ByteBinding()); + primitives.put(Short.class, new ShortBinding()); + primitives.put(Integer.class, new IntegerBinding()); + primitives.put(Long.class, new LongBinding()); + primitives.put(Float.class, new FloatBinding()); + primitives.put(Double.class, new DoubleBinding()); + } + + /** + * Creates a tuple binding. + */ + public TupleBinding() { + } + + // javadoc is inherited + public Object entryToObject(DatabaseEntry entry) { + + return entryToObject(entryToInput(entry)); + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + TupleOutput output = newOutput(); + objectToEntry(object, output); + outputToEntry(output, entry); + } + + /** + * Utility method for use by bindings to create a tuple output object. + * + * @return a new tuple output object. + */ + public static TupleOutput newOutput() { + + return new TupleOutput(); + } + + /** + * Utility method for use by bindings to create a tuple output object + * with a specific starting size. + * + * @return a new tuple output object. + */ + public static TupleOutput newOutput(byte[] buffer) { + + return new TupleOutput(buffer); + } + + /** + * Utility method to set the data in a entry buffer to the data in a tuple + * output object. + * + * @param output is the source tuple output object. + * + * @param entry is the destination entry buffer. + */ + public static void outputToEntry(TupleOutput output, DatabaseEntry entry) { + + entry.setData(output.getBufferBytes(), output.getBufferOffset(), + output.getBufferLength()); + } + + /** + * Utility method to set the data in a entry buffer to the data in a tuple + * input object. + * + * @param input is the source tuple input object. + * + * @param entry is the destination entry buffer. + */ + public static void inputToEntry(TupleInput input, DatabaseEntry entry) { + + entry.setData(input.getBufferBytes(), input.getBufferOffset(), + input.getBufferLength()); + } + + /** + * Utility method to create a new tuple input object for reading the data + * from a given buffer. If an existing input is reused, it is reset before + * returning it. + * + * @param entry is the source entry buffer. + * + * @return the new tuple input object. + */ + public static TupleInput entryToInput(DatabaseEntry entry) { + + return new TupleInput(entry.getData(), entry.getOffset(), + entry.getSize()); + } + + /** + * Constructs a key or data object from a {@link TupleInput} entry. + * + * @param input is the tuple key or data entry. + * + * @return the key or data object constructed from the entry. + */ + public abstract Object entryToObject(TupleInput input); + + /** + * Converts a key or data object to a tuple entry. + * + * @param object is the key or data object. + * + * @param output is the tuple entry to which the key or data should be + * written. + */ + public abstract void objectToEntry(Object object, TupleOutput output); + + /** + * Creates a tuple binding for a primitive Java class. The following + * Java classes are supported. + * <ul> + * <li><code>String</code></li> + * <li><code>Character</code></li> + * <li><code>Boolean</code></li> + * <li><code>Byte</code></li> + * <li><code>Short</code></li> + * <li><code>Integer</code></li> + * <li><code>Long</code></li> + * <li><code>Float</code></li> + * <li><code>Double</code></li> + * </ul> + * + * @param cls is the primitive Java class. + * + * @return a new binding for the primitive class or null if the cls + * parameter is not one of the supported classes. + */ + public static TupleBinding getPrimitiveBinding(Class cls) { + + return (TupleBinding) primitives.get(cls); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleInput.java b/db/java/src/com/sleepycat/bind/tuple/TupleInput.java new file mode 100644 index 000000000..97108b10a --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleInput.java @@ -0,0 +1,482 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleInput.java,v 1.4 2004/09/01 14:34:20 mark Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.util.FastInputStream; +import com.sleepycat.util.UtfOps; + +/** + * An <code>InputStream</code> with <code>DataInput</code>-like methods for + * reading tuple fields. It is used by <code>TupleBinding</code>. + * + * <p>This class has many methods that have the same signatures as methods in + * the {@link java.io.DataInput} interface. The reason this class does not + * implement {@link java.io.DataInput} is because it would break the interface + * contract for those methods because of data format differences.</p> + * + * <p>Signed numbers are stored in the buffer in MSB (most significant byte + * first) order with their sign bit (high-order bit) inverted to cause negative + * numbers to be sorted first when comparing values as unsigned byte arrays, + * as done in a database. Unsigned numbers, including characters, are stored + * in MSB order with no change to their sign bit.</p> + * + * <p>Strings and character arrays are stored either as a fixed length array of + * unicode characters, where the length must be known by the application, or as + * a null-terminated UTF byte array.</p> + * <ul> + * <li>Null strings are UTF encoded as { 0xFF }, which is not allowed in a + * standard UTF encoding. This allows null strings, as distinct from empty or + * zero length strings, to be represented in a tuple. Using the default + * comparator, null strings will be ordered last.</li> + * <li>Zero (0x0000) character values are UTF encoded as non-zero values, and + * therefore embedded zeros in the string are supported. The sequence { 0xC0, + * 0x80 } is used to encode a zero character. This UTF encoding is the same + * one used by native Java UTF libraries. However, this encoding of zero does + * impact the lexicographical ordering, and zeros will not be sorted first (the + * natural order) or last. For all character values other than zero, the + * default UTF byte ordering is the same as the Unicode lexicographical + * character ordering.</li> + * </ul> + * + * <p>Floats and doubles are stored in standard Java integer-bit representation + * (IEEE 754). Non-negative numbers are correctly ordered by numeric value. + * However, negative numbers are not correctly ordered; therefore, if you use + * negative floating point numbers in a key, you'll need to implement and + * configure a custom comparator to get correct numeric ordering.</p> + * + * @author Mark Hayes + */ +public class TupleInput extends FastInputStream { + + /** + * Creates a tuple input object for reading a byte array of tuple data. A + * reference to the byte array will be kept by this object (it will not be + * copied) and therefore the byte array should not be modified while this + * object is in use. + * + * @param buffer is the byte array to be read and should contain data in + * tuple format. + */ + public TupleInput(byte[] buffer) { + + super(buffer); + } + + /** + * Creates a tuple input object for reading a byte array of tuple data at + * a given offset for a given length. A reference to the byte array will + * be kept by this object (it will not be copied) and therefore the byte + * array should not be modified while this object is in use. + * + * @param buffer is the byte array to be read and should contain data in + * tuple format. + * + * @param offset is the byte offset at which to begin reading. + * + * @param length is the number of bytes to be read. + */ + public TupleInput(byte[] buffer, int offset, int length) { + + super(buffer, offset, length); + } + + /** + * Creates a tuple input object from the data contained in a tuple output + * object. A reference to the tuple output's byte array will be kept by + * this object (it will not be copied) and therefore the tuple output + * object should not be modified while this object is in use. + * + * @param output is the tuple output object containing the data to be read. + */ + public TupleInput(TupleOutput output) { + + super(output.getBufferBytes(), output.getBufferOffset(), + output.getBufferLength()); + } + + // --- begin DataInput compatible methods --- + + /** + * Reads a null-terminated UTF string from the data buffer and converts + * the data from UTF to Unicode. + * Reads values that were written using {@link + * TupleOutput#writeString(String)}. + * + * @return the converted string. + * + * @throws IndexOutOfBoundsException if no null terminating byte is found + * in the buffer. + * + * @throws IllegalArgumentException malformed UTF data is encountered. + */ + public final String readString() + throws IndexOutOfBoundsException, IllegalArgumentException { + + byte[] buf = getBufferBytes(); + int off = getBufferOffset(); + if (available() >= 2 && + buf[off] == TupleOutput.NULL_STRING_UTF_VALUE && + buf[off + 1] == 0) { + skip(2); + return null; + } else { + int byteLen = UtfOps.getZeroTerminatedByteLength(buf, off); + skip(byteLen + 1); + return UtfOps.bytesToString(buf, off, byteLen); + } + } + + /** + * Reads a char (two byte) unsigned value from the buffer. + * Reads values that were written using {@link TupleOutput#writeChar}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final char readChar() throws IndexOutOfBoundsException { + + return (char) readUnsignedShort(); + } + + /** + * Reads a boolean (one byte) unsigned value from the buffer and returns + * true if it is non-zero and false if it is zero. + * Reads values that were written using {@link TupleOutput#writeBoolean}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final boolean readBoolean() throws IndexOutOfBoundsException { + + int c = readFast(); + if (c < 0) { + throw new IndexOutOfBoundsException(); + } + return (c != 0); + } + + /** + * Reads a signed byte (one byte) value from the buffer. + * Reads values that were written using {@link TupleOutput#writeByte}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final byte readByte() throws IndexOutOfBoundsException { + + return (byte) (readUnsignedByte() ^ 0x80); + } + + /** + * Reads a signed short (two byte) value from the buffer. + * Reads values that were written using {@link TupleOutput#writeShort}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final short readShort() throws IndexOutOfBoundsException { + + return (short) (readUnsignedShort() ^ 0x8000); + } + + /** + * Reads a signed int (four byte) value from the buffer. + * Reads values that were written using {@link TupleOutput#writeInt}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final int readInt() throws IndexOutOfBoundsException { + + return (int) (readUnsignedInt() ^ 0x80000000); + } + + /** + * Reads a signed long (eight byte) value from the buffer. + * Reads values that were written using {@link TupleOutput#writeLong}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final long readLong() throws IndexOutOfBoundsException { + + return readUnsignedLong() ^ 0x8000000000000000L; + } + + /** + * Reads a signed float (four byte) value from the buffer. + * Reads values that were written using {@link TupleOutput#writeFloat}. + * <code>Float.intBitsToFloat</code> is used to convert the signed int + * value. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final float readFloat() throws IndexOutOfBoundsException { + + return Float.intBitsToFloat((int) readUnsignedInt()); + } + + /** + * Reads a signed double (eight byte) value from the buffer. + * Reads values that were written using {@link TupleOutput#writeDouble}. + * <code>Double.longBitsToDouble</code> is used to convert the signed long + * value. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final double readDouble() throws IndexOutOfBoundsException { + + return Double.longBitsToDouble(readUnsignedLong()); + } + + /** + * Reads an unsigned byte (one byte) value from the buffer. + * Reads values that were written using {@link + * TupleOutput#writeUnsignedByte}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final int readUnsignedByte() throws IndexOutOfBoundsException { + + int c = readFast(); + if (c < 0) { + throw new IndexOutOfBoundsException(); + } + return c; + } + + /** + * Reads an unsigned short (two byte) value from the buffer. + * Reads values that were written using {@link + * TupleOutput#writeUnsignedShort}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final int readUnsignedShort() throws IndexOutOfBoundsException { + + int c1 = readFast(); + int c2 = readFast(); + if ((c1 | c2) < 0) { + throw new IndexOutOfBoundsException(); + } + return ((c1 << 8) | c2); + } + + // --- end DataInput compatible methods --- + + /** + * Reads an unsigned int (four byte) value from the buffer. + * Reads values that were written using {@link + * TupleOutput#writeUnsignedInt}. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final long readUnsignedInt() throws IndexOutOfBoundsException { + + long c1 = readFast(); + long c2 = readFast(); + long c3 = readFast(); + long c4 = readFast(); + if ((c1 | c2 | c3 | c4) < 0) { + throw new IndexOutOfBoundsException(); + } + return ((c1 << 24) | (c2 << 16) | (c3 << 8) | c4); + } + + /** + * This method is private since an unsigned long cannot be treated as + * such in Java, nor converted to a BigInteger of the same value. + */ + private final long readUnsignedLong() throws IndexOutOfBoundsException { + + long c1 = readFast(); + long c2 = readFast(); + long c3 = readFast(); + long c4 = readFast(); + long c5 = readFast(); + long c6 = readFast(); + long c7 = readFast(); + long c8 = readFast(); + if ((c1 | c2 | c3 | c4 | c5 | c6 | c7 | c8) < 0) { + throw new IndexOutOfBoundsException(); + } + return ((c1 << 56) | (c2 << 48) | (c3 << 40) | (c4 << 32) | + (c5 << 24) | (c6 << 16) | (c7 << 8) | c8); + } + + /** + * Reads the specified number of bytes from the buffer, converting each + * unsigned byte value to a character of the resulting string. + * Reads values that were written using {@link TupleOutput#writeBytes}. + * Only characters with values below 0x100 may be read using this method. + * + * @param length is the number of bytes to be read. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final String readBytes(int length) + throws IndexOutOfBoundsException { + + StringBuffer buf = new StringBuffer(length); + for (int i = 0; i < length; i++) { + int c = readFast(); + if (c < 0) { + throw new IndexOutOfBoundsException(); + } + buf.append((char) c); + } + return buf.toString(); + } + + /** + * Reads the specified number of characters from the buffer, converting + * each two byte unsigned value to a character of the resulting string. + * Reads values that were written using {@link TupleOutput#writeChars}. + * + * @param length is the number of characters to be read. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final String readChars(int length) + throws IndexOutOfBoundsException { + + StringBuffer buf = new StringBuffer(length); + for (int i = 0; i < length; i++) { + buf.append(readChar()); + } + return buf.toString(); + } + + /** + * Reads the specified number of bytes from the buffer, converting each + * unsigned byte value to a character of the resulting array. + * Reads values that were written using {@link TupleOutput#writeBytes}. + * Only characters with values below 0x100 may be read using this method. + * + * @param chars is the array to receive the data and whose length is used + * to determine the number of bytes to be read. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final void readBytes(char[] chars) + throws IndexOutOfBoundsException { + + for (int i = 0; i < chars.length; i++) { + int c = readFast(); + if (c < 0) { + throw new IndexOutOfBoundsException(); + } + chars[i] = (char) c; + } + } + + /** + * Reads the specified number of characters from the buffer, converting + * each two byte unsigned value to a character of the resulting array. + * Reads values that were written using {@link TupleOutput#writeChars}. + * + * @param chars is the array to receive the data and whose length is used + * to determine the number of characters to be read. + * + * @return the value read from the buffer. + * + * @throws IndexOutOfBoundsException if not enough bytes are available in + * the buffer. + */ + public final void readChars(char[] chars) + throws IndexOutOfBoundsException { + + for (int i = 0; i < chars.length; i++) { + chars[i] = readChar(); + } + } + + /** + * Reads the specified number of UTF characters string from the data + * buffer and converts the data from UTF to Unicode. + * Reads values that were written using {@link + * TupleOutput#writeString(char[])}. + * + * @param length is the number of characters to be read. + * + * @return the converted string. + * + * @throws IndexOutOfBoundsException if no null terminating byte is found + * in the buffer. + * + * @throws IllegalArgumentException malformed UTF data is encountered. + */ + public final String readString(int length) + throws IndexOutOfBoundsException, IllegalArgumentException { + + char[] chars = new char[length]; + readString(chars); + return new String(chars); + } + + /** + * Reads the specified number of UTF characters string from the data + * buffer and converts the data from UTF to Unicode. + * Reads values that were written using {@link + * TupleOutput#writeString(char[])}. + * + * @param chars is the array to receive the data and whose length is used + * to determine the number of characters to be read. + * + * @return the converted string. + * + * @throws IndexOutOfBoundsException if no null terminating byte is found + * in the buffer. + * + * @throws IllegalArgumentException malformed UTF data is encountered. + */ + public final void readString(char[] chars) + throws IndexOutOfBoundsException, IllegalArgumentException { + + byte[] buf = getBufferBytes(); + off = UtfOps.bytesToChars(buf, off, chars, 0, chars.length, false); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleInputBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleInputBinding.java new file mode 100644 index 000000000..20523dfce --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleInputBinding.java @@ -0,0 +1,46 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleInputBinding.java,v 1.2 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.db.DatabaseEntry; + +/** + * A concrete <code>EntryBinding</code> that uses the <code>TupleInput</code> + * object as the key or data object. + * + * A concrete tuple binding for key or data entries which are {@link + * TupleInput} objects. This binding is used when tuples themselves are the + * objects, rather than using application defined objects. A {@link TupleInput} + * must always be used. To convert a {@link TupleOutput} to a {@link + * TupleInput}, use the {@link TupleInput#TupleInput(TupleOutput)} constructor. + * + * @author Mark Hayes + */ +public class TupleInputBinding implements EntryBinding { + + /** + * Creates a tuple input binding. + */ + public TupleInputBinding() { + } + + // javadoc is inherited + public Object entryToObject(DatabaseEntry entry) { + + return TupleBinding.entryToInput(entry); + } + + // javadoc is inherited + public void objectToEntry(Object object, DatabaseEntry entry) { + + TupleBinding.inputToEntry((TupleInput) object, entry); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleMarshalledBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleMarshalledBinding.java new file mode 100644 index 000000000..05644eab0 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleMarshalledBinding.java @@ -0,0 +1,70 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleMarshalledBinding.java,v 1.3 2004/09/22 18:01:01 bostic Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.util.RuntimeExceptionWrapper; + +/** + * A concrete <code>TupleBinding</code> that delegates to the + * <code>MarshalledTupleEntry</code> interface of the data or key object. + * + * <p>This class works by calling the methods of the {@link + * MarshalledTupleEntry} interface, which must be implemented by the key or + * data class, to convert between the key or data entry and the object.</p> + * + * @author Mark Hayes + */ +public class TupleMarshalledBinding extends TupleBinding { + + private Class cls; + + /** + * Creates a tuple marshalled binding object. + * + * <p>The given class is used to instantiate key or data objects using + * {@link Class#forName}, and therefore must be a public class and have a + * public no-arguments constructor. It must also implement the {@link + * MarshalledTupleEntry} interface.</p> + * + * @param cls is the class of the key or data objects. + */ + public TupleMarshalledBinding(Class cls) { + + this.cls = cls; + + /* The class will be used to instantiate the object. */ + if (!MarshalledTupleEntry.class.isAssignableFrom(cls)) { + throw new IllegalArgumentException(cls.toString() + + " does not implement MarshalledTupleEntry"); + } + } + + // javadoc is inherited + public Object entryToObject(TupleInput input) { + + try { + MarshalledTupleEntry obj = + (MarshalledTupleEntry) cls.newInstance(); + obj.unmarshalEntry(input); + return obj; + } catch (IllegalAccessException e) { + throw new RuntimeExceptionWrapper(e); + } catch (InstantiationException e) { + throw new RuntimeExceptionWrapper(e); + } + } + + // javadoc is inherited + public void objectToEntry(Object object, TupleOutput output) { + + MarshalledTupleEntry obj = (MarshalledTupleEntry) object; + obj.marshalEntry(output); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleOutput.java b/db/java/src/com/sleepycat/bind/tuple/TupleOutput.java new file mode 100644 index 000000000..da1fa0c8f --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleOutput.java @@ -0,0 +1,398 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleOutput.java,v 1.4 2004/09/01 14:34:20 mark Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.util.FastOutputStream; +import com.sleepycat.util.UtfOps; + +/** + * An <code>OutputStream</code> with <code>DataOutput</code>-like methods for + * writing tuple fields. It is used by <code>TupleBinding</code>. + * + * <p>This class has many methods that have the same signatures as methods in + * the {@link java.io.DataOutput} interface. The reason this class does not + * implement {@link java.io.DataOutput} is because it would break the interface + * contract for those methods because of data format differences.</p> + * + * <p>Signed numbers are stored in the buffer in MSB (most significant byte + * first) order with their sign bit (high-order bit) inverted to cause negative + * numbers to be sorted first when comparing values as unsigned byte arrays, + * as done in a database. Unsigned numbers, including characters, are stored + * in MSB order with no change to their sign bit.</p> + * + * <p>Strings and character arrays are stored either as a fixed length array of + * unicode characters, where the length must be known by the application, or as + * a null-terminated UTF byte array.</p> + * <ul> + * <li>Null strings are UTF encoded as { 0xFF }, which is not allowed in a + * standard UTF encoding. This allows null strings, as distinct from empty or + * zero length strings, to be represented in a tuple. Using the default + * comparator, null strings will be ordered last.</li> + * <li>Zero (0x0000) character values are UTF encoded as non-zero values, and + * therefore embedded zeros in the string are supported. The sequence { 0xC0, + * 0x80 } is used to encode a zero character. This UTF encoding is the same + * one used by native Java UTF libraries. However, this encoding of zero does + * impact the lexicographical ordering, and zeros will not be sorted first (the + * natural order) or last. For all character values other than zero, the + * default UTF byte ordering is the same as the Unicode lexicographical + * character ordering.</li> + * </ul> + * + * <p>Floats and doubles are stored in standard Java integer-bit representation + * (IEEE 754). Non-negative numbers are correctly ordered by numeric value. + * However, negative numbers are not correctly ordered; therefore, if you use + * negative floating point numbers in a key, you'll need to implement and + * configure a custom comparator to get correct numeric ordering.</p> + * + * @author Mark Hayes + */ +public class TupleOutput extends FastOutputStream { + + /** + * We represent a null string as a single FF UTF character, which cannot + * occur in a UTF encoded string. + */ + static final int NULL_STRING_UTF_VALUE = ((byte) 0xFF); + + /** + * Creates a tuple output object for writing a byte array of tuple data. + */ + public TupleOutput() { + + super(); + } + + /** + * Creates a tuple output object for writing a byte array of tuple data, + * using a given buffer. A new buffer will be allocated only if the number + * of bytes needed is greater than the length of this buffer. A reference + * to the byte array will be kept by this object and therefore the byte + * array should not be modified while this object is in use. + * + * @param buffer is the byte array to use as the buffer. + */ + public TupleOutput(byte[] buffer) { + + super(buffer); + } + + // --- begin DataOutput compatible methods --- + + /** + * Writes the specified bytes to the buffer, converting each character to + * an unsigned byte value. + * Writes values that can be read using {@link TupleInput#readBytes}. + * Only characters with values below 0x100 may be written using this + * method, since the high-order 8 bits of all characters are discarded. + * + * @param val is the string containing the values to be written. + * + * @return this tuple output object. + * + * @throws NullPointerException if the val parameter is null. + */ + public final TupleOutput writeBytes(String val) { + + writeBytes(val.toCharArray()); + return this; + } + + /** + * Writes the specified characters to the buffer, converting each character + * to a two byte unsigned value. + * Writes values that can be read using {@link TupleInput#readChars}. + * + * @param val is the string containing the characters to be written. + * + * @return this tuple output object. + * + * @throws NullPointerException if the val parameter is null. + */ + public final TupleOutput writeChars(String val) { + + writeChars(val.toCharArray()); + return this; + } + + /** + * Writes the specified characters to the buffer, converting each character + * to UTF format, and adding a null terminator byte. + * Note that zero (0x0000) character values are encoded as non-zero values + * and a null String parameter is encoded as 0xFF. + * Writes values that can be read using {@link TupleInput#readString()}. + * + * @param val is the string containing the characters to be written. + * + * @return this tuple output object. + */ + public final TupleOutput writeString(String val) { + + if (val != null) { + writeString(val.toCharArray()); + } else { + writeFast(NULL_STRING_UTF_VALUE); + } + writeFast(0); + return this; + } + + /** + * Writes a char (two byte) unsigned value to the buffer. + * Writes values that can be read using {@link TupleInput#readChar}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeChar(int val) { + + writeFast((byte) (val >>> 8)); + writeFast((byte) val); + return this; + } + + /** + * Writes a boolean (one byte) unsigned value to the buffer, writing one + * if the value is true and zero if it is false. + * Writes values that can be read using {@link TupleInput#readBoolean}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeBoolean(boolean val) { + + writeFast(val ? (byte)1 : (byte)0); + return this; + } + + /** + * Writes an signed byte (one byte) value to the buffer. + * Writes values that can be read using {@link TupleInput#readByte}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeByte(int val) { + + writeUnsignedByte(val ^ 0x80); + return this; + } + + /** + * Writes an signed short (two byte) value to the buffer. + * Writes values that can be read using {@link TupleInput#readShort}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeShort(int val) { + + writeUnsignedShort(val ^ 0x8000); + return this; + } + + /** + * Writes an signed int (four byte) value to the buffer. + * Writes values that can be read using {@link TupleInput#readInt}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeInt(int val) { + + writeUnsignedInt(val ^ 0x80000000); + return this; + } + + /** + * Writes an signed long (eight byte) value to the buffer. + * Writes values that can be read using {@link TupleInput#readLong}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeLong(long val) { + + writeUnsignedLong(val ^ 0x8000000000000000L); + return this; + } + + /** + * Writes an signed float (four byte) value to the buffer. + * Writes values that can be read using {@link TupleInput#readFloat}. + * <code>Float.floatToIntBits</code> is used to convert the signed float + * value. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeFloat(float val) { + + writeUnsignedInt(Float.floatToIntBits(val)); + return this; + } + + /** + * Writes an signed double (eight byte) value to the buffer. + * Writes values that can be read using {@link TupleInput#readDouble}. + * <code>Double.doubleToLongBits</code> is used to convert the signed + * double value. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeDouble(double val) { + + writeUnsignedLong(Double.doubleToLongBits(val)); + return this; + } + + // --- end DataOutput compatible methods --- + + /** + * Writes the specified bytes to the buffer, converting each character to + * an unsigned byte value. + * Writes values that can be read using {@link TupleInput#readBytes}. + * Only characters with values below 0x100 may be written using this + * method, since the high-order 8 bits of all characters are discarded. + * + * @param chars is the array of values to be written. + * + * @return this tuple output object. + * + * @throws NullPointerException if the chars parameter is null. + */ + public final TupleOutput writeBytes(char[] chars) { + + for (int i = 0; i < chars.length; i++) { + writeFast((byte) chars[i]); + } + return this; + } + + /** + * Writes the specified characters to the buffer, converting each character + * to a two byte unsigned value. + * Writes values that can be read using {@link TupleInput#readChars}. + * + * @param chars is the array of characters to be written. + * + * @return this tuple output object. + * + * @throws NullPointerException if the chars parameter is null. + */ + public final TupleOutput writeChars(char[] chars) { + + for (int i = 0; i < chars.length; i++) { + writeFast((byte) (chars[i] >>> 8)); + writeFast((byte) chars[i]); + } + return this; + } + + /** + * Writes the specified characters to the buffer, converting each character + * to UTF format. + * Note that zero (0x0000) character values are encoded as non-zero values. + * Writes values that can be read using {@link TupleInput#readString(int)} + * or {@link TupleInput#readString(char[])}. + * + * @param chars is the array of characters to be written. + * + * @return this tuple output object. + * + * @throws NullPointerException if the chars parameter is null. + */ + public final TupleOutput writeString(char[] chars) { + + if (chars.length == 0) return this; + + int utfLength = UtfOps.getByteLength(chars); + + makeSpace(utfLength); + UtfOps.charsToBytes(chars, 0, getBufferBytes(), getBufferLength(), + chars.length); + addSize(utfLength); + return this; + } + + /** + * Writes an unsigned byte (one byte) value to the buffer. + * Writes values that can be read using {@link + * TupleInput#readUnsignedByte}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeUnsignedByte(int val) { + + writeFast(val); + return this; + } + + /** + * Writes an unsigned short (two byte) value to the buffer. + * Writes values that can be read using {@link + * TupleInput#readUnsignedShort}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeUnsignedShort(int val) { + + writeFast((byte) (val >>> 8)); + writeFast((byte) val); + return this; + } + + /** + * Writes an unsigned int (four byte) value to the buffer. + * Writes values that can be read using {@link + * TupleInput#readUnsignedInt}. + * + * @param val is the value to write to the buffer. + * + * @return this tuple output object. + */ + public final TupleOutput writeUnsignedInt(long val) { + + writeFast((byte) (val >>> 24)); + writeFast((byte) (val >>> 16)); + writeFast((byte) (val >>> 8)); + writeFast((byte) val); + return this; + } + + /** + * This method is private since an unsigned long cannot be treated as + * such in Java, nor converted to a BigInteger of the same value. + */ + private final TupleOutput writeUnsignedLong(long val) { + + writeFast((byte) (val >>> 56)); + writeFast((byte) (val >>> 48)); + writeFast((byte) (val >>> 40)); + writeFast((byte) (val >>> 32)); + writeFast((byte) (val >>> 24)); + writeFast((byte) (val >>> 16)); + writeFast((byte) (val >>> 8)); + writeFast((byte) val); + return this; + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleTupleBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleTupleBinding.java new file mode 100644 index 000000000..ac8f4158f --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleTupleBinding.java @@ -0,0 +1,96 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleTupleBinding.java,v 1.2 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.db.DatabaseEntry; + +/** + * An abstract <code>EntityBinding</code> that treats an entity's key entry and + * data entry as tuples. + * + * <p>This class takes care of converting the entries to/from {@link + * TupleInput} and {@link TupleOutput} objects. Its three abstract methods + * must be implemented by a concrete subclass to convert between tuples and + * entity objects.</p> + * <ul> + * <li> {@link #entryToObject(TupleInput,TupleInput)} </li> + * <li> {@link #objectToKey(Object,TupleOutput)} </li> + * <li> {@link #objectToData(Object,TupleOutput)} </li> + * </ul> + * + * @author Mark Hayes + */ +public abstract class TupleTupleBinding implements EntityBinding { + + /** + * Creates a tuple-tuple entity binding. + */ + public TupleTupleBinding() { + } + + // javadoc is inherited + public Object entryToObject(DatabaseEntry key, DatabaseEntry data) { + + return entryToObject(TupleBinding.entryToInput(key), + TupleBinding.entryToInput(data)); + } + + // javadoc is inherited + public void objectToKey(Object object, DatabaseEntry key) { + + TupleOutput output = TupleBinding.newOutput(); + objectToKey(object, output); + TupleBinding.outputToEntry(output, key); + } + + // javadoc is inherited + public void objectToData(Object object, DatabaseEntry data) { + + TupleOutput output = TupleBinding.newOutput(); + objectToData(object, output); + TupleBinding.outputToEntry(output, data); + } + + // abstract methods + + /** + * Constructs an entity object from {@link TupleInput} key and data + * entries. + * + * @param keyInput is the {@link TupleInput} key entry object. + * + * @param dataInput is the {@link TupleInput} data entry object. + * + * @return the entity object constructed from the key and data. + */ + public abstract Object entryToObject(TupleInput keyInput, + TupleInput dataInput); + + /** + * Extracts a key tuple from an entity object. + * + * @param object is the entity object. + * + * @param output is the {@link TupleOutput} to which the key should be + * written. + */ + public abstract void objectToKey(Object object, TupleOutput output); + + /** + * Extracts a key tuple from an entity object. + * + * @param object is the entity object. + * + * @param output is the {@link TupleOutput} to which the data should be + * written. + */ + public abstract void objectToData(Object object, TupleOutput output); +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleTupleKeyCreator.java b/db/java/src/com/sleepycat/bind/tuple/TupleTupleKeyCreator.java new file mode 100644 index 000000000..f5041a94e --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleTupleKeyCreator.java @@ -0,0 +1,105 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleTupleKeyCreator.java,v 1.4 2004/08/02 18:52:05 mjc Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.SecondaryDatabase; +import com.sleepycat.db.SecondaryKeyCreator; + +/** + * An abstract key creator that uses a tuple key and a tuple data entry. This + * class takes care of converting the key and data entry to/from {@link + * TupleInput} and {@link TupleOutput} objects. + * + * @author Mark Hayes + */ +public abstract class TupleTupleKeyCreator + implements SecondaryKeyCreator { + + /** + * Creates a tuple-tuple key creator. + */ + public TupleTupleKeyCreator() { + } + + // javadoc is inherited + public boolean createSecondaryKey(SecondaryDatabase db, + DatabaseEntry primaryKeyEntry, + DatabaseEntry dataEntry, + DatabaseEntry indexKeyEntry) + throws DatabaseException { + + TupleOutput output = TupleBinding.newOutput(); + TupleInput primaryKeyInput = + TupleBinding.entryToInput(primaryKeyEntry); + TupleInput dataInput = TupleBinding.entryToInput(dataEntry); + if (createSecondaryKey(primaryKeyInput, dataInput, output)) { + TupleBinding.outputToEntry(output, indexKeyEntry); + return true; + } else { + return false; + } + } + + // javadoc is inherited + public boolean nullifyForeignKey(SecondaryDatabase db, + DatabaseEntry dataEntry) + throws DatabaseException { + + TupleOutput output = TupleBinding.newOutput(); + if (nullifyForeignKey(TupleBinding.entryToInput(dataEntry), + output)) { + TupleBinding.outputToEntry(output, dataEntry); + return true; + } else { + return false; + } + } + + /** + * Creates the index key from primary key tuple and data tuple. + * + * @param primaryKeyInput is the {@link TupleInput} for the primary key + * entry. + * + * @param dataInput is the {@link TupleInput} for the data entry. + * + * @param indexKeyOutput is the destination index key tuple. + * + * @return true if a key was created, or false to indicate that the key is + * not present. + */ + public abstract boolean createSecondaryKey(TupleInput primaryKeyInput, + TupleInput dataInput, + TupleOutput indexKeyOutput); + + /** + * Clears the index key in the tuple data entry. The dataInput should be + * read and then written to the dataOutput, clearing the index key in the + * process. + * + * <p>The secondary key should be output or removed by this method such + * that {@link #createSecondaryKey} will return false. Other fields in the + * data object should remain unchanged.</p> + * + * @param dataInput is the {@link TupleInput} for the data entry. + * + * @param dataOutput is the destination {@link TupleOutput}. + * + * @return true if the key was cleared, or false to indicate that the key + * is not present and no change is necessary. + */ + public boolean nullifyForeignKey(TupleInput dataInput, + TupleOutput dataOutput) { + + return false; + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledBinding.java b/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledBinding.java new file mode 100644 index 000000000..370b4cc8a --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledBinding.java @@ -0,0 +1,94 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleTupleMarshalledBinding.java,v 1.3 2004/09/22 18:01:01 bostic Exp $ + */ + +package com.sleepycat.bind.tuple; + +import com.sleepycat.util.RuntimeExceptionWrapper; + +/** + * A concrete <code>TupleTupleBinding</code> that delegates to the + * <code>MarshalledTupleEntry</code> and + * <code>MarshalledTupleKeyEntity</code> interfaces of the entity class. + * + * <p>This class calls the methods of the {@link MarshalledTupleEntry} + * interface to convert between the data entry and entity object. It calls the + * methods of the {@link MarshalledTupleKeyEntity} interface to convert between + * the key entry and the entity object. These two interfaces must both be + * implemented by the entity class.</p> + * + * @author Mark Hayes + */ +public class TupleTupleMarshalledBinding extends TupleTupleBinding { + + private Class cls; + + /** + * Creates a tuple-tuple marshalled binding object. + * + * <p>The given class is used to instantiate entity objects using + * {@link Class#forName}, and therefore must be a public class and have a + * public no-arguments constructor. It must also implement the {@link + * MarshalledTupleEntry} and {@link MarshalledTupleKeyEntity} + * interfaces.</p> + * + * @param cls is the class of the entity objects. + */ + public TupleTupleMarshalledBinding(Class cls) { + + this.cls = cls; + + // The entity class will be used to instantiate the entity object. + // + if (!MarshalledTupleKeyEntity.class.isAssignableFrom(cls)) { + throw new IllegalArgumentException(cls.toString() + + " does not implement MarshalledTupleKeyEntity"); + } + if (!MarshalledTupleEntry.class.isAssignableFrom(cls)) { + throw new IllegalArgumentException(cls.toString() + + " does not implement MarshalledTupleEntry"); + } + } + + // javadoc is inherited + public Object entryToObject(TupleInput keyInput, TupleInput dataInput) { + + // This "tricky" binding returns the stored data as the entity, but + // first it sets the transient key fields from the stored key. + MarshalledTupleEntry obj; + try { + obj = (MarshalledTupleEntry) cls.newInstance(); + } catch (IllegalAccessException e) { + throw new RuntimeExceptionWrapper(e); + } catch (InstantiationException e) { + throw new RuntimeExceptionWrapper(e); + } + if (dataInput != null) { // may be null if used by key extractor + obj.unmarshalEntry(dataInput); + } + MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) obj; + if (keyInput != null) { // may be null if used by key extractor + entity.unmarshalPrimaryKey(keyInput); + } + return entity; + } + + // javadoc is inherited + public void objectToKey(Object object, TupleOutput output) { + + MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) object; + entity.marshalPrimaryKey(output); + } + + // javadoc is inherited + public void objectToData(Object object, TupleOutput output) { + + MarshalledTupleEntry entity = (MarshalledTupleEntry) object; + entity.marshalEntry(output); + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledKeyCreator.java b/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledKeyCreator.java new file mode 100644 index 000000000..aa2911a91 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/TupleTupleMarshalledKeyCreator.java @@ -0,0 +1,75 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleTupleMarshalledKeyCreator.java,v 1.2 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.bind.tuple; + +/** + * A concrete key creator that works in conjunction with a {@link + * TupleTupleMarshalledBinding}. This key creator works by calling the + * methods of the {@link MarshalledTupleKeyEntity} interface to create and + * clear the index key. + * + * <p>Note that a marshalled tuple key creator is somewhat less efficient + * than a non-marshalled key tuple creator because more conversions are + * needed. A marshalled key creator must convert the entry to an object in + * order to create the key, while an unmarshalled key creator does not.</p> + * + * @author Mark Hayes + */ +public class TupleTupleMarshalledKeyCreator extends TupleTupleKeyCreator { + + private String keyName; + private TupleTupleMarshalledBinding binding; + + /** + * Creates a tuple-tuple marshalled key creator. + * + * @param binding is the binding used for the tuple-tuple entity. + * + * @param keyName is the key name passed to the {@link + * MarshalledTupleKeyEntity#marshalSecondaryKey} method to identify the + * index key. + */ + public TupleTupleMarshalledKeyCreator(TupleTupleMarshalledBinding binding, + String keyName) { + + this.binding = binding; + this.keyName = keyName; + } + + // javadoc is inherited + public boolean createSecondaryKey(TupleInput primaryKeyInput, + TupleInput dataInput, + TupleOutput indexKeyOutput) { + + /* The primary key is unmarshalled before marshalling the index key, to + * account for cases where the index key includes fields taken from the + * primary key. + */ + MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) + binding.entryToObject(primaryKeyInput, dataInput); + + return entity.marshalSecondaryKey(keyName, indexKeyOutput); + } + + // javadoc is inherited + public boolean nullifyForeignKey(TupleInput dataInput, + TupleOutput dataOutput) { + + // XXX null primary key input below may be unexpected by the binding + MarshalledTupleKeyEntity entity = (MarshalledTupleKeyEntity) + binding.entryToObject(null, dataInput); + if (entity.nullifyForeignKey(keyName)) { + binding.objectToData(entity, dataOutput); + return true; + } else { + return false; + } + } +} diff --git a/db/java/src/com/sleepycat/bind/tuple/package.html b/db/java/src/com/sleepycat/bind/tuple/package.html new file mode 100644 index 000000000..9f2723523 --- /dev/null +++ b/db/java/src/com/sleepycat/bind/tuple/package.html @@ -0,0 +1,6 @@ +<!-- $Id: package.html,v 1.1 2004/08/02 18:52:05 mjc Exp $ --> +<html> +<body> +Bindings that use sequences of primitive fields, or tuples. +</body> +</html> diff --git a/db/java/src/com/sleepycat/collections/CurrentTransaction.java b/db/java/src/com/sleepycat/collections/CurrentTransaction.java new file mode 100644 index 000000000..1876f26fc --- /dev/null +++ b/db/java/src/com/sleepycat/collections/CurrentTransaction.java @@ -0,0 +1,433 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: CurrentTransaction.java,v 1.4 2004/09/22 18:01:02 bostic Exp $ + */ + +package com.sleepycat.collections; + +import java.util.ArrayList; +import java.util.List; +import java.util.WeakHashMap; + +import com.sleepycat.compat.DbCompat; +import com.sleepycat.db.Cursor; +import com.sleepycat.db.CursorConfig; +import com.sleepycat.db.Database; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.Environment; +import com.sleepycat.db.EnvironmentConfig; +import com.sleepycat.db.LockMode; +import com.sleepycat.db.Transaction; +import com.sleepycat.db.TransactionConfig; +import com.sleepycat.util.RuntimeExceptionWrapper; + +/** + * Provides access to the current transaction for the current thread within the + * context of a Berkeley DB environment. This class provides explicit + * transaction control beyond that provided by the {@link TransactionRunner} + * class. However, both methods of transaction control manage per-thread + * transactions. + * + * @author Mark Hayes + */ +public class CurrentTransaction { + + /* For internal use, this class doubles as an Environment wrapper. */ + + private static WeakHashMap envMap = new WeakHashMap(); + + private LockMode writeLockMode; + private boolean cdbMode; + private boolean txnMode; + private Environment env; + private ThreadLocal localTrans = new ThreadLocal(); + private ThreadLocal localCdbCursors; + + /** + * Gets the CurrentTransaction accessor for a specified Berkeley DB + * environment. This method always returns the same reference when called + * more than once with the same environment parameter. + * + * @param env is an open Berkeley DB environment. + * + * @return the CurrentTransaction accessor for the given environment, or + * null if the environment is not transactional. + */ + public static CurrentTransaction getInstance(Environment env) { + + CurrentTransaction currentTxn = getInstanceInternal(env); + return currentTxn.isTxnMode() ? currentTxn : null; + } + + /** + * Gets the CurrentTransaction accessor for a specified Berkeley DB + * environment. Unlike getInstance(), this method never returns null. + * + * @param env is an open Berkeley DB environment. + */ + static CurrentTransaction getInstanceInternal(Environment env) { + synchronized (envMap) { + CurrentTransaction myEnv = (CurrentTransaction) envMap.get(env); + if (myEnv == null) { + myEnv = new CurrentTransaction(env); + envMap.put(env, myEnv); + } + return myEnv; + } + } + + private CurrentTransaction(Environment env) { + this.env = env; + try { + EnvironmentConfig config = env.getConfig(); + txnMode = config.getTransactional(); + if (txnMode || DbCompat.getInitializeLocking(config)) { + writeLockMode = LockMode.RMW; + } else { + writeLockMode = LockMode.DEFAULT; + } + cdbMode = DbCompat.getInitializeCDB(config); + if (cdbMode) { + localCdbCursors = new ThreadLocal(); + } + } catch (DatabaseException e) { + throw new RuntimeExceptionWrapper(e); + } + } + + /** + * Returns whether this is a transactional environment. + */ + final boolean isTxnMode() { + + return txnMode; + } + + /** + * Returns whether this is a Concurrent Data Store environment. + */ + final boolean isCdbMode() { + + return cdbMode; + } + + /** + * Return the LockMode.RMW or null, depending on whether locking is + * enabled. LockMode.RMW will cause an error if passed when locking + * is not enabled. Locking is enabled if locking or transactions were + * specified for this environment. + */ + final LockMode getWriteLockMode() { + + return writeLockMode; + } + + /** + * Returns the underlying Berkeley DB environment. + */ + public final Environment getEnvironment() { + + return env; + } + + /** + * Returns the transaction associated with the current thread for this + * environment, or null if no transaction is active. + */ + public final Transaction getTransaction() { + + Trans trans = (Trans) localTrans.get(); + return (trans != null) ? trans.txn : null; + } + + /** + * Begins a new transaction for this environment and associates it with + * the current thread. If a transaction is already active for this + * environment and thread, a nested transaction will be created. + * + * @param config the transaction configuration used for calling + * {@link Environment#beginTransaction}, or null to use the default + * configuration. + * + * @return the new transaction. + * + * @throws DatabaseException if the transaction cannot be started, in which + * case any existing transaction is not affected. + * + * @throws IllegalStateException if a transaction is already active and + * nested transactions are not supported by the environment. + */ + public final Transaction beginTransaction(TransactionConfig config) + throws DatabaseException { + + Trans trans = (Trans) localTrans.get(); + if (trans != null) { + if (trans.txn != null) { + if (!DbCompat.NESTED_TRANSACTIONS) { + throw new IllegalStateException( + "Nested transactions are not supported"); + } + Transaction parentTxn = trans.txn; + trans = new Trans(trans, config); + trans.txn = env.beginTransaction(parentTxn, config); + localTrans.set(trans); + } else { + trans.txn = env.beginTransaction(null, config); + trans.config = config; + } + } else { + trans = new Trans(null, config); + trans.txn = env.beginTransaction(null, config); + localTrans.set(trans); + } + return trans.txn; + } + + /** + * Commits the transaction that is active for the current thread for this + * environment and makes the parent transaction (if any) the current + * transaction. + * + * @return the parent transaction or null if the committed transaction was + * not nested. + * + * @throws DatabaseException if an error occurs committing the transaction. + * The transaction will still be closed and the parent transaction will + * become the current transaction. + * + * @throws IllegalStateException if no transaction is active for the + * current thread for this environment. + */ + public final Transaction commitTransaction() + throws DatabaseException, IllegalStateException { + + Trans trans = (Trans) localTrans.get(); + if (trans != null && trans.txn != null) { + Transaction parent = closeTxn(trans); + trans.txn.commit(); + return parent; + } else { + throw new IllegalStateException("No transaction is active"); + } + } + + /** + * Aborts the transaction that is active for the current thread for this + * environment and makes the parent transaction (if any) the current + * transaction. + * + * @return the parent transaction or null if the aborted transaction was + * not nested. + * + * @throws DatabaseException if an error occurs aborting the transaction. + * The transaction will still be closed and the parent transaction will + * become the current transaction. + * + * @throws IllegalStateException if no transaction is active for the + * current thread for this environment. + */ + public final Transaction abortTransaction() + throws DatabaseException, IllegalStateException { + + Trans trans = (Trans) localTrans.get(); + if (trans != null && trans.txn != null) { + Transaction parent = closeTxn(trans); + trans.txn.abort(); + return parent; + } else { + throw new IllegalStateException("No transaction is active"); + } + } + + /** + * Returns whether the current transaction is a dirtyRead transaction. + */ + final boolean isDirtyRead() { + + Trans trans = (Trans) localTrans.get(); + if (trans != null && trans.config != null) { + return trans.config.getDirtyRead(); + } else { + return false; + } + } + + private Transaction closeTxn(Trans trans) { + + localTrans.set(trans.parent); + return (trans.parent != null) ? trans.parent.txn : null; + } + + private static class Trans { + + private Trans parent; + private Transaction txn; + private TransactionConfig config; + + private Trans(Trans parent, TransactionConfig config) { + + this.parent = parent; + this.config = config; + } + } + + /** + * Opens a cursor for a given database, dup'ing an existing CDB cursor if + * one is open for the current thread. + */ + Cursor openCursor(Database db, boolean writeCursor, Transaction txn) + throws DatabaseException { + + if (cdbMode) { + CdbCursors cdbCursors = null; + WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get(); + if (cdbCursorsMap == null) { + cdbCursorsMap = new WeakHashMap(); + localCdbCursors.set(cdbCursorsMap); + } else { + cdbCursors = (CdbCursors) cdbCursorsMap.get(db); + } + if (cdbCursors == null) { + cdbCursors = new CdbCursors(); + cdbCursorsMap.put(db, cdbCursors); + } + List cursors; + CursorConfig config; + if (writeCursor) { + if (cdbCursors.readCursors.size() > 0) { + + /* + * Although CDB allows opening a write cursor when a read + * cursor is open, a self-deadlock will occur if a write is + * attempted for a record that is read-locked; we should + * avoid self-deadlocks at all costs + */ + throw new IllegalStateException( + "cannot open CDB write cursor when read cursor is open"); + } + cursors = cdbCursors.writeCursors; + config = new CursorConfig(); + DbCompat.setWriteCursor(config, true); + } else { + cursors = cdbCursors.readCursors; + config = null; + } + Cursor cursor; + if (cursors.size() > 0) { + Cursor other = ((Cursor) cursors.get(0)); + cursor = other.dup(false); + } else { + cursor = db.openCursor(null, config); + } + cursors.add(cursor); + return cursor; + } else { + return db.openCursor(txn, null); + } + } + + /** + * Duplicates a cursor for a given database. + * + * @param writeCursor true to open a write cursor in a CDB environment, and + * ignored for other environments. + * + * @param samePosition is passed through to Cursor.dup(). + * + * @return the open cursor. + * + * @throws DatabaseException if a database problem occurs. + */ + Cursor dupCursor(Cursor cursor, boolean writeCursor, boolean samePosition) + throws DatabaseException { + + if (cdbMode) { + WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get(); + if (cdbCursorsMap != null) { + Database db = cursor.getDatabase(); + CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db); + if (cdbCursors != null) { + List cursors = writeCursor ? cdbCursors.writeCursors + : cdbCursors.readCursors; + if (cursors.contains(cursor)) { + Cursor newCursor = cursor.dup(samePosition); + cursors.add(newCursor); + return newCursor; + } + } + } + throw new IllegalStateException("cursor to dup not tracked"); + } else { + return cursor.dup(samePosition); + } + } + + /** + * Closes a cursor. + * + * @param cursor the cursor to close. + * + * @throws DatabaseException if a database problem occurs. + */ + void closeCursor(Cursor cursor) + throws DatabaseException { + + if (cursor == null) { + return; + } + if (cdbMode) { + WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get(); + if (cdbCursorsMap != null) { + Database db = cursor.getDatabase(); + CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db); + if (cdbCursors != null) { + if (cdbCursors.readCursors.remove(cursor) || + cdbCursors.writeCursors.remove(cursor)) { + cursor.close(); + return; + } + } + } + throw new IllegalStateException( + "closing CDB cursor that was not known to be open"); + } else { + cursor.close(); + } + } + + /** + * Returns true if a CDB cursor is open and therefore a Database write + * operation should not be attempted since a self-deadlock may result. + */ + boolean isCDBCursorOpen(Database db) + throws DatabaseException { + + if (cdbMode) { + WeakHashMap cdbCursorsMap = (WeakHashMap) localCdbCursors.get(); + if (cdbCursorsMap != null) { + CdbCursors cdbCursors = (CdbCursors) cdbCursorsMap.get(db); + + /* + * FindBugs whines unnecessarily about a Null pointer + * dereference here. + */ + if (cdbCursors != null && + cdbCursors.readCursors.size() > 0 || + cdbCursors.writeCursors.size() > 0) { + return true; + } + } + } + return false; + } + + static final class CdbCursors { + + List writeCursors = new ArrayList(); + List readCursors = new ArrayList(); + } +} diff --git a/db/java/src/com/sleepycat/collections/DataCursor.java b/db/java/src/com/sleepycat/collections/DataCursor.java new file mode 100644 index 000000000..cc77e7a27 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/DataCursor.java @@ -0,0 +1,690 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: DataCursor.java,v 1.4 2004/09/22 18:01:02 bostic Exp $ + */ + +package com.sleepycat.collections; + +import com.sleepycat.compat.DbCompat; +import com.sleepycat.db.Cursor; +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.JoinConfig; +import com.sleepycat.db.JoinCursor; +import com.sleepycat.db.LockMode; +import com.sleepycat.db.OperationStatus; + +/** + * Represents a Berkeley DB cursor and adds support for indices, bindings and + * key ranges. + * + * <p>This class operates on a view and takes care of reading and updating + * indices, calling bindings, constraining access to a key range, etc.</p> + * + * @author Mark Hayes + */ +final class DataCursor implements Cloneable { + + private RangeCursor cursor; + private JoinCursor joinCursor; + private DataView view; + private KeyRange range; + private boolean writeAllowed; + private boolean dirtyRead; + private DatabaseEntry keyThang; + private DatabaseEntry valueThang; + private DatabaseEntry primaryKeyThang; + private DatabaseEntry otherThang; + private DataCursor[] indexCursorsToClose; + + /** + * Creates a cursor for a given view. + */ + DataCursor(DataView view, boolean writeAllowed) + throws DatabaseException { + + init(view, writeAllowed, null); + } + + /** + * Creates a cursor for a given view and single key range. + */ + DataCursor(DataView view, boolean writeAllowed, Object singleKey) + throws DatabaseException { + + init(view, writeAllowed, view.subRange(singleKey)); + } + + /** + * Creates a cursor for a given view and key range. + */ + DataCursor(DataView view, boolean writeAllowed, + Object beginKey, boolean beginInclusive, + Object endKey, boolean endInclusive) + throws DatabaseException { + + init(view, writeAllowed, + view.subRange(beginKey, beginInclusive, endKey, endInclusive)); + } + + /** + * Creates a join cursor. + */ + DataCursor(DataView view, DataCursor[] indexCursors, + JoinConfig joinConfig, boolean closeIndexCursors) + throws DatabaseException { + + if (view.isSecondary()) { + throw new IllegalArgumentException( + "The primary collection in a join must not be a secondary " + + "database"); + } + Cursor[] cursors = new Cursor[indexCursors.length]; + for (int i = 0; i < cursors.length; i += 1) { + cursors[i] = indexCursors[i].cursor.getCursor(); + } + joinCursor = view.db.join(cursors, joinConfig); + init(view, false, null); + if (closeIndexCursors) { + indexCursorsToClose = indexCursors; + } + } + + /** + * Clones a cursor preserving the current position. + */ + DataCursor cloneCursor() + throws DatabaseException { + + checkNoJoinCursor(); + + DataCursor o; + try { + o = (DataCursor) super.clone(); + } catch (CloneNotSupportedException neverHappens) { + return null; + } + + o.initThangs(); + KeyRange.copy(keyThang, o.keyThang); + KeyRange.copy(valueThang, o.valueThang); + if (primaryKeyThang != keyThang) { + KeyRange.copy(primaryKeyThang, o.primaryKeyThang); + } + + o.cursor = cursor.dup(true); + return o; + } + + /** + * Returns the internal range cursor. + */ + RangeCursor getCursor() { + return cursor; + } + + /** + * Constructor helper. + */ + private void init(DataView view, boolean writeAllowed, KeyRange range) + throws DatabaseException { + + this.view = view; + this.writeAllowed = writeAllowed && view.writeAllowed; + this.range = (range != null) ? range : view.range; + dirtyRead = view.dirtyReadEnabled; + + initThangs(); + + if (joinCursor == null) { + cursor = new RangeCursor(view, this.range, this.writeAllowed); + } + } + + /** + * Constructor helper. + */ + private void initThangs() + throws DatabaseException { + + keyThang = new DatabaseEntry(); + primaryKeyThang = view.isSecondary() ? (new DatabaseEntry()) + : keyThang; + valueThang = new DatabaseEntry(); + } + + /** + * Closes the associated cursor. + */ + void close() + throws DatabaseException { + + if (joinCursor != null) { + JoinCursor toClose = joinCursor; + joinCursor = null; + toClose.close(); + } + if (cursor != null) { + Cursor toClose = cursor.getCursor(); + cursor = null; + view.currentTxn.closeCursor(toClose ); + } + if (indexCursorsToClose != null) { + DataCursor[] toClose = indexCursorsToClose; + indexCursorsToClose = null; + for (int i = 0; i < toClose.length; i += 1) { + toClose[i].close(); + } + } + } + + /** + * Returns the view for this cursor. + */ + DataView getView() { + + return view; + } + + /** + * Returns the range for this cursor. + */ + KeyRange getRange() { + + return range; + } + + /** + * Returns whether write is allowed for this cursor, as specified to the + * constructor. + */ + boolean isWriteAllowed() { + + return writeAllowed; + } + + /** + * Returns the key object for the last record read. + */ + Object getCurrentKey() + throws DatabaseException { + + if (view.keyBinding == null) { + throw new UnsupportedOperationException(); + } + return view.makeKey(keyThang); + } + + /** + * Returns the value object for the last record read. + */ + Object getCurrentValue() + throws DatabaseException { + + return view.makeValue(primaryKeyThang, valueThang); + } + + /** + * Returns whether record number access is allowed. + */ + boolean hasRecNumAccess() { + + return view.recNumAccess; + } + + /** + * Returns the record number for the last record read. + */ + int getCurrentRecordNumber() + throws DatabaseException { + + if (view.btreeRecNumDb) { + /* BTREE-RECNO access. */ + if (otherThang == null) { + otherThang = new DatabaseEntry(); + } + DbCompat.getCurrentRecordNumber(cursor.getCursor(), otherThang, + getLockMode(false)); + return DbCompat.getRecordNumber(otherThang); + } else { + /* QUEUE or RECNO database. */ + return DbCompat.getRecordNumber(keyThang); + } + } + + /** + * Binding version of Cursor.getCurrent(), no join cursor allowed. + */ + OperationStatus getCurrent(boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + return cursor.getCurrent(keyThang, primaryKeyThang, valueThang, + getLockMode(lockForWrite)); + } + + /** + * Binding version of Cursor.getFirst(), join cursor is allowed. + */ + OperationStatus getFirst(boolean lockForWrite) + throws DatabaseException { + + LockMode lockMode = getLockMode(lockForWrite); + if (joinCursor != null) { + return joinCursor.getNext(keyThang, valueThang, lockMode); + } else { + return cursor.getFirst(keyThang, primaryKeyThang, valueThang, + lockMode); + } + } + + /** + * Binding version of Cursor.getNext(), join cursor is allowed. + */ + OperationStatus getNext(boolean lockForWrite) + throws DatabaseException { + + LockMode lockMode = getLockMode(lockForWrite); + if (joinCursor != null) { + return joinCursor.getNext(keyThang, valueThang, lockMode); + } else { + return cursor.getNext(keyThang, primaryKeyThang, valueThang, + lockMode); + } + } + + /** + * Binding version of Cursor.getNext(), join cursor is allowed. + */ + OperationStatus getNextNoDup(boolean lockForWrite) + throws DatabaseException { + + LockMode lockMode = getLockMode(lockForWrite); + if (joinCursor != null) { + return joinCursor.getNext(keyThang, valueThang, lockMode); + } else { + return cursor.getNextNoDup(keyThang, primaryKeyThang, valueThang, + lockMode); + } + } + + /** + * Binding version of Cursor.getNextDup(), no join cursor allowed. + */ + OperationStatus getNextDup(boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + return cursor.getNextDup(keyThang, primaryKeyThang, valueThang, + getLockMode(lockForWrite)); + } + + /** + * Binding version of Cursor.getLast(), no join cursor allowed. + */ + OperationStatus getLast(boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + return cursor.getLast(keyThang, primaryKeyThang, valueThang, + getLockMode(lockForWrite)); + } + + /** + * Binding version of Cursor.getPrev(), no join cursor allowed. + */ + OperationStatus getPrev(boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + return cursor.getPrev(keyThang, primaryKeyThang, valueThang, + getLockMode(lockForWrite)); + } + + /** + * Binding version of Cursor.getPrevNoDup(), no join cursor allowed. + */ + OperationStatus getPrevNoDup(boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + return cursor.getPrevNoDup(keyThang, primaryKeyThang, valueThang, + getLockMode(lockForWrite)); + } + + /** + * Binding version of Cursor.getPrevDup(), no join cursor allowed. + */ + OperationStatus getPrevDup(boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + return cursor.getPrevDup(keyThang, primaryKeyThang, valueThang, + getLockMode(lockForWrite)); + } + + /** + * Binding version of Cursor.getSearchKey(), no join cursor allowed. + * Searches by record number in a BTREE-RECNO db with RECNO access. + */ + OperationStatus getSearchKey(Object key, Object value, + boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + if (view.useKey(key, value, keyThang, range)) { + return doGetSearchKey(lockForWrite); + } else { + return OperationStatus.NOTFOUND; + } + } + + /** + * Pass-thru version of Cursor.getSearchKey(). + * Searches by record number in a BTREE-RECNO db with RECNO access. + */ + private OperationStatus doGetSearchKey(boolean lockForWrite) + throws DatabaseException { + + LockMode lockMode = getLockMode(lockForWrite); + if (view.btreeRecNumAccess) { + return cursor.getSearchRecordNumber(keyThang, primaryKeyThang, + valueThang, lockMode); + } else { + return cursor.getSearchKey(keyThang, primaryKeyThang, + valueThang, lockMode); + } + } + + /** + * Binding version of Cursor.getSearchKeyRange(), no join cursor allowed. + */ + OperationStatus getSearchKeyRange(Object key, Object value, + boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + if (view.useKey(key, value, keyThang, range)) { + return cursor.getSearchKeyRange(keyThang, primaryKeyThang, + valueThang, + getLockMode(lockForWrite)); + } else { + return OperationStatus.NOTFOUND; + } + } + + /** + * Binding version of Cursor.getSearchBoth(), no join cursor allowed. + * Unlike SecondaryCursor.getSearchBoth, for a secondary this searches for + * the primary value not the primary key. + */ + OperationStatus getSearchBoth(Object key, Object value, + boolean lockForWrite) + throws DatabaseException { + + checkNoJoinCursor(); + LockMode lockMode = getLockMode(lockForWrite); + view.useValue(value, valueThang, null); + if (view.useKey(key, value, keyThang, range)) { + if (view.isSecondary()) { + if (otherThang == null) { + otherThang = new DatabaseEntry(); + } + OperationStatus status = cursor.getSearchKey(keyThang, + primaryKeyThang, + otherThang, + lockMode); + while (status == OperationStatus.SUCCESS) { + if (KeyRange.equalBytes(otherThang, valueThang)) { + break; + } + status = cursor.getNextDup(keyThang, primaryKeyThang, + otherThang, lockMode); + } + /* if status != SUCCESS set range cursor to invalid? */ + return status; + } else { + return cursor.getSearchBoth(keyThang, null, valueThang, + lockMode); + } + } else { + return OperationStatus.NOTFOUND; + } + } + + /** + * Find the given value using getSearchBoth if possible or a sequential + * scan otherwise, no join cursor allowed. + */ + OperationStatus find(Object value, boolean findFirst) + throws DatabaseException { + + checkNoJoinCursor(); + LockMode lockMode = getLockMode(false); + + if (view.entityBinding != null && !view.isSecondary() && + (findFirst || !view.dupsAllowed)) { + return getSearchBoth(null, value, false); + } else { + if (otherThang == null) { + otherThang = new DatabaseEntry(); + } + view.useValue(value, otherThang, null); + OperationStatus status = findFirst ? getFirst(false) + : getLast(false); + while (status == OperationStatus.SUCCESS) { + if (KeyRange.equalBytes(valueThang, otherThang)) { + break; + } + status = findFirst ? getNext(false) : getPrev(false); + } + return status; + } + } + + /** + * Calls Cursor.count(), no join cursor allowed. + */ + int count() + throws DatabaseException { + + checkNoJoinCursor(); + return cursor.count(); + } + + /** + * Binding version of Cursor.putCurrent(). + */ + OperationStatus putCurrent(Object value) + throws DatabaseException { + + checkWriteAllowed(false); + view.useValue(value, valueThang, keyThang); + + /* + * Workaround for a DB core problem: With HASH type a put() with + * different data is allowed. + */ + boolean hashWorkaround = (view.dupsOrdered && !view.ordered); + if (hashWorkaround) { + if (otherThang == null) { + otherThang = new DatabaseEntry(); + } + cursor.getCurrent(keyThang, primaryKeyThang, otherThang, + LockMode.DEFAULT); + if (KeyRange.equalBytes(valueThang, otherThang)) { + return OperationStatus.SUCCESS; + } else { + throw new IllegalArgumentException( + "Current data differs from put data with sorted duplicates"); + } + } + + return cursor.putCurrent(valueThang); + } + + /** + * Binding version of Cursor.putAfter(). + */ + OperationStatus putAfter(Object value) + throws DatabaseException { + + checkWriteAllowed(false); + view.useValue(value, valueThang, null); /* why no key check? */ + return cursor.putAfter(valueThang); + } + + /** + * Binding version of Cursor.putBefore(). + */ + OperationStatus putBefore(Object value) + throws DatabaseException { + + checkWriteAllowed(false); + view.useValue(value, valueThang, keyThang); + return cursor.putBefore(valueThang); + } + + /** + * Binding version of Cursor.put(), optionally returning the old value and + * optionally using the current key instead of the key parameter. + */ + OperationStatus put(Object key, Object value, Object[] oldValue, + boolean useCurrentKey) + throws DatabaseException { + + initForPut(key, value, oldValue, useCurrentKey); + return cursor.put(keyThang, valueThang); + } + + /** + * Binding version of Cursor.putNoOverwrite(), optionally using the current + * key instead of the key parameter. + */ + OperationStatus putNoOverwrite(Object key, Object value, + boolean useCurrentKey) + throws DatabaseException { + + initForPut(key, value, null, useCurrentKey); + return cursor.putNoOverwrite(keyThang, valueThang); + } + + /** + * Binding version of Cursor.putNoDupData(), optionally returning the old + * value and optionally using the current key instead of the key parameter. + */ + OperationStatus putNoDupData(Object key, Object value, Object[] oldValue, + boolean useCurrentKey) + throws DatabaseException { + + initForPut(key, value, oldValue, useCurrentKey); + if (view.dupsOrdered) { + return cursor.putNoDupData(keyThang, valueThang); + } else { + if (view.dupsAllowed) { + /* Unordered duplicates. */ + OperationStatus status = + cursor.getSearchBoth(keyThang, primaryKeyThang, + valueThang, + getLockMode(false)); + if (status == OperationStatus.SUCCESS) { + return OperationStatus.KEYEXIST; + } else { + return cursor.put(keyThang, valueThang); + } + } else { + /* No duplicates. */ + return cursor.putNoOverwrite(keyThang, valueThang); + } + } + } + + /** + * Do setup for a put() operation. + */ + private void initForPut(Object key, Object value, Object[] oldValue, + boolean useCurrentKey) + throws DatabaseException { + + checkWriteAllowed(false); + if (!useCurrentKey && !view.useKey(key, value, keyThang, range)) { + throw new IllegalArgumentException("key out of range"); + } + if (oldValue != null) { + oldValue[0] = null; + if (!view.dupsAllowed) { + OperationStatus status = doGetSearchKey(true); + if (status == OperationStatus.SUCCESS) { + oldValue[0] = getCurrentValue(); + } + } + } + view.useValue(value, valueThang, keyThang); + } + + /** + * Sets the key entry to the begin key of a single key range, so the next + * time a putXxx() method is called that key will be used. + */ + void useRangeKey() { + if (!range.singleKey) { + throw new IllegalStateException(); + } + KeyRange.copy(range.beginKey, keyThang); + } + + /** + * Perform an arbitrary database 'delete' operation. + */ + OperationStatus delete() + throws DatabaseException { + + checkWriteAllowed(true); + return cursor.delete(); + } + + /** + * Returns the lock mode to use for a getXxx() operation. + */ + LockMode getLockMode(boolean lockForWrite) { + + /* Dirty-read takes precedence over write-locking. */ + + if (dirtyRead) { + return LockMode.DIRTY_READ; + } else if (lockForWrite && !view.currentTxn.isDirtyRead()) { + return view.currentTxn.getWriteLockMode(); + } else { + return LockMode.DEFAULT; + } + } + + /** + * Throws an exception if a join cursor is in use. + */ + private void checkNoJoinCursor() { + + if (joinCursor != null) { + throw new UnsupportedOperationException + ("Not allowed with a join cursor"); + } + } + + /** + * Throws an exception if write is not allowed or if a join cursor is in + * use. + */ + private void checkWriteAllowed(boolean allowSecondary) { + + checkNoJoinCursor(); + + if (!writeAllowed || (!allowSecondary && view.isSecondary())) { + throw new UnsupportedOperationException + ("Writing is not allowed"); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/DataView.java b/db/java/src/com/sleepycat/collections/DataView.java new file mode 100644 index 000000000..e8cec80b3 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/DataView.java @@ -0,0 +1,598 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: DataView.java,v 1.4 2004/08/02 18:52:05 mjc Exp $ + */ + +package com.sleepycat.collections; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.compat.DbCompat; +import com.sleepycat.db.Database; +import com.sleepycat.db.DatabaseConfig; +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.Environment; +import com.sleepycat.db.JoinConfig; +import com.sleepycat.db.OperationStatus; +import com.sleepycat.db.SecondaryConfig; +import com.sleepycat.db.SecondaryDatabase; +import com.sleepycat.db.SecondaryKeyCreator; +import com.sleepycat.db.Transaction; +import com.sleepycat.util.RuntimeExceptionWrapper; + +/** + * Represents a Berkeley DB database and adds support for indices, bindings and + * key ranges. + * + * <p>This class defines a view and takes care of reading and updating indices, + * calling bindings, constraining access to a key range, etc.</p> + * + * @author Mark Hayes + */ +final class DataView implements Cloneable { + + Database db; + SecondaryDatabase secDb; + CurrentTransaction currentTxn; + KeyRange range; + EntryBinding keyBinding; + EntryBinding valueBinding; + EntityBinding entityBinding; + PrimaryKeyAssigner keyAssigner; + SecondaryKeyCreator secKeyCreator; + boolean writeAllowed; // Read-write view + boolean ordered; // Not a HASH Db + boolean recNumAllowed; // QUEUE, RECNO, or BTREE-RECNUM Db + boolean recNumAccess; // recNumAllowed && using a rec num binding + boolean btreeRecNumDb; // BTREE-RECNUM Db + boolean btreeRecNumAccess; // recNumAccess && BTREE-RECNUM Db + boolean recNumRenumber; // RECNO-RENUM Db + boolean keysRenumbered; // recNumRenumber || btreeRecNumAccess + boolean dupsAllowed; // Dups configured + boolean dupsOrdered; // Sorted dups configured + boolean transactional; // Db is transactional + boolean dirtyReadAllowed; // Dirty-read is optional in DB-CORE + boolean dirtyReadEnabled; // This view is a dirty-ready view + + /** + * Creates a view for a given database and bindings. The initial key range + * of the view will be open. + */ + DataView(Database database, EntryBinding keyBinding, + EntryBinding valueBinding, EntityBinding entityBinding, + boolean writeAllowed, PrimaryKeyAssigner keyAssigner) + throws IllegalArgumentException { + + if (database == null) { + throw new IllegalArgumentException("database is null"); + } + db = database; + try { + currentTxn = + CurrentTransaction.getInstanceInternal(db.getEnvironment()); + DatabaseConfig dbConfig; + if (db instanceof SecondaryDatabase) { + secDb = (SecondaryDatabase) database; + SecondaryConfig secConfig = secDb.getSecondaryConfig(); + secKeyCreator = secConfig.getKeyCreator(); + dbConfig = secConfig; + } else { + dbConfig = db.getConfig(); + } + ordered = !DbCompat.isTypeHash(dbConfig); + recNumAllowed = DbCompat.isTypeQueue(dbConfig) || + DbCompat.isTypeRecno(dbConfig) || + DbCompat.getBtreeRecordNumbers(dbConfig); + recNumRenumber = DbCompat.getRenumbering(dbConfig); + dupsAllowed = DbCompat.getSortedDuplicates(dbConfig) || + DbCompat.getUnsortedDuplicates(dbConfig); + dupsOrdered = DbCompat.getSortedDuplicates(dbConfig); + transactional = currentTxn.isTxnMode() && + dbConfig.getTransactional(); + dirtyReadAllowed = DbCompat.getDirtyRead(dbConfig); + btreeRecNumDb = recNumAllowed && DbCompat.isTypeBtree(dbConfig); + range = new KeyRange(dbConfig.getBtreeComparator()); + } catch (DatabaseException e) { + throw new RuntimeExceptionWrapper(e); + } + this.writeAllowed = writeAllowed; + this.keyBinding = keyBinding; + this.valueBinding = valueBinding; + this.entityBinding = entityBinding; + this.keyAssigner = keyAssigner; + + if (valueBinding != null && entityBinding != null) + throw new IllegalArgumentException( + "both valueBinding and entityBinding are non-null"); + + if (keyBinding instanceof com.sleepycat.bind.RecordNumberBinding) { + if (!recNumAllowed) { + throw new IllegalArgumentException( + "RecordNumberBinding requires DB_BTREE/DB_RECNUM, " + + "DB_RECNO, or DB_QUEUE"); + } + recNumAccess = true; + if (btreeRecNumDb) { + btreeRecNumAccess = true; + } + } + keysRenumbered = recNumRenumber || btreeRecNumAccess; + } + + /** + * Clones the view. + */ + private DataView cloneView() { + + try { + return (DataView) super.clone(); + } catch (CloneNotSupportedException willNeverOccur) { + throw new IllegalStateException(); + } + } + + /** + * Return a new key-set view derived from this view by setting the + * entity and value binding to null. + * + * @return the derived view. + */ + DataView keySetView() { + + if (keyBinding == null) { + throw new UnsupportedOperationException("must have keyBinding"); + } + DataView view = cloneView(); + view.valueBinding = null; + view.entityBinding = null; + return view; + } + + /** + * Return a new value-set view derived from this view by setting the + * key binding to null. + * + * @return the derived view. + */ + DataView valueSetView() { + + if (valueBinding == null && entityBinding == null) { + throw new UnsupportedOperationException( + "must have valueBinding or entityBinding"); + } + DataView view = cloneView(); + view.keyBinding = null; + return view; + } + + /** + * Return a new value-set view for single key range. + * + * @param singleKey the single key value. + * + * @return the derived view. + * + * @throws DatabaseException if a database problem occurs. + * + * @throws KeyRangeException if the specified range is not within the + * current range. + */ + DataView valueSetView(Object singleKey) + throws DatabaseException, KeyRangeException { + + /* + * Must do subRange before valueSetView since the latter clears the + * key binding needed for the former. + */ + KeyRange singleKeyRange = subRange(singleKey); + DataView view = valueSetView(); + view.range = singleKeyRange; + return view; + } + + /** + * Return a new value-set view for key range, optionally changing + * the key binding. + */ + DataView subView(Object beginKey, boolean beginInclusive, + Object endKey, boolean endInclusive, + EntryBinding keyBinding) + throws DatabaseException, KeyRangeException { + + DataView view = cloneView(); + view.setRange(beginKey, beginInclusive, endKey, endInclusive); + if (keyBinding != null) view.keyBinding = keyBinding; + return view; + } + + /** + * Returns a new view with a specified dirtyRead setting. + */ + DataView dirtyReadView(boolean enable) { + + if (!dirtyReadAllowed) + return this; + DataView view = cloneView(); + view.dirtyReadEnabled = enable; + return view; + } + + /** + * Returns the current transaction for the view or null if the environment + * is non-transactional. + */ + CurrentTransaction getCurrentTxn() { + + return transactional ? currentTxn : null; + } + + /** + * Sets this view's range to a subrange with the given parameters. + */ + private void setRange(Object beginKey, boolean beginInclusive, + Object endKey, boolean endInclusive) + throws DatabaseException, KeyRangeException { + + range = subRange(beginKey, beginInclusive, endKey, endInclusive); + } + + /** + * Returns the key thang for a single key range, or null if a single key + * range is not used. + */ + DatabaseEntry getSingleKeyThang() { + + return range.getSingleKey(); + } + + /** + * Returns the environment for the database. + */ + final Environment getEnv() { + + return currentTxn.getEnvironment(); + } + + /** + * Returns whether this is a view on a secondary database rather + * than directly on a primary database. + */ + final boolean isSecondary() { + + return (secDb != null); + } + + /** + * Returns whether no records are present in the view. + */ + boolean isEmpty() + throws DatabaseException { + + DataCursor cursor = new DataCursor(this, false); + try { + return cursor.getFirst(false) != OperationStatus.SUCCESS; + } finally { + cursor.close(); + } + } + + /** + * Appends a value and returns the new key. If a key assigner is used + * it assigns the key, otherwise a QUEUE or RECNO database is required. + */ + OperationStatus append(Object value, Object[] retPrimaryKey, + Object[] retValue) + throws DatabaseException { + + /* + * Flags will be NOOVERWRITE if used with assigner, or APPEND + * otherwise. + * Requires: if value param, value or entity binding + * Requires: if retPrimaryKey, primary key binding (no index). + * Requires: if retValue, value or entity binding + */ + DatabaseEntry keyThang = new DatabaseEntry(); + DatabaseEntry valueThang = new DatabaseEntry(); + useValue(value, valueThang, null); + OperationStatus status; + if (keyAssigner != null) { + keyAssigner.assignKey(keyThang); + if (!range.check(keyThang)) { + throw new IllegalArgumentException( + "assigned key out of range"); + } + DataCursor cursor = new DataCursor(this, true); + try { + status = cursor.getCursor().putNoOverwrite(keyThang, + valueThang); + } finally { + cursor.close(); + } + } else { + /* Assume QUEUE/RECNO access method. */ + if (currentTxn.isCDBCursorOpen(db)) { + throw new IllegalStateException( + "cannot open CDB write cursor when read cursor is open"); + } + status = DbCompat.append(db, useTransaction(), + keyThang, valueThang); + if (status == OperationStatus.SUCCESS && !range.check(keyThang)) { + db.delete(useTransaction(), keyThang); + throw new IllegalArgumentException( + "appended record number out of range"); + } + } + if (status == OperationStatus.SUCCESS) { + returnPrimaryKeyAndValue(keyThang, valueThang, + retPrimaryKey, retValue); + } + return status; + } + + /** + * Returns the current transaction if the database is transaction, or null + * if the database is not transactional or there is no current transaction. + */ + Transaction useTransaction() { + return transactional ? currentTxn.getTransaction() : null; + } + + /** + * Deletes all records in the current range. + */ + void clear() + throws DatabaseException { + + DataCursor cursor = new DataCursor(this, true); + try { + OperationStatus status = OperationStatus.SUCCESS; + while (status == OperationStatus.SUCCESS) { + if (keysRenumbered) { + status = cursor.getFirst(true); + } else { + status = cursor.getNext(true); + } + if (status == OperationStatus.SUCCESS) { + cursor.delete(); + } + } + } finally { + cursor.close(); + } + } + + /** + * Returns a cursor for this view that reads only records having the + * specified index key values. + */ + DataCursor join(DataView[] indexViews, Object[] indexKeys, + JoinConfig joinConfig) + throws DatabaseException { + + DataCursor joinCursor = null; + DataCursor[] indexCursors = new DataCursor[indexViews.length]; + try { + for (int i = 0; i < indexViews.length; i += 1) { + indexCursors[i] = new DataCursor(indexViews[i], false); + indexCursors[i].getSearchKey(indexKeys[i], null, false); + } + joinCursor = new DataCursor(this, indexCursors, joinConfig, true); + return joinCursor; + } finally { + if (joinCursor == null) { + // An exception is being thrown, so close cursors we opened. + for (int i = 0; i < indexCursors.length; i += 1) { + if (indexCursors[i] != null) { + try { indexCursors[i].close(); } + catch (Exception e) { + /* FindBugs, this is ok. */ + } + } + } + } + } + } + + /** + * Returns a cursor for this view that reads only records having the + * index key values at the specified cursors. + */ + DataCursor join(DataCursor[] indexCursors, JoinConfig joinConfig) + throws DatabaseException { + + return new DataCursor(this, indexCursors, joinConfig, false); + } + + /** + * Returns primary key and value if return parameters are non-null. + */ + private void returnPrimaryKeyAndValue(DatabaseEntry keyThang, + DatabaseEntry valueThang, + Object[] retPrimaryKey, + Object[] retValue) + throws DatabaseException { + + // Requires: if retPrimaryKey, primary key binding (no index). + // Requires: if retValue, value or entity binding + + if (retPrimaryKey != null) { + if (keyBinding == null) { + throw new IllegalArgumentException( + "returning key requires primary key binding"); + } else if (isSecondary()) { + throw new IllegalArgumentException( + "returning key requires unindexed view"); + } else { + retPrimaryKey[0] = keyBinding.entryToObject(keyThang); + } + } + if (retValue != null) { + retValue[0] = makeValue(keyThang, valueThang); + } + } + + /** + * Populates the key entry and returns whether the key is within range. + */ + boolean useKey(Object key, Object value, DatabaseEntry keyThang, + KeyRange checkRange) + throws DatabaseException { + + if (key != null) { + if (keyBinding == null) { + throw new IllegalArgumentException( + "non-null key with null key binding"); + } + keyBinding.objectToEntry(key, keyThang); + } else { + if (value == null) { + throw new IllegalArgumentException( + "null key and null value"); + } + if (entityBinding == null) { + throw new IllegalStateException( + "EntityBinding required to derive key from value"); + } + if (isSecondary()) { + DatabaseEntry primaryKeyThang = new DatabaseEntry(); + entityBinding.objectToKey(value, primaryKeyThang); + DatabaseEntry valueThang = new DatabaseEntry(); + entityBinding.objectToData(value, valueThang); + secKeyCreator.createSecondaryKey(secDb, primaryKeyThang, + valueThang, keyThang); + } else { + entityBinding.objectToKey(value, keyThang); + } + } + if (recNumAccess && DbCompat.getRecordNumber(keyThang) <= 0) { + return false; + } + if (checkRange != null && !checkRange.check(keyThang)) { + return false; + } + return true; + } + + /** + * Returns whether data keys can be derived from the value/entity binding + * of this view, which determines whether a value/entity object alone is + * sufficient for operations that require keys. + */ + final boolean canDeriveKeyFromValue() { + + return (entityBinding != null); + } + + /** + * Populates the value entry and throws an exception if the primary key + * would be changed via an entity binding. + */ + void useValue(Object value, DatabaseEntry valueThang, + DatabaseEntry checkKeyThang) + throws DatabaseException { + + if (value != null) { + if (valueBinding != null) { + valueBinding.objectToEntry(value, valueThang); + } else if (entityBinding != null) { + entityBinding.objectToData(value, valueThang); + if (checkKeyThang != null) { + DatabaseEntry thang = new DatabaseEntry(); + entityBinding.objectToKey(value, thang); + if (!KeyRange.equalBytes(thang, checkKeyThang)) { + throw new IllegalArgumentException( + "cannot change primary key"); + } + } + } else { + throw new IllegalArgumentException( + "non-null value with null value/entity binding"); + } + } else { + valueThang.setData(new byte[0]); + valueThang.setOffset(0); + valueThang.setSize(0); + } + } + + /** + * Converts a key entry to a key object. + */ + Object makeKey(DatabaseEntry keyThang) + throws DatabaseException { + + if (keyThang.getSize() == 0) return null; + return keyBinding.entryToObject(keyThang); + } + + /** + * Converts a key-value entry pair to a value object. + */ + Object makeValue(DatabaseEntry primaryKeyThang, DatabaseEntry valueThang) + throws DatabaseException { + + Object value; + if (valueBinding != null) { + value = valueBinding.entryToObject(valueThang); + } else if (entityBinding != null) { + value = entityBinding.entryToObject(primaryKeyThang, + valueThang); + } else { + throw new UnsupportedOperationException( + "requires valueBinding or entityBinding"); + } + return value; + } + + /** + * Intersects the given key and the current range. + */ + KeyRange subRange(Object singleKey) + throws DatabaseException, KeyRangeException { + + return range.subRange(makeRangeKey(singleKey)); + } + + /** + * Intersects the given range and the current range. + */ + KeyRange subRange(Object beginKey, boolean beginInclusive, + Object endKey, boolean endInclusive) + throws DatabaseException, KeyRangeException { + + if (beginKey == endKey && beginInclusive && endInclusive) { + return subRange(beginKey); + } + if (!ordered) { + throw new UnsupportedOperationException( + "Cannot use key ranges on an unsorted database"); + } + DatabaseEntry beginThang = + (beginKey != null) ? makeRangeKey(beginKey) : null; + DatabaseEntry endThang = + (endKey != null) ? makeRangeKey(endKey) : null; + + return range.subRange(beginThang, beginInclusive, + endThang, endInclusive); + } + + /** + * Given a key object, make a key entry that can be used in a range. + */ + private DatabaseEntry makeRangeKey(Object key) + throws DatabaseException { + + DatabaseEntry thang = new DatabaseEntry(); + if (keyBinding != null) { + useKey(key, null, thang, null); + } else { + useKey(null, key, thang, null); + } + return thang; + } +} diff --git a/db/java/src/com/sleepycat/collections/KeyRange.java b/db/java/src/com/sleepycat/collections/KeyRange.java new file mode 100644 index 000000000..8a5b7d3b2 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/KeyRange.java @@ -0,0 +1,299 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: KeyRange.java,v 1.2 2004/05/05 15:43:48 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Comparator; + +import com.sleepycat.db.DatabaseEntry; + +/** + * Encapsulates a key range for use with a RangeCursor. + */ +class KeyRange { + + Comparator comparator; + DatabaseEntry beginKey; + DatabaseEntry endKey; + boolean singleKey; + boolean beginInclusive; + boolean endInclusive; + + /** + * Creates an unconstrained key range. + */ + KeyRange(Comparator comparator) { + this.comparator = comparator; + } + + /** + * Creates a range for a single key. + */ + KeyRange subRange(DatabaseEntry key) + throws KeyRangeException { + + if (!check(key)) { + throw new KeyRangeException("singleKey out of range"); + } + KeyRange range = new KeyRange(comparator); + range.beginKey = key; + range.endKey = key; + range.beginInclusive = true; + range.endInclusive = true; + range.singleKey = true; + return range; + } + + /** + * Creates a range that is the intersection of this range and the given + * range parameters. + */ + KeyRange subRange(DatabaseEntry beginKey, boolean beginInclusive, + DatabaseEntry endKey, boolean endInclusive) + throws KeyRangeException { + + if (beginKey == null) { + beginKey = this.beginKey; + beginInclusive = this.beginInclusive; + } else if (!check(beginKey, beginInclusive)) { + throw new KeyRangeException("beginKey out of range"); + } + if (endKey == null) { + endKey = this.endKey; + endInclusive = this.endInclusive; + } else if (!check(endKey, endInclusive)) { + throw new KeyRangeException("endKey out of range"); + } + KeyRange range = new KeyRange(comparator); + range.beginKey = beginKey; + range.endKey = endKey; + range.beginInclusive = beginInclusive; + range.endInclusive = endInclusive; + return range; + } + + /** + * Returns the key of a single-key range, or null if not a single-key + * range. + */ + final DatabaseEntry getSingleKey() { + + return singleKey ? beginKey : null; + } + + /** + * Returns whether this range has a begin or end bound. + */ + final boolean hasBound() { + + return endKey != null || beginKey != null; + } + + /** + * Formats this range as a string for debugging. + */ + public String toString() { + + return "[KeyRange " + beginKey + ' ' + beginInclusive + + endKey + ' ' + endInclusive + + (singleKey ? " single" : ""); + } + + /** + * Returns whether a given key is within range. + */ + boolean check(DatabaseEntry key) { + + if (singleKey) { + return (compare(key, beginKey) == 0); + } else { + return checkBegin(key, true) && checkEnd(key, true); + } + } + + /** + * Returns whether a given key is within range. + */ + boolean check(DatabaseEntry key, boolean inclusive) { + + if (singleKey) { + return (compare(key, beginKey) == 0); + } else { + return checkBegin(key, inclusive) && checkEnd(key, inclusive); + } + } + + /** + * Returns whether the given key is within range with respect to the + * beginning of the range. + * + * <p>The inclusive parameter should be true for checking a key read from + * the database; this will require that the key is within range. When + * inclusive=false the key is allowed to be equal to the beginKey for the + * range; this is used for checking a new exclusive bound of a + * sub-range.</p> + * + * <p>Note that when inclusive=false and beginInclusive=true our check is + * not exactly correct because in theory we should allow the key to be "one + * less" than the existing bound; however, checking for "one less" is + * impossible so we do the best we can and test the bounds + * conservatively.</p> + */ + boolean checkBegin(DatabaseEntry key, boolean inclusive) { + + if (beginKey == null) { + return true; + } else if (!beginInclusive && inclusive) { + return compare(key, beginKey) > 0; + } else { + return compare(key, beginKey) >= 0; + } + } + + /** + * Returns whether the given key is within range with respect to the + * end of the range. See checkBegin for details. + */ + boolean checkEnd(DatabaseEntry key, boolean inclusive) { + + if (endKey == null) { + return true; + } else if (!endInclusive && inclusive) { + return compare(key, endKey) < 0; + } else { + return compare(key, endKey) <= 0; + } + } + + /** + * Compares two keys, using the user comparator if there is one. + */ + int compare(DatabaseEntry key1, DatabaseEntry key2) { + + if (comparator != null) { + return comparator.compare(getByteArray(key1), getByteArray(key2)); + } else { + return compareBytes + (key1.getData(), key1.getOffset(), key1.getSize(), + key2.getData(), key2.getOffset(), key2.getSize()); + + } + } + + /** + * Compares two keys as unsigned byte arrays, which is the default + * comparison used by JE/DB. + */ + static int compareBytes(byte[] data1, int offset1, int size1, + byte[] data2, int offset2, int size2) { + + for (int i = 0; i < size1 && i < size2; i++) { + + int b1 = 0xFF & data1[offset1 + i]; + int b2 = 0xFF & data2[offset2 + i]; + if (b1 < b2) + return -1; + else if (b1 > b2) + return 1; + } + + if (size1 < size2) + return -1; + else if (size1 > size2) + return 1; + else + return 0; + } + + /** + * Returns a copy of an entry. + */ + static DatabaseEntry copy(DatabaseEntry from) { + return new DatabaseEntry(getByteArray(from)); + } + + /** + * Copies one entry to another. + */ + static void copy(DatabaseEntry from, DatabaseEntry to) { + to.setData(getByteArray(from)); + to.setOffset(0); + } + + /** + * Returns an entry's byte array, copying it if the entry offset is + * non-zero. + */ + static byte[] getByteArray(DatabaseEntry entry) { + + byte[] bytes = entry.getData(); + if (bytes == null) return null; + int size = entry.getSize(); + byte[] data = new byte[size]; + System.arraycopy(bytes, entry.getOffset(), data, 0, size); + return data; + } + + /** + * Returns the two DatabaseEntry objects have the same data value. + */ + static boolean equalBytes(DatabaseEntry e1, DatabaseEntry e2) { + + if (e1 == null && e2 == null) { + return true; + } + if (e1 == null || e2 == null) { + return false; + } + + byte[] d1 = e1.getData(); + byte[] d2 = e2.getData(); + int s1 = e1.getSize(); + int s2 = e2.getSize(); + int o1 = e1.getOffset(); + int o2 = e2.getOffset(); + + if (d1 == null && d2 == null) { + return true; + } + if (d1 == null || d2 == null) { + return false; + } + if (s1 != s2) { + return false; + } + for (int i = 0; i < s1; i += 1) { + if (d1[o1 + i] != d2[o2 + i]) { + return false; + } + } + return true; + } + + /** + * Converts the byte array of this thang to space-separated integers, + * and suffixed by the record number if applicable. + * + * @param dbt the thang to convert. + * + * @param the resulting string. + */ + static String toString(DatabaseEntry dbt) { + + int len = dbt.getOffset() + dbt.getSize(); + StringBuffer buf = new StringBuffer(len * 2); + byte[] data = dbt.getData(); + for (int i = dbt.getOffset(); i < len; i++) { + String num = Integer.toHexString(data[i]); + if (num.length() < 2) buf.append('0'); + buf.append(num); + } + return buf.toString(); + } +} diff --git a/db/java/src/com/sleepycat/collections/KeyRangeException.java b/db/java/src/com/sleepycat/collections/KeyRangeException.java new file mode 100644 index 000000000..342ebd528 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/KeyRangeException.java @@ -0,0 +1,26 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: KeyRangeException.java,v 1.1 2004/04/09 16:34:08 mark Exp $ + */ + +package com.sleepycat.collections; + +/** + * An exception thrown when a key is out of range. + * + * @author Mark Hayes + */ +class KeyRangeException extends IllegalArgumentException { + + /** + * Creates a key range exception. + */ + public KeyRangeException(String msg) { + + super(msg); + } +} diff --git a/db/java/src/com/sleepycat/collections/MapEntryParameter.java b/db/java/src/com/sleepycat/collections/MapEntryParameter.java new file mode 100644 index 000000000..8148bbe8a --- /dev/null +++ b/db/java/src/com/sleepycat/collections/MapEntryParameter.java @@ -0,0 +1,126 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: MapEntryParameter.java,v 1.1 2004/04/09 16:34:08 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Map; + +/** + * A simple <code>Map.Entry</code> implementation that can be used as in + * input parameter. Since a <code>MapEntryParameter</code> is not obtained + * from a map, it is not attached to any map in particular. To emphasize that + * changing this object does not change the map, the {@link #setValue} method + * always throws <code>UnsupportedOperationException</code>. + * + * <p><b>Warning:</b> Use of this interface violates the Java Collections + * interface contract since these state that <code>Map.Entry</code> objects + * should only be obtained from <code>Map.entrySet()</code> sets, while this + * class allows constructing them directly. However, it is useful for + * performing operations on an entry set such as add(), contains(), etc. For + * restrictions see {@link #getValue} and {@link #setValue}.</p> + * + * @author Mark Hayes + */ +public class MapEntryParameter implements Map.Entry { + + private Object key; + private Object value; + + /** + * Creates a map entry with a given key and value. + * + * @param key is the key to use. + * + * @param value is the value to use. + */ + public MapEntryParameter(Object key, Object value) { + + this.key = key; + this.value = value; + } + + /** + * Computes a hash code as specified by {@link + * java.util.Map.Entry#hashCode}. + * + * @return the computed hash code. + */ + public int hashCode() { + + return ((key == null) ? 0 : key.hashCode()) ^ + ((value == null) ? 0 : value.hashCode()); + } + + /** + * Compares this entry to a given entry as specified by {@link + * java.util.Map.Entry#equals}. + * + * @return the computed hash code. + */ + public boolean equals(Object other) { + + if (!(other instanceof Map.Entry)) { + return false; + } + + Map.Entry e = (Map.Entry) other; + + return ((key == null) ? (e.getKey() == null) + : key.equals(e.getKey())) && + ((value == null) ? (e.getValue() == null) + : value.equals(e.getValue())); + } + + /** + * Returns the key of this entry. + * + * @return the key of this entry. + */ + public final Object getKey() { + + return key; + } + + /** + * Returns the value of this entry. Note that this will be the value + * passed to the constructor or the last value passed to {@link #setValue}. + * It will not reflect changes made to a Map. + * + * @return the value of this entry. + */ + public final Object getValue() { + + return value; + } + + /** + * Always throws <code>UnsupportedOperationException</code> since this + * object is not attached to a map. + */ + public Object setValue(Object newValue) { + + throw new UnsupportedOperationException(); + } + + final void setValueInternal(Object newValue) { + + this.value = newValue; + } + + /** + * Converts the entry to a string representation for debugging. + * + * @return the string representation. + */ + public String toString() { + + return "[key [" + key + "] value [" + value + ']'; + } +} + diff --git a/db/java/src/com/sleepycat/collections/PrimaryKeyAssigner.java b/db/java/src/com/sleepycat/collections/PrimaryKeyAssigner.java new file mode 100644 index 000000000..1af3ba1ae --- /dev/null +++ b/db/java/src/com/sleepycat/collections/PrimaryKeyAssigner.java @@ -0,0 +1,30 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: PrimaryKeyAssigner.java,v 1.2 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.collections; + +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; + +/** + * An interface implemented to assign new primary key values. + * An implementation of this interface is passed to the {@link StoredMap} + * or {@link StoredSortedMap} constructor to assign primary keys for that + * store. Key assignment occurs when <code>StoredMap.append()</code> is called. + * + * @author Mark Hayes + */ +public interface PrimaryKeyAssigner { + + /** + * Assigns a new primary key value into the given data buffer. + */ + void assignKey(DatabaseEntry keyData) + throws DatabaseException; +} diff --git a/db/java/src/com/sleepycat/collections/RangeCursor.java b/db/java/src/com/sleepycat/collections/RangeCursor.java new file mode 100644 index 000000000..ae664ef51 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/RangeCursor.java @@ -0,0 +1,874 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: RangeCursor.java,v 1.3 2004/09/22 18:01:02 bostic Exp $ + */ + +package com.sleepycat.collections; + +import com.sleepycat.compat.DbCompat; +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.Cursor; +import com.sleepycat.db.LockMode; +import com.sleepycat.db.OperationStatus; +import com.sleepycat.db.SecondaryCursor; + +/** + * A cursor-like interface that enforces a key range. The method signatures + * are actually those of SecondaryCursor, but the pKey parameter may be null. + * It was done this way to avoid doubling the number of methods. + * + * <p>This is not a general implementation of a range cursor and should not + * be used outside this package; however, it may evolve into a generally useful + * range cursor some day.</p> + * + * @author Mark Hayes + */ +class RangeCursor implements Cloneable { + + /** + * The cursor and secondary cursor are the same object. The secCursor is + * null if the database is not a secondary database. + */ + private Cursor cursor; + private SecondaryCursor secCursor; + private CurrentTransaction currentTxn; + private boolean writeAllowed; + + /** + * The range is always non-null, but may be unbounded meaning that it is + * open and not used. + */ + private KeyRange range; + + /** + * If the database is a RECNO or QUEUE database, we know its keys are + * record numbers. We treat a non-positive record number as out of bounds, + * that is, we return NOTFOUND rather than throwing + * IllegalArgumentException as would happen if we passed a non-positive + * record number into the DB cursor. This behavior is required by the + * collections interface. + */ + private boolean isRecnoOrQueue; + + /** + * The privXxx entries are used only when the range is bounded. We read + * into these private entries to avoid modifying the caller's entry + * parameters in the case where we read sucessfully but the key is out of + * range. In that case we return NOTFOUND and we want to leave the entry + * parameters unchanged. + */ + private DatabaseEntry privKey; + private DatabaseEntry privPKey; + private DatabaseEntry privData; + + /** + * The initialized flag is set to true whenever we sucessfully position the + * cursor. It is used to implement the getNext/Prev logic for doing a + * getFirst/Last when the cursor is not initialized. We can't rely on + * Cursor to do that for us, since if we position the underlying cursor + * sucessfully but the key is out of range, we have no way to set the + * underlying cursor to uninitialized. A range cursor always starts in + * the uninitialized state. + */ + private boolean initialized; + + /** + * Create a range cursor. + */ + RangeCursor(DataView view, KeyRange range, boolean writeAllowed) + throws DatabaseException { + + this.range = range; + this.writeAllowed = writeAllowed; + this.currentTxn = view.currentTxn; + isRecnoOrQueue = view.recNumAllowed && !view.btreeRecNumDb; + + cursor = currentTxn.openCursor(view.db, writeAllowed, + view.useTransaction()); + init(); + } + + /** + * Create a cloned range cursor. The caller must clone the underlying + * cursor before using this constructor, because cursor open/close is + * handled specially for CDS cursors outside this class. + */ + RangeCursor dup(boolean samePosition) + throws DatabaseException { + + try { + RangeCursor c = (RangeCursor) super.clone(); + c.cursor = currentTxn.dupCursor(cursor, writeAllowed, + samePosition); + c.init(); + return c; + } catch (CloneNotSupportedException neverHappens) { + return null; + } + } + + /** + * Used for opening and duping (cloning). + */ + private void init() { + + if (cursor instanceof SecondaryCursor) { + secCursor = (SecondaryCursor) cursor; + } else { + secCursor = null; + } + + if (range.hasBound()) { + privKey = new DatabaseEntry(); + privPKey = new DatabaseEntry(); + privData = new DatabaseEntry(); + } else { + privKey = null; + privPKey = null; + privData = null; + } + } + + /** + * Returns the underlying cursor. Used for cloning. + */ + Cursor getCursor() { + return cursor; + } + + /** + * When an unbounded range is used, this method is called to use the + * callers entry parameters directly, to avoid the extra step of copying + * between the private entries and the caller's entries. + */ + private void setParams(DatabaseEntry key, DatabaseEntry pKey, + DatabaseEntry data) { + privKey = key; + privPKey = pKey; + privData = data; + } + + /** + * Dups the cursor, sets the cursor and secCursor fields to the duped + * cursor, and returns the old cursor. Always call endOperation in a + * finally clause after calling beginOperation. + * + * <p>If the returned cursor == the cursor field, the cursor is + * uninitialized and was not duped; this case is handled correctly by + * endOperation.</p> + */ + private Cursor beginOperation() + throws DatabaseException { + + Cursor oldCursor = cursor; + if (initialized) { + cursor = currentTxn.dupCursor(cursor, writeAllowed, true); + if (secCursor != null) { + secCursor = (SecondaryCursor) cursor; + } + } else { + return cursor; + } + return oldCursor; + } + + /** + * If the operation succeded, leaves the duped cursor in place and closes + * the oldCursor. If the operation failed, moves the oldCursor back in + * place and closes the duped cursor. oldCursor may be null if + * beginOperation was not called, in cases where we don't need to dup + * the cursor. Always call endOperation when a successful operation ends, + * in order to set the initialized field. + */ + private void endOperation(Cursor oldCursor, OperationStatus status, + DatabaseEntry key, DatabaseEntry pKey, + DatabaseEntry data) + throws DatabaseException { + + if (status == OperationStatus.SUCCESS) { + if (oldCursor != null && oldCursor != cursor) { + currentTxn.closeCursor(oldCursor); + } + if (key != null) { + swapData(key, privKey); + } + if (pKey != null && secCursor != null) { + swapData(pKey, privPKey); + } + if (data != null) { + swapData(data, privData); + } + initialized = true; + } else { + if (oldCursor != null && oldCursor != cursor) { + currentTxn.closeCursor(cursor); + cursor = oldCursor; + if (secCursor != null) { + secCursor = (SecondaryCursor) cursor; + } + } + } + } + + /** + * Swaps the contents of the two entries. Used to return entry data to + * the caller when the operation was successful. + */ + static private void swapData(DatabaseEntry e1, DatabaseEntry e2) { + + byte[] d1 = e1.getData(); + int o1 = e1.getOffset(); + int s1 = e1.getSize(); + + e1.setData(e2.getData(), e2.getOffset(), e2.getSize()); + e2.setData(d1, o1, s1); + } + + /** + * Shares the same byte array, offset and size between two entries. + * Used when copying the entry data is not necessary because it is known + * that the underlying operation will not modify the entry, for example, + * with getSearchKey. + */ + static private void shareData(DatabaseEntry from, DatabaseEntry to) { + + if (from != null) { + to.setData(from.getData(), from.getOffset(), from.getSize()); + } + } + + public OperationStatus getFirst(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetFirst(lockMode); + endOperation(null, status, null, null, null); + return status; + } + if (range.singleKey) { + KeyRange.copy(range.beginKey, privKey); + status = doGetSearchKey(lockMode); + endOperation(null, status, key, pKey, data); + } else { + status = OperationStatus.NOTFOUND; + Cursor oldCursor = beginOperation(); + try { + if (range.beginKey == null) { + status = doGetFirst(lockMode); + } else { + KeyRange.copy(range.beginKey, privKey); + status = doGetSearchKeyRange(lockMode); + if (status == OperationStatus.SUCCESS && + !range.beginInclusive && + range.compare(privKey, range.beginKey) == 0) { + status = doGetNext(lockMode); + } + } + if (status == OperationStatus.SUCCESS && + !range.check(privKey)) { + status = OperationStatus.NOTFOUND; + } + } finally { + endOperation(oldCursor, status, key, pKey, data); + } + } + return status; + } + + public OperationStatus getLast(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status = OperationStatus.NOTFOUND; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetLast(lockMode); + endOperation(null, status, null, null, null); + return status; + } + Cursor oldCursor = beginOperation(); + try { + if (range.endKey == null) { + status = doGetLast(lockMode); + } else { + KeyRange.copy(range.endKey, privKey); + status = doGetSearchKeyRange(lockMode); + if (status == OperationStatus.SUCCESS) { + if (range.endInclusive && + range.compare(range.endKey, privKey) == 0) { + status = doGetNextNoDup(lockMode); + if (status == OperationStatus.SUCCESS) { + status = doGetPrev(lockMode); + } else { + status = doGetLast(lockMode); + } + } else { + status = doGetPrev(lockMode); + } + } else { + status = doGetLast(lockMode); + } + } + if (status == OperationStatus.SUCCESS && + !range.checkBegin(privKey, true)) { + status = OperationStatus.NOTFOUND; + } + } finally { + endOperation(oldCursor, status, key, pKey, data); + } + return status; + } + + public OperationStatus getNext(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status; + if (!initialized) { + return getFirst(key, pKey, data, lockMode); + } + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetNext(lockMode); + endOperation(null, status, null, null, null); + return status; + } + if (range.singleKey) { + status = doGetNextDup(lockMode); + endOperation(null, status, key, pKey, data); + } else { + status = OperationStatus.NOTFOUND; + Cursor oldCursor = beginOperation(); + try { + status = doGetNext(lockMode); + if (status == OperationStatus.SUCCESS && + !range.check(privKey)) { + status = OperationStatus.NOTFOUND; + } + } finally { + endOperation(oldCursor, status, key, pKey, data); + } + } + return status; + } + + public OperationStatus getNextNoDup(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status; + if (!initialized) { + return getFirst(key, pKey, data, lockMode); + } + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetNextNoDup(lockMode); + endOperation(null, status, null, null, null); + return status; + } + if (range.singleKey) { + status = OperationStatus.NOTFOUND; + } else { + status = OperationStatus.NOTFOUND; + Cursor oldCursor = beginOperation(); + try { + status = doGetNextNoDup(lockMode); + if (status == OperationStatus.SUCCESS && + !range.check(privKey)) { + status = OperationStatus.NOTFOUND; + } + } finally { + endOperation(oldCursor, status, key, pKey, data); + } + } + return status; + } + + public OperationStatus getPrev(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status; + if (!initialized) { + return getLast(key, pKey, data, lockMode); + } + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetPrev(lockMode); + endOperation(null, status, null, null, null); + return status; + } + if (range.singleKey) { + status = doGetPrevDup(lockMode); + endOperation(null, status, key, pKey, data); + } else { + status = OperationStatus.NOTFOUND; + Cursor oldCursor = beginOperation(); + try { + status = doGetPrev(lockMode); + if (status == OperationStatus.SUCCESS && + !range.check(privKey)) { + status = OperationStatus.NOTFOUND; + } + } finally { + endOperation(oldCursor, status, key, pKey, data); + } + } + return status; + } + + public OperationStatus getPrevNoDup(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status; + if (!initialized) { + return getLast(key, pKey, data, lockMode); + } + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetPrevNoDup(lockMode); + endOperation(null, status, null, null, null); + return status; + } + if (range.singleKey) { + status = OperationStatus.NOTFOUND; + } else { + status = OperationStatus.NOTFOUND; + Cursor oldCursor = beginOperation(); + try { + status = doGetPrevNoDup(lockMode); + if (status == OperationStatus.SUCCESS && + !range.check(privKey)) { + status = OperationStatus.NOTFOUND; + } + } finally { + endOperation(oldCursor, status, key, pKey, data); + } + } + return status; + } + + public OperationStatus getSearchKey(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetSearchKey(lockMode); + endOperation(null, status, null, null, null); + return status; + } + if (!range.check(key)) { + status = OperationStatus.NOTFOUND; + } else { + shareData(key, privKey); + status = doGetSearchKey(lockMode); + endOperation(null, status, key, pKey, data); + } + return status; + } + + public OperationStatus getSearchBoth(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetSearchBoth(lockMode); + endOperation(null, status, null, null, null); + return status; + } + if (!range.check(key)) { + status = OperationStatus.NOTFOUND; + } else { + shareData(key, privKey); + if (secCursor != null) { + shareData(pKey, privPKey); + } else { + shareData(data, privData); + } + status = doGetSearchBoth(lockMode); + endOperation(null, status, key, pKey, data); + } + return status; + } + + public OperationStatus getSearchKeyRange(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status = OperationStatus.NOTFOUND; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetSearchKeyRange(lockMode); + endOperation(null, status, null, null, null); + return status; + } + Cursor oldCursor = beginOperation(); + try { + shareData(key, privKey); + status = doGetSearchKeyRange(lockMode); + if (status == OperationStatus.SUCCESS && + !range.check(privKey)) { + status = OperationStatus.NOTFOUND; + } + } finally { + endOperation(oldCursor, status, key, pKey, data); + } + return status; + } + + public OperationStatus getSearchBothRange(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status = OperationStatus.NOTFOUND; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetSearchBothRange(lockMode); + endOperation(null, status, null, null, null); + return status; + } + Cursor oldCursor = beginOperation(); + try { + shareData(key, privKey); + if (secCursor != null) { + shareData(pKey, privPKey); + } else { + shareData(data, privData); + } + status = doGetSearchBothRange(lockMode); + if (status == OperationStatus.SUCCESS && + !range.check(privKey)) { + status = OperationStatus.NOTFOUND; + } + } finally { + endOperation(oldCursor, status, key, pKey, data); + } + return status; + } + + public OperationStatus getSearchRecordNumber(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + OperationStatus status; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetSearchRecordNumber(lockMode); + endOperation(null, status, null, null, null); + return status; + } + if (!range.check(key)) { + status = OperationStatus.NOTFOUND; + } else { + shareData(key, privKey); + status = doGetSearchRecordNumber(lockMode); + endOperation(null, status, key, pKey, data); + } + return status; + } + + public OperationStatus getNextDup(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + if (!initialized) { + throw new DatabaseException("Cursor not initialized"); + } + OperationStatus status; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetNextDup(lockMode); + endOperation(null, status, null, null, null); + } else { + status = doGetNextDup(lockMode); + endOperation(null, status, key, pKey, data); + } + return status; + } + + public OperationStatus getPrevDup(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + if (!initialized) { + throw new DatabaseException("Cursor not initialized"); + } + OperationStatus status; + if (!range.hasBound()) { + setParams(key, pKey, data); + status = doGetPrevDup(lockMode); + endOperation(null, status, null, null, null); + } else { + status = doGetPrevDup(lockMode); + endOperation(null, status, key, pKey, data); + } + return status; + } + + public OperationStatus getCurrent(DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + if (!initialized) { + throw new DatabaseException("Cursor not initialized"); + } + if (secCursor != null && pKey != null) { + return secCursor.getCurrent(key, pKey, data, lockMode); + } else { + return cursor.getCurrent(key, data, lockMode); + } + } + + /* + * Pass-thru methods. + */ + + public void close() + throws DatabaseException { + + currentTxn.closeCursor(cursor); + } + + public int count() + throws DatabaseException { + + return cursor.count(); + } + + public OperationStatus delete() + throws DatabaseException { + + return cursor.delete(); + } + + public OperationStatus put(DatabaseEntry key, DatabaseEntry data) + throws DatabaseException { + + return cursor.put(key, data); + } + + public OperationStatus putNoOverwrite(DatabaseEntry key, + DatabaseEntry data) + throws DatabaseException { + + return cursor.putNoOverwrite(key, data); + } + + public OperationStatus putNoDupData(DatabaseEntry key, DatabaseEntry data) + throws DatabaseException { + + return cursor.putNoDupData(key, data); + } + + public OperationStatus putCurrent(DatabaseEntry data) + throws DatabaseException { + + return cursor.putCurrent(data); + } + + public OperationStatus putAfter(DatabaseEntry data) + throws DatabaseException { + + return DbCompat.putAfter(cursor, data); + } + + public OperationStatus putBefore(DatabaseEntry data) + throws DatabaseException { + + return DbCompat.putBefore(cursor, data); + } + + private OperationStatus doGetFirst(LockMode lockMode) + throws DatabaseException { + + if (secCursor != null && privPKey != null) { + return secCursor.getFirst(privKey, privPKey, privData, lockMode); + } else { + return cursor.getFirst(privKey, privData, lockMode); + } + } + + private OperationStatus doGetLast(LockMode lockMode) + throws DatabaseException { + + if (secCursor != null && privPKey != null) { + return secCursor.getLast(privKey, privPKey, privData, lockMode); + } else { + return cursor.getLast(privKey, privData, lockMode); + } + } + + private OperationStatus doGetNext(LockMode lockMode) + throws DatabaseException { + + if (secCursor != null && privPKey != null) { + return secCursor.getNext(privKey, privPKey, privData, lockMode); + } else { + return cursor.getNext(privKey, privData, lockMode); + } + } + + private OperationStatus doGetNextDup(LockMode lockMode) + throws DatabaseException { + + if (secCursor != null && privPKey != null) { + return secCursor.getNextDup(privKey, privPKey, privData, lockMode); + } else { + return cursor.getNextDup(privKey, privData, lockMode); + } + } + + private OperationStatus doGetNextNoDup(LockMode lockMode) + throws DatabaseException { + + if (secCursor != null && privPKey != null) { + return secCursor.getNextNoDup(privKey, privPKey, privData, + lockMode); + } else { + return cursor.getNextNoDup(privKey, privData, lockMode); + } + } + + private OperationStatus doGetPrev(LockMode lockMode) + throws DatabaseException { + + if (secCursor != null && privPKey != null) { + return secCursor.getPrev(privKey, privPKey, privData, lockMode); + } else { + return cursor.getPrev(privKey, privData, lockMode); + } + } + + private OperationStatus doGetPrevDup(LockMode lockMode) + throws DatabaseException { + + if (secCursor != null && privPKey != null) { + return secCursor.getPrevDup(privKey, privPKey, privData, lockMode); + } else { + return cursor.getPrevDup(privKey, privData, lockMode); + } + } + + private OperationStatus doGetPrevNoDup(LockMode lockMode) + throws DatabaseException { + + if (secCursor != null && privPKey != null) { + return secCursor.getPrevNoDup(privKey, privPKey, privData, + lockMode); + } else { + return cursor.getPrevNoDup(privKey, privData, lockMode); + } + } + + private OperationStatus doGetSearchKey(LockMode lockMode) + throws DatabaseException { + + if (isRecnoOrQueue && DbCompat.getRecordNumber(privKey) <= 0) { + return OperationStatus.NOTFOUND; + } + if (secCursor != null && privPKey != null) { + return secCursor.getSearchKey(privKey, privPKey, privData, + lockMode); + } else { + return cursor.getSearchKey(privKey, privData, lockMode); + } + } + + private OperationStatus doGetSearchKeyRange(LockMode lockMode) + throws DatabaseException { + + if (isRecnoOrQueue && DbCompat.getRecordNumber(privKey) <= 0) { + return OperationStatus.NOTFOUND; + } + if (secCursor != null && privPKey != null) { + return secCursor.getSearchKeyRange(privKey, privPKey, privData, + lockMode); + } else { + return cursor.getSearchKeyRange(privKey, privData, lockMode); + } + } + + private OperationStatus doGetSearchBoth(LockMode lockMode) + throws DatabaseException { + + if (isRecnoOrQueue && DbCompat.getRecordNumber(privKey) <= 0) { + return OperationStatus.NOTFOUND; + } + if (secCursor != null && privPKey != null) { + return secCursor.getSearchBoth(privKey, privPKey, privData, + lockMode); + } else { + return cursor.getSearchBoth(privKey, privData, lockMode); + } + } + + private OperationStatus doGetSearchBothRange(LockMode lockMode) + throws DatabaseException { + + if (isRecnoOrQueue && DbCompat.getRecordNumber(privKey) <= 0) { + return OperationStatus.NOTFOUND; + } + if (secCursor != null && privPKey != null) { + return secCursor.getSearchBothRange(privKey, privPKey, privData, + lockMode); + } else { + return cursor.getSearchBothRange(privKey, privData, lockMode); + } + } + + private OperationStatus doGetSearchRecordNumber(LockMode lockMode) + throws DatabaseException { + + if (DbCompat.getRecordNumber(privKey) <= 0) { + return OperationStatus.NOTFOUND; + } + if (secCursor != null && privPKey != null) { + return DbCompat.getSearchRecordNumber(secCursor, privKey, privPKey, + privData, lockMode); + } else { + return DbCompat.getSearchRecordNumber(cursor, privKey, privData, + lockMode); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredCollection.java b/db/java/src/com/sleepycat/collections/StoredCollection.java new file mode 100644 index 000000000..90f1a2f11 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredCollection.java @@ -0,0 +1,460 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredCollection.java,v 1.4 2004/08/02 18:52:05 mjc Exp $ + */ + +package com.sleepycat.collections; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; + +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.JoinConfig; +import com.sleepycat.db.OperationStatus; + +/** + * A abstract base class for all stored collections. This class, and its + * base class {@link StoredContainer}, provide implementations of most methods + * in the {@link Collection} interface. Other methods, such as {@link #add} + * and {@link #remove}, are provided by concrete classes that extend this + * class. + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * <p>In addition, this class provides the following methods for stored + * collections only. Note that the use of these methods is not compatible with + * the standard Java collections interface.</p> + * <ul> + * <li>{@link #iterator(boolean)}</li> + * <li>{@link #join}</li> + * <li>{@link #toList()}</li> + * </ul> + * + * @author Mark Hayes + */ +public abstract class StoredCollection extends StoredContainer + implements Collection { + + StoredCollection(DataView view) { + + super(view); + } + + final boolean add(Object key, Object value) { + + DataCursor cursor = null; + boolean doAutoCommit = beginAutoCommit(); + try { + cursor = new DataCursor(view, true); + OperationStatus status = + cursor.putNoDupData(key, value, null, false); + closeCursor(cursor); + commitAutoCommit(doAutoCommit); + return (status == OperationStatus.SUCCESS); + } catch (Exception e) { + closeCursor(cursor); + throw handleException(e, doAutoCommit); + } + } + + /** + * Returns an iterator over the elements in this collection. + * The iterator will be read-only if the collection is read-only. + * This method conforms to the {@link Collection#iterator} interface. + * + * @return a {@link StoredIterator} for this collection. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + * + * @see #isWriteAllowed + */ + public Iterator iterator() { + + return iterator(isWriteAllowed()); + } + + /** + * Returns a read or read-write iterator over the elements in this + * collection. + * This method does not exist in the standard {@link Collection} interface. + * + * @param writeAllowed is true to open a read-write iterator or false to + * open a read-only iterator. If the collection is read-only the iterator + * will always be read-only. + * + * @return a {@link StoredIterator} for this collection. + * + * @throws IllegalStateException if writeAllowed is true but the collection + * is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + * + * @see #isWriteAllowed + */ + public StoredIterator iterator(boolean writeAllowed) { + + try { + return new StoredIterator(this, writeAllowed && isWriteAllowed(), + null); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Returns an array of all the elements in this collection. + * This method conforms to the {@link Collection#toArray()} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public Object[] toArray() { + + ArrayList list = new ArrayList(); + Iterator i = iterator(); + try { + while (i.hasNext()) { + list.add(i.next()); + } + } finally { + StoredIterator.close(i); + } + return list.toArray(); + } + + /** + * Returns an array of all the elements in this collection whose runtime + * type is that of the specified array. + * This method conforms to the {@link Collection#toArray(Object[])} + * interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public Object[] toArray(Object[] a) { + + int j = 0; + Iterator i = iterator(); + try { + while (j < a.length && i.hasNext()) { + a[j++] = i.next(); + } + if (j < a.length) { + a[j] = null; + } else if (i.hasNext()) { + ArrayList list = new ArrayList(Arrays.asList(a)); + while (i.hasNext()) { + list.add(i.next()); + } + a = list.toArray(a); + } + } finally { + StoredIterator.close(i); + } + return a; + } + + /** + * Returns true if this collection contains all of the elements in the + * specified collection. + * This method conforms to the {@link Collection#containsAll} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean containsAll(Collection coll) { + Iterator i = coll.iterator(); + try { + while (i.hasNext()) { + if (!contains(i.next())) { + return false; + } + } + } finally { + StoredIterator.close(i); + } + return true; + } + + /** + * Adds all of the elements in the specified collection to this collection + * (optional operation). + * This method calls the {@link #add(Object)} method of the concrete + * collection class, which may or may not be supported. + * This method conforms to the {@link Collection#addAll} interface. + * + * @throws UnsupportedOperationException if the collection is read-only, or + * if the collection is indexed, or if the add method is not supported by + * the concrete collection. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean addAll(Collection coll) { + Iterator i = null; + boolean doAutoCommit = beginAutoCommit(); + try { + i = coll.iterator(); + boolean changed = false; + while (i.hasNext()) { + if (add(i.next())) { + changed = true; + } + } + StoredIterator.close(i); + commitAutoCommit(doAutoCommit); + return changed; + } catch (Exception e) { + StoredIterator.close(i); + throw handleException(e, doAutoCommit); + } + } + + /** + * Removes all this collection's elements that are also contained in the + * specified collection (optional operation). + * This method conforms to the {@link Collection#removeAll} interface. + * + * @throws UnsupportedOperationException if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean removeAll(Collection coll) { + + return removeAll(coll, true); + } + + /** + * Retains only the elements in this collection that are contained in the + * specified collection (optional operation). + * This method conforms to the {@link Collection#removeAll} interface. + * + * @throws UnsupportedOperationException if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean retainAll(Collection coll) { + + return removeAll(coll, false); + } + + private boolean removeAll(Collection coll, boolean ifExistsInColl) { + Iterator i = null; + boolean doAutoCommit = beginAutoCommit(); + try { + boolean changed = false; + i = iterator(); + while (i.hasNext()) { + if (ifExistsInColl == coll.contains(i.next())) { + i.remove(); + changed = true; + } + } + StoredIterator.close(i); + commitAutoCommit(doAutoCommit); + return changed; + } catch (Exception e) { + StoredIterator.close(i); + throw handleException(e, doAutoCommit); + } + } + + /** + * Compares the specified object with this collection for equality. + * A value comparison is performed by this method and the stored values + * are compared rather than calling the equals() method of each element. + * This method conforms to the {@link Collection#equals} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean equals(Object other) { + + if (other instanceof Collection) { + Collection otherColl = StoredCollection.copyCollection(other); + Iterator i = iterator(); + try { + while (i.hasNext()) { + if (!otherColl.remove(i.next())) { + return false; + } + } + return otherColl.isEmpty(); + } finally { + StoredIterator.close(i); + } + } else { + return false; + } + } + + /* + * Add this in to keep FindBugs from whining at us about implementing + * equals(), but not hashCode(). + */ + public int hashCode() { + return super.hashCode(); + } + + /** + * Returns a copy of this collection as an ArrayList. This is the same as + * {@link #toArray()} but returns a collection instead of an array. + * + * @return an {@link ArrayList} containing a copy of all elements in this + * collection. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public List toList() { + + ArrayList list = new ArrayList(); + Iterator i = iterator(); + try { + while (i.hasNext()) list.add(i.next()); + return list; + } finally { + StoredIterator.close(i); + } + } + + /** + * Converts the collection to a string representation for debugging. + * WARNING: All elements will be converted to strings and returned and + * therefore the returned string may be very large. + * + * @return the string representation. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("["); + Iterator i = iterator(); + try { + while (i.hasNext()) { + if (buf.length() > 1) buf.append(','); + buf.append(i.next().toString()); + } + buf.append(']'); + return buf.toString(); + } finally { + StoredIterator.close(i); + } + } + + /** + * Returns an iterator representing an equality join of the indices and + * index key values specified. + * This method does not exist in the standard {@link Collection} interface. + * + * <p>The returned iterator supports only the two methods: hasNext() and + * next(). All other methods will throw UnsupportedOperationException.</p> + * + * @param indices is an array of indices with elements corresponding to + * those in the indexKeys array. + * + * @param indexKeys is an array of index key values identifying the + * elements to be selected. + * + * @param joinConfig is the join configuration, or null to use the + * default configuration. + * + * @return an iterator over the elements in this collection that match + * all specified index key values. + * + * @throws IllegalArgumentException if this collection is indexed or if a + * given index does not have the same store as this collection. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public StoredIterator join(StoredContainer[] indices, Object[] indexKeys, + JoinConfig joinConfig) { + + try { + DataView[] indexViews = new DataView[indices.length]; + for (int i = 0; i < indices.length; i += 1) { + indexViews[i] = indices[i].view; + } + DataCursor cursor = view.join(indexViews, indexKeys, joinConfig); + return new StoredIterator(this, false, cursor); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + final Object getFirstOrLast(boolean doGetFirst) { + + DataCursor cursor = null; + try { + cursor = new DataCursor(view, false); + OperationStatus status; + if (doGetFirst) { + status = cursor.getFirst(false); + } else { + status = cursor.getLast(false); + } + return (status == OperationStatus.SUCCESS) ? + makeIteratorData(null, cursor) : null; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } finally { + closeCursor(cursor); + } + } + + abstract Object makeIteratorData(StoredIterator iterator, + DataCursor cursor) + throws DatabaseException; + + abstract boolean hasValues(); + + boolean iterateDuplicates() { + + return true; + } + + void checkIterAddAllowed() + throws UnsupportedOperationException { + + if (!areDuplicatesAllowed()) { + throw new UnsupportedOperationException("duplicates required"); + } + } + + int getIndexOffset() { + + return 0; + } + + private static Collection copyCollection(Object other) { + + if (other instanceof StoredCollection) { + return ((StoredCollection) other).toList(); + } else { + return new ArrayList((Collection) other); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredCollections.java b/db/java/src/com/sleepycat/collections/StoredCollections.java new file mode 100644 index 000000000..c9a4ab9fc --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredCollections.java @@ -0,0 +1,156 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredCollections.java,v 1.1 2004/04/09 16:34:09 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.SortedMap; +import java.util.SortedSet; + +/** + * This class consists exclusively of static methods that operate on or return + * stored collections. It contains methods for changing certain properties of a + * collection. Because collection properties are immutable, these methods + * always return a new collection reference. This allows stored collections to + * be used safely by multiple threads. Note that creating the new collection + * reference is not expensive and creates only two new objects. + * + * <p>When a collection is created with a particular property, all collections + * and iterators derived from that collection will inherit the property. For + * example, if a dirty-read Map is created then calls to subMap(), values(), + * entrySet(), and keySet() will create dirty-read collections also.</p> + * + * <p><b>Dirty-Read</b> Methods names beginning with dirtyRead create a new + * dirty-read container from a given stored container. When dirty-read is + * enabled, data will be read that has been modified by another transaction but + * not committed. Using dirty-read can improve concurrency since reading will + * not wait for other transactions to complete. For a non-transactional + * container (when {@link StoredContainer#isTransactional} returns false), + * dirty-read has no effect. If {@link StoredContainer#isDirtyReadAllowed} + * returns false, dirty-read also has no effect. If dirty-ready is enabled + * (and allowed) for a container, {@link StoredContainer#isDirtyRead} will + * return true. Dirty-read is disabled by default for a container.</p> + */ +public class StoredCollections { + + private StoredCollections() {} + + /** + * Creates a dirty-read collection from a given stored collection. + * + * @param storedCollection the base collection. + * + * @return the dirty-read collection. + * + * @throws ClassCastException if the given container is not a + * StoredContainer. + */ + public static Collection dirtyReadCollection(Collection storedCollection) { + + return (Collection) + ((StoredContainer) storedCollection).dirtyReadClone(); + } + + /** + * Creates a dirty-read list from a given stored list. + * + * @param storedList the base list. + * + * @return the dirty-read list. + * + * @throws ClassCastException if the given container is not a + * StoredContainer. + */ + public static List dirtyReadList(List storedList) { + + return (List) ((StoredContainer) storedList).dirtyReadClone(); + } + + /** + * Creates a dirty-read map from a given stored map. + * + * @param storedMap the base map. + * + * @return the dirty-read map. + * + * @throws ClassCastException if the given container is not a + * StoredContainer. + */ + public static Map dirtyReadMap(Map storedMap) { + + return (Map) ((StoredContainer) storedMap).dirtyReadClone(); + } + + /** + * Creates a dirty-read set from a given stored set. + * + * @param storedSet the base set. + * + * @return the dirty-read set. + * + * @throws ClassCastException if the given container is not a + * StoredContainer. + */ + public static Set dirtyReadSet(Set storedSet) { + + return (Set) ((StoredContainer) storedSet).dirtyReadClone(); + } + + /** + * Creates a dirty-read sorted map from a given stored sorted map. + * + * @param storedSortedMap the base map. + * + * @return the dirty-read map. + * + * @throws ClassCastException if the given container is not a + * StoredContainer. + */ + public static SortedMap dirtyReadSortedMap(SortedMap storedSortedMap) { + + return (SortedMap) + ((StoredContainer) storedSortedMap).dirtyReadClone(); + } + + /** + * Creates a dirty-read sorted set from a given stored sorted set. + * + * @param storedSortedSet the base set. + * + * @return the dirty-read set. + * + * @throws ClassCastException if the given container is not a + * StoredContainer. + */ + public static SortedSet dirtyReadSortedSet(SortedSet storedSortedSet) { + + return (SortedSet) + ((StoredContainer) storedSortedSet).dirtyReadClone(); + } + + /** + * Clones a stored iterator preserving its current position. + * + * @param storedIterator an iterator to clone. + * + * @return a new {@link StoredIterator} having the same position as the + * given iterator. + * + * @throws ClassCastException if the given iterator is not a + * StoredIterator. + */ + public static Iterator iterator(Iterator storedIterator) { + + return (Iterator) ((StoredIterator) storedIterator).clone(); + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredContainer.java b/db/java/src/com/sleepycat/collections/StoredContainer.java new file mode 100644 index 000000000..f503a5dc8 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredContainer.java @@ -0,0 +1,415 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredContainer.java,v 1.2 2004/06/02 20:59:38 mark Exp $ + */ + +package com.sleepycat.collections; + +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.OperationStatus; +import com.sleepycat.util.RuntimeExceptionWrapper; + +/** + * A abstract base class for all stored collections and maps. This class + * provides implementations of methods that are common to the {@link + * java.util.Collection} and the {@link java.util.Map} interfaces, namely + * {@link #clear}, {@link #isEmpty} and {@link #size}. + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * <p>In addition, this class provides the following methods for stored + * collections only. Note that the use of these methods is not compatible with + * the standard Java collections interface.</p> + * <ul> + * <li>{@link #isWriteAllowed()}</li> + * <li>{@link #isSecondary()}</li> + * <li>{@link #isOrdered()}</li> + * <li>{@link #areDuplicatesAllowed()}</li> + * <li>{@link #areDuplicatesOrdered()}</li> + * <li>{@link #areKeysRenumbered()}</li> + * <li>{@link #isDirtyReadAllowed()}</li> + * <li>{@link #isDirtyRead()}</li> + * <li>{@link #isTransactional()}</li> + * </ul> + * + * @author Mark Hayes + */ +public abstract class StoredContainer implements Cloneable { + + DataView view; + + StoredContainer(DataView view) { + + this.view = view; + } + + /** + * Returns true if this is a read-write container or false if this is a + * read-only container. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether write is allowed. + */ + public final boolean isWriteAllowed() { + + return view.writeAllowed; + } + + /** + * Returns whether dirty-read is allowed for this container. + * For the JE product, dirty-read is always allowed; for the DB product, + * dirty-read is allowed if it was configured for the underlying database + * for this container. + * Even when dirty-read is allowed it must specifically be enabled by + * calling one of the {@link StoredCollections} methods. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether dirty-read is allowed. + */ + public final boolean isDirtyReadAllowed() { + + return view.dirtyReadAllowed; + } + + /** + * Returns whether dirty-read is enabled for this container. + * If dirty-read is enabled, data will be read that is modified but not + * committed. + * Dirty-read is disabled by default. + * This method always returns false if {@link #isDirtyReadAllowed} returns + * false. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether dirty-read is enabled. + */ + public final boolean isDirtyRead() { + + return view.dirtyReadEnabled; + } + + /** + * Returns whether the databases underlying this container are + * transactional. + * Even in a transactional environment, a database will be transactional + * only if it was opened within a transaction or if the auto-commit option + * was specified when it was opened. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether the database is transactional. + */ + public final boolean isTransactional() { + + return view.transactional; + } + + /** + * Clones and enables dirty-read in the clone. + */ + final StoredContainer dirtyReadClone() { + + if (!isDirtyReadAllowed()) + return this; + try { + StoredContainer cont = (StoredContainer) clone(); + cont.view = cont.view.dirtyReadView(true); + return cont; + } catch (CloneNotSupportedException willNeverOccur) { return null; } + } + + /** + * Returns whether duplicate keys are allowed in this container. + * Duplicates are optionally allowed for HASH and BTREE databases. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether duplicates are allowed. + */ + public final boolean areDuplicatesAllowed() { + + return view.dupsAllowed; + } + + /** + * Returns whether duplicate keys are allowed and sorted by element value. + * Duplicates are optionally sorted for HASH and BTREE databases. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether duplicates are ordered. + */ + public final boolean areDuplicatesOrdered() { + + return view.dupsOrdered; + } + + /** + * Returns whether keys are renumbered when insertions and deletions occur. + * Keys are optionally renumbered for RECNO databases. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether keys are renumbered. + */ + public final boolean areKeysRenumbered() { + + return view.keysRenumbered; + } + + /** + * Returns whether keys are ordered in this container. + * Keys are ordered for BTREE, RECNO and QUEUE database. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether keys are ordered. + */ + public final boolean isOrdered() { + + return view.ordered; + } + + /** + * Returns whether this container is a view on a secondary database rather + * than directly on a primary database. + * This method does not exist in the standard {@link java.util.Map} or + * {@link java.util.Collection} interfaces. + * + * @return whether the view is for a secondary database. + */ + public final boolean isSecondary() { + + return view.isSecondary(); + } + + /** + * Always throws UnsupportedOperationException. The size of a database + * cannot be obtained reliably or inexpensively. + * This method therefore violates the {@link java.util.Collection#size} and + * {@link java.util.Map#size} interfaces. + * + * @return always throws an exception. + * + * @throws UnsupportedOperationException unconditionally. + */ + public int size() { + + throw new UnsupportedOperationException( + "collection size not available"); + } + + /** + * Returns true if this map or collection contains no mappings or elements. + * This method conforms to the {@link java.util.Collection#isEmpty} and + * {@link java.util.Map#isEmpty} interfaces. + * + * @return whether the container is empty. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. + */ + public boolean isEmpty() { + + try { + return view.isEmpty(); + } catch (Exception e) { + throw convertException(e); + } + } + + /** + * Removes all mappings or elements from this map or collection (optional + * operation). + * This method conforms to the {@link java.util.Collection#clear} and + * {@link java.util.Map#clear} interfaces. + * + * @throws UnsupportedOperationException if the container is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. + */ + public void clear() { + + boolean doAutoCommit = beginAutoCommit(); + try { + view.clear(); + commitAutoCommit(doAutoCommit); + } catch (Exception e) { + throw handleException(e, doAutoCommit); + } + } + + Object get(Object key) { + + DataCursor cursor = null; + try { + cursor = new DataCursor(view, false); + if (OperationStatus.SUCCESS == + cursor.getSearchKey(key, null, false)) { + return cursor.getCurrentValue(); + } else { + return null; + } + } catch (Exception e) { + throw StoredContainer.convertException(e); + } finally { + closeCursor(cursor); + } + } + + Object put(final Object key, final Object value) { + + DataCursor cursor = null; + boolean doAutoCommit = beginAutoCommit(); + try { + cursor = new DataCursor(view, true); + Object[] oldValue = new Object[1]; + cursor.put(key, value, oldValue, false); + closeCursor(cursor); + commitAutoCommit(doAutoCommit); + return oldValue[0]; + } catch (Exception e) { + closeCursor(cursor); + throw handleException(e, doAutoCommit); + } + } + + final boolean removeKey(final Object key, final Object[] oldVal) { + + DataCursor cursor = null; + boolean doAutoCommit = beginAutoCommit(); + try { + cursor = new DataCursor(view, true); + boolean found = false; + OperationStatus status = cursor.getSearchKey(key, null, true); + while (status == OperationStatus.SUCCESS) { + cursor.delete(); + found = true; + if (oldVal != null && oldVal[0] == null) { + oldVal[0] = cursor.getCurrentValue(); + } + status = cursor.getNextDup(true); + } + closeCursor(cursor); + commitAutoCommit(doAutoCommit); + return found; + } catch (Exception e) { + closeCursor(cursor); + throw handleException(e, doAutoCommit); + } + } + + boolean containsKey(Object key) { + + DataCursor cursor = null; + try { + cursor = new DataCursor(view, false); + return OperationStatus.SUCCESS == + cursor.getSearchKey(key, null, false); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } finally { + closeCursor(cursor); + } + } + + final boolean removeValue(Object value) { + + DataCursor cursor = null; + boolean doAutoCommit = beginAutoCommit(); + try { + cursor = new DataCursor(view, true); + OperationStatus status = cursor.find(value, true); + if (status == OperationStatus.SUCCESS) { + cursor.delete(); + } + closeCursor(cursor); + commitAutoCommit(doAutoCommit); + return (status == OperationStatus.SUCCESS); + } catch (Exception e) { + closeCursor(cursor); + throw handleException(e, doAutoCommit); + } + } + + boolean containsValue(Object value) { + + DataCursor cursor = null; + try { + cursor = new DataCursor(view, false); + OperationStatus status = cursor.find(value, true); + return (status == OperationStatus.SUCCESS); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } finally { + closeCursor(cursor); + } + } + + final void closeCursor(DataCursor cursor) { + + if (cursor != null) { + try { + cursor.close(); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + } + + final boolean beginAutoCommit() { + + if (view.transactional) { + try { + CurrentTransaction currentTxn = view.getCurrentTxn(); + if (currentTxn.getTransaction() == null) { + currentTxn.beginTransaction(null); + return true; + } + } catch (DatabaseException e) { + throw new RuntimeExceptionWrapper(e); + } + } + return false; + } + + final void commitAutoCommit(boolean doAutoCommit) + throws DatabaseException { + + if (doAutoCommit) view.getCurrentTxn().commitTransaction(); + } + + final RuntimeException handleException(Exception e, boolean doAutoCommit) { + + if (doAutoCommit) { + try { + view.getCurrentTxn().abortTransaction(); + } catch (DatabaseException ignored) { + } + } + return StoredContainer.convertException(e); + } + + static RuntimeException convertException(Exception e) { + + if (e instanceof RuntimeException) { + return (RuntimeException) e; + } else { + return new RuntimeExceptionWrapper(e); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredEntrySet.java b/db/java/src/com/sleepycat/collections/StoredEntrySet.java new file mode 100644 index 000000000..fbc5f85d2 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredEntrySet.java @@ -0,0 +1,176 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredEntrySet.java,v 1.3 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.OperationStatus; + +/** + * The Set returned by Map.entrySet(). This class may not be instantiated + * directly. Contrary to what is stated by {@link Map#entrySet} this class + * does support the {@link #add} and {@link #addAll} methods. + * + * <p>The {@link java.util.Map.Entry#setValue} method of the Map.Entry objects + * that are returned by this class and its iterators behaves just as the {@link + * StoredIterator#set} method does.</p> + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredEntrySet extends StoredCollection implements Set { + + StoredEntrySet(DataView mapView) { + + super(mapView); + } + + /** + * Adds the specified element to this set if it is not already present + * (optional operation). + * This method conforms to the {@link Set#add} interface. + * + * @param mapEntry must be a {@link java.util.Map.Entry} instance. + * + * @return true if the key-value pair was added to the set (and was not + * previously present). + * + * @throws UnsupportedOperationException if the collection is read-only. + * + * @throws ClassCastException if the mapEntry is not a {@link + * java.util.Map.Entry} instance. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. + */ + public boolean add(Object mapEntry) { + + Map.Entry entry = (Map.Entry) mapEntry; // allow ClassCastException + return add(entry.getKey(), entry.getValue()); + } + + /** + * Removes the specified element from this set if it is present (optional + * operation). + * This method conforms to the {@link Set#remove} interface. + * + * @param mapEntry is a {@link java.util.Map.Entry} instance to be removed. + * + * @return true if the key-value pair was removed from the set, or false if + * the mapEntry is not a {@link java.util.Map.Entry} instance or is not + * present in the set. + * + * @throws UnsupportedOperationException if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. + */ + public boolean remove(Object mapEntry) { + + if (!(mapEntry instanceof Map.Entry)) { + return false; + } + Map.Entry entry = (Map.Entry) mapEntry; + DataCursor cursor = null; + boolean doAutoCommit = beginAutoCommit(); + try { + cursor = new DataCursor(view, true); + OperationStatus status = + cursor.getSearchBoth(entry.getKey(), entry.getValue(), true); + if (status == OperationStatus.SUCCESS) { + cursor.delete(); + } + closeCursor(cursor); + commitAutoCommit(doAutoCommit); + return (status == OperationStatus.SUCCESS); + } catch (Exception e) { + closeCursor(cursor); + throw handleException(e, doAutoCommit); + } + } + + /** + * Returns true if this set contains the specified element. + * This method conforms to the {@link Set#contains} interface. + * + * @param mapEntry is a {@link java.util.Map.Entry} instance to be checked. + * + * @return true if the key-value pair is present in the set, or false if + * the mapEntry is not a {@link java.util.Map.Entry} instance or is not + * present in the set. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. + */ + public boolean contains(Object mapEntry) { + + if (!(mapEntry instanceof Map.Entry)) { + return false; + } + Map.Entry entry = (Map.Entry) mapEntry; + DataCursor cursor = null; + try { + cursor = new DataCursor(view, false); + OperationStatus status = + cursor.getSearchBoth(entry.getKey(), entry.getValue(), false); + return (status == OperationStatus.SUCCESS); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } finally { + closeCursor(cursor); + } + } + + // javadoc is inherited + public String toString() { + StringBuffer buf = new StringBuffer(); + buf.append("["); + Iterator i = iterator(); + try { + while (i.hasNext()) { + Map.Entry entry = (Map.Entry) i.next(); + if (buf.length() > 1) buf.append(','); + Object key = entry.getKey(); + Object val = entry.getValue(); + if (key != null) buf.append(key.toString()); + buf.append('='); + if (val != null) buf.append(val.toString()); + } + buf.append(']'); + return buf.toString(); + } + finally { + StoredIterator.close(i); + } + } + + Object makeIteratorData(StoredIterator iterator, DataCursor cursor) + throws DatabaseException { + + return new StoredMapEntry(cursor.getCurrentKey(), + cursor.getCurrentValue(), + this, iterator); + } + + boolean hasValues() { + + return true; + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredIterator.java b/db/java/src/com/sleepycat/collections/StoredIterator.java new file mode 100644 index 000000000..cfc7c67a4 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredIterator.java @@ -0,0 +1,600 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredIterator.java,v 1.5 2004/09/22 18:01:03 bostic Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Iterator; +import java.util.ListIterator; +import java.util.NoSuchElementException; + +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.OperationStatus; + +/** + * The Iterator returned by all stored collections. + * + * <p>While in general this class conforms to the {@link Iterator} interface, + * it is important to note that all iterators for stored collections must be + * explicitly closed with {@link #close()}. The static method {@link + * #close(java.util.Iterator)} allows calling close for all iterators without + * harm to iterators that are not from stored collections, and also avoids + * casting. If a stored iterator is not closed, unpredictable behavior + * including process death may result.</p> + * + * <p>This class implements the {@link Iterator} interface for all stored + * iterators. It also implements {@link ListIterator} because some list + * iterator methods apply to all stored iterators, for example, {@link + * #previous} and {@link #hasPrevious}. Other list iterator methods are always + * supported for lists, but for other types of collections are only supported + * under certain conditions. See {@link #nextIndex}, {@link #previousIndex}, + * {@link #add} and {@link #set} for details.</p> + * + * <p>In addition, this class provides the following methods for stored + * collection iterators only. Note that the use of these methods is not + * compatible with the standard Java collections interface.</p> + * <ul> + * <li>{@link #close()}</li> + * <li>{@link #close(Iterator)}</li> + * <li>{@link #getCollection}</li> + * <li>{@link #setReadModifyWrite}</li> + * <li>{@link #isReadModifyWrite}</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredIterator implements ListIterator, Cloneable { + + /** + * Closes the given iterator using {@link #close()} if it is a {@link + * StoredIterator}. If the given iterator is not a {@link StoredIterator}, + * this method does nothing. + * + * @param i is the iterator to close. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. + */ + public static void close(Iterator i) { + + if (i instanceof StoredIterator) { + ((StoredIterator) i).close(); + } + } + + private static final int MOVE_NEXT = 1; + private static final int MOVE_PREV = 2; + private static final int MOVE_FIRST = 3; + + private boolean lockForWrite; + private StoredCollection coll; + private DataCursor cursor; + private int toNext; + private int toPrevious; + private int toCurrent; + private boolean writeAllowed; + private boolean setAndRemoveAllowed; + private Object currentData; + private final boolean recNumAccess; + + StoredIterator(StoredCollection coll, boolean writeAllowed, + DataCursor joinCursor) { + try { + this.coll = coll; + this.writeAllowed = writeAllowed; + if (joinCursor == null) + this.cursor = new DataCursor(coll.view, writeAllowed); + else + this.cursor = joinCursor; + this.recNumAccess = cursor.hasRecNumAccess(); + reset(); + } catch (Exception e) { + try { + /* Ensure that the cursor is closed. [#10516] */ + close(); + } catch (Exception ignored) {} + throw StoredContainer.convertException(e); + } + } + + /** + * Clones this iterator preserving its current position. + * + * @return a new {@link StoredIterator} having the same position as this + * iterator. + */ + protected Object clone() { + + try { + StoredIterator o = (StoredIterator) super.clone(); + o.cursor = cursor.cloneCursor(); + return o; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Returns whether write-locks will be obtained when reading with this + * cursor. + * Obtaining write-locks can prevent deadlocks when reading and then + * modifying data. + * + * @return the write-lock setting. + */ + public final boolean isReadModifyWrite() { + + return lockForWrite; + } + + /** + * Changes whether write-locks will be obtained when reading with this + * cursor. + * Obtaining write-locks can prevent deadlocks when reading and then + * modifying data. + * + * @param lockForWrite the write-lock setting. + */ + public void setReadModifyWrite(boolean lockForWrite) { + + this.lockForWrite = lockForWrite; + } + + // --- begin Iterator/ListIterator methods --- + + /** + * Returns true if this iterator has more elements when traversing in the + * forward direction. False is returned if the iterator has been closed. + * This method conforms to the {@link Iterator#hasNext} interface. + * + * @return whether {@link #next()} will succeed. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. + */ + public boolean hasNext() { + + if (cursor == null) { + return false; + } + try { + if (toNext != 0) { + OperationStatus status = move(toNext); + if (status == OperationStatus.SUCCESS) { + toNext = 0; + toPrevious = MOVE_PREV; + toCurrent = MOVE_PREV; + } + } + return (toNext == 0); + } + catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Returns true if this iterator has more elements when traversing in the + * reverse direction. It returns false if the iterator has been closed. + * This method conforms to the {@link ListIterator#hasPrevious} interface. + * + * @return whether {@link #previous()} will succeed. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is thrown. + */ + public boolean hasPrevious() { + + if (cursor == null) { + return false; + } + try { + if (toPrevious != 0) { + OperationStatus status = move(toPrevious); + if (status == OperationStatus.SUCCESS) { + toPrevious = 0; + toNext = MOVE_NEXT; + toCurrent = MOVE_NEXT; + } + } + return (toPrevious == 0); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Returns the next element in the iteration. + * This method conforms to the {@link Iterator#next} interface. + * + * @return the next element. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public Object next() { + + try { + if (toNext != 0) { + OperationStatus status = move(toNext); + if (status == OperationStatus.SUCCESS) { + toNext = 0; + } + } + if (toNext == 0) { + currentData = coll.makeIteratorData(this, cursor); + toNext = MOVE_NEXT; + toPrevious = 0; + toCurrent = 0; + setAndRemoveAllowed = true; + return currentData; + } + // else throw NoSuchElementException below + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + throw new NoSuchElementException(); + } + + /** + * Returns the next element in the iteration. + * This method conforms to the {@link ListIterator#previous} interface. + * + * @return the previous element. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public Object previous() { + + try { + if (toPrevious != 0) { + OperationStatus status = move(toPrevious); + if (status == OperationStatus.SUCCESS) { + toPrevious = 0; + } + } + if (toPrevious == 0) { + currentData = coll.makeIteratorData(this, cursor); + toPrevious = MOVE_PREV; + toNext = 0; + toCurrent = 0; + setAndRemoveAllowed = true; + return currentData; + } + // else throw NoSuchElementException below + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + throw new NoSuchElementException(); + } + + /** + * Returns the index of the element that would be returned by a subsequent + * call to next. + * This method conforms to the {@link ListIterator#nextIndex} interface + * except that it returns Integer.MAX_VALUE for stored lists when + * positioned at the end of the list, rather than returning the list size + * as specified by the ListIterator interface. This is because the database + * size is not available. + * + * @return the next index. + * + * @throws UnsupportedOperationException if this iterator's collection does + * not use record number keys. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public int nextIndex() { + + if (!recNumAccess) { + throw new UnsupportedOperationException( + "Record number access not supported"); + } + try { + return hasNext() ? (cursor.getCurrentRecordNumber() - + coll.getIndexOffset()) + : Integer.MAX_VALUE; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Returns the index of the element that would be returned by a subsequent + * call to previous. + * This method conforms to the {@link ListIterator#previousIndex} + * interface. + * + * @return the previous index. + * + * @throws UnsupportedOperationException if this iterator's collection does + * not use record number keys. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public int previousIndex() { + + if (!recNumAccess) { + throw new UnsupportedOperationException( + "Record number access not supported"); + } + try { + return hasPrevious() ? (cursor.getCurrentRecordNumber() - + coll.getIndexOffset()) + : -1; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Replaces the last element returned by next or previous with the + * specified element (optional operation). + * This method conforms to the {@link ListIterator#set} interface. + * + * @param value the new value. + * + * @throws UnsupportedOperationException if the collection is a {@link + * StoredKeySet} (the set returned by {@link java.util.Map#keySet}), or if + * duplicates are sorted since this would change the iterator position, or + * if the collection is indexed, or if the collection is read-only. + * + * @throws IllegalArgumentException if an entity value binding is used and + * the primary key of the value given is different than the existing stored + * primary key. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public void set(Object value) { + + if (!coll.hasValues()) throw new UnsupportedOperationException(); + if (!setAndRemoveAllowed) throw new IllegalStateException(); + try { + moveToCurrent(); + cursor.putCurrent(value); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Removes the last element that was returned by next or previous (optional + * operation). + * This method conforms to the {@link ListIterator#remove} interface except + * that when the collection is a list and the RECNO-RENUMBER access method + * is not used, list indices will not be renumbered. + * + * @throws UnsupportedOperationException if the collection is a sublist, or + * if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public void remove() { + + if (!setAndRemoveAllowed) throw new IllegalStateException(); + try { + moveToCurrent(); + cursor.delete(); + setAndRemoveAllowed = false; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Inserts the specified element into the list or inserts a duplicate into + * other types of collections (optional operation). + * This method conforms to the {@link ListIterator#add} interface when + * the collection is a list and the RECNO-RENUMBER access method is used. + * Otherwise, this method may only be called when duplicates are allowed. + * If duplicates are unsorted, the new value will be inserted in the same + * manner as list elements. + * If duplicates are sorted, the new value will be inserted in sort order. + * + * @param value the new value. + * + * @throws UnsupportedOperationException if the collection is a sublist, or + * if the collection is indexed, or if the collection is read-only, or if + * the collection is a list and the RECNO-RENUMBER access method was not + * used, or if the collection is not a list and duplicates are not allowed. + * + * @throws IllegalStateException if the collection is empty and is not a + * list with RECNO-RENUMBER access. + * + * @throws IllegalArgumentException if a duplicate value is being added + * that already exists and duplicates are sorted. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public void add(Object value) { + + coll.checkIterAddAllowed(); + try { + OperationStatus status = OperationStatus.SUCCESS; + if (toNext != 0 && toPrevious != 0) { // database is empty + if (coll.view.keysRenumbered) { // recno-renumber database + /* + * Close cursor during append and then reopen to support + * CDB restriction that append may not be called with a + * cursor open; note the append will still fail if the + * application has another cursor open. + */ + close(); + status = coll.view.append(value, null, null); + cursor = new DataCursor(coll.view, writeAllowed); + reset(); + next(); // move past new record + } else { // hash/btree with duplicates + throw new IllegalStateException( + "Collection is empty, cannot add() duplicate"); + } + } else { // database is not empty + boolean putBefore = false; + if (coll.view.keysRenumbered) { // recno-renumber database + moveToCurrent(); + if (hasNext()) { + status = cursor.putBefore(value); + putBefore = true; + } else { + status = cursor.putAfter(value); + } + } else { // hash/btree with duplicates + if (coll.areDuplicatesOrdered()) { + status = cursor.putNoDupData(null, value, null, true); + } else if (toNext == 0) { + status = cursor.putBefore(value); + putBefore = true; + } else { + status = cursor.putAfter(value); + } + } + if (putBefore) { + toPrevious = 0; + toNext = MOVE_NEXT; + } + } + if (status == OperationStatus.KEYEXIST) { + throw new IllegalArgumentException("Duplicate value"); + } else if (status != OperationStatus.SUCCESS) { + throw new IllegalArgumentException("Could not insert: " + + status); + } + setAndRemoveAllowed = false; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + // --- end Iterator/ListIterator methods --- + + /** + * Resets cursor to an uninitialized state. + */ + private void reset() { + + toNext = MOVE_FIRST; + toPrevious = MOVE_PREV; + toCurrent = 0; + currentData = null; + /* + * Initialize cursor at beginning to avoid "initial previous == last" + * behavior when cursor is uninitialized. + * + * FindBugs whines about us ignoring the return value from hasNext(). + */ + hasNext(); + } + + /** + * Returns the number of elements having the same key value as the key + * value of the element last returned by next() or previous(). If no + * duplicates are allowed, 1 is always returned. + * + * @return the number of duplicates. + * + * @throws IllegalStateException if next() or previous() has not been + * called for this iterator, or if remove() or add() were called after + * the last call to next() or previous(). + */ + public int count() { + + if (!setAndRemoveAllowed) throw new IllegalStateException(); + try { + moveToCurrent(); + return cursor.count(); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Closes this iterator. + * This method does not exist in the standard {@link Iterator} or {@link + * ListIterator} interfaces. + * + * <p>After being closed, only the {@link #hasNext} and {@link + * #hasPrevious} methods may be called and these will return false. {@link + * #close()} may also be called again and will do nothing. If other + * methods are called a <code>NullPointerException</code> will generally be + * thrown.</p> + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public void close() { + + if (cursor != null) { + coll.closeCursor(cursor); + cursor = null; + } + } + + /** + * Returns the collection associated with this iterator. + * This method does not exist in the standard {@link Iterator} or {@link + * ListIterator} interfaces. + * + * @return the collection associated with this iterator. + */ + public final StoredCollection getCollection() { + + return coll; + } + + final boolean isCurrentData(Object currentData) { + + return (this.currentData == currentData); + } + + final boolean moveToIndex(int index) { + + try { + OperationStatus status = + cursor.getSearchKey(new Integer(index), null, lockForWrite); + setAndRemoveAllowed = (status == OperationStatus.SUCCESS); + return setAndRemoveAllowed; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + private void moveToCurrent() + throws DatabaseException { + + if (toCurrent != 0) { + move(toCurrent); + toCurrent = 0; + } + } + + private OperationStatus move(int direction) + throws DatabaseException { + + switch (direction) { + case MOVE_NEXT: + if (coll.iterateDuplicates()) { + return cursor.getNext(lockForWrite); + } else { + return cursor.getNextNoDup(lockForWrite); + } + case MOVE_PREV: + if (coll.iterateDuplicates()) { + return cursor.getPrev(lockForWrite); + } else { + return cursor.getPrevNoDup(lockForWrite); + } + case MOVE_FIRST: + return cursor.getFirst(lockForWrite); + default: + throw new IllegalArgumentException(String.valueOf(direction)); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredKeySet.java b/db/java/src/com/sleepycat/collections/StoredKeySet.java new file mode 100644 index 000000000..1c86aa003 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredKeySet.java @@ -0,0 +1,144 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredKeySet.java,v 1.3 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Set; + +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.db.Database; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.OperationStatus; + +/** + * The Set returned by Map.keySet() and which can also be constructed directly + * if a Map is not needed. + * Since this collection is a set it only contains one element for each key, + * even when duplicates are allowed. Key set iterators are therefore + * particularly useful for enumerating the unique keys of a store or index that + * allows duplicates. + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredKeySet extends StoredCollection implements Set { + + /** + * Creates a key set view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public StoredKeySet(Database database, EntryBinding keyBinding, + boolean writeAllowed) { + + super(new DataView(database, keyBinding, null, null, + writeAllowed, null)); + } + + StoredKeySet(DataView keySetView) { + + super(keySetView); + } + + /** + * Adds the specified key to this set if it is not already present + * (optional operation). + * When a key is added the value in the underlying data store will be + * empty. + * This method conforms to the {@link Set#add} interface. + * + * @throws UnsupportedOperationException if the collection is indexed, or + * if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean add(Object key) { + + DataCursor cursor = null; + boolean doAutoCommit = beginAutoCommit(); + try { + cursor = new DataCursor(view, true); + OperationStatus status = cursor.putNoOverwrite(key, null, false); + closeCursor(cursor); + commitAutoCommit(doAutoCommit); + return (status == OperationStatus.SUCCESS); + } catch (Exception e) { + closeCursor(cursor); + throw handleException(e, doAutoCommit); + } + } + + /** + * Removes the specified key from this set if it is present (optional + * operation). + * If duplicates are allowed, this method removes all duplicates for the + * given key. + * This method conforms to the {@link Set#remove} interface. + * + * @throws UnsupportedOperationException if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean remove(Object key) { + + return removeKey(key, null); + } + + /** + * Returns true if this set contains the specified key. + * This method conforms to the {@link Set#contains} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean contains(Object key) { + + return containsKey(key); + } + + boolean hasValues() { + + return false; + } + + Object makeIteratorData(StoredIterator iterator, DataCursor cursor) + throws DatabaseException { + + return cursor.getCurrentKey(); + } + + boolean iterateDuplicates() { + + return false; + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredList.java b/db/java/src/com/sleepycat/collections/StoredList.java new file mode 100644 index 000000000..a65e5a734 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredList.java @@ -0,0 +1,604 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredList.java,v 1.4 2004/08/02 18:52:05 mjc Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.ListIterator; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.bind.RecordNumberBinding; +import com.sleepycat.db.Database; +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.OperationStatus; + +/** + * A List view of a {@link Database}. + * + * <p>For all stored lists the keys of the underlying Database + * must have record number format, and therefore the store or index must be a + * RECNO, RECNO-RENUMBER, QUEUE, or BTREE-RECNUM database. Only RECNO-RENUMBER + * allows true list behavior where record numbers are renumbered following the + * position of an element that is added or removed. For the other access + * methods (RECNO, QUEUE, and BTREE-RECNUM), stored Lists are most useful as + * read-only collections where record numbers are not required to be + * sequential.</p> + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * <p>In addition to the standard List methods, this class provides the + * following methods for stored lists only. Note that the use of these methods + * is not compatible with the standard Java collections interface.</p> + * <ul> + * <li>{@link #append(Object)}</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredList extends StoredCollection implements List { + + private static final EntryBinding DEFAULT_KEY_BINDING = + new IndexKeyBinding(1); + + private int baseIndex = 1; + private boolean isSubList; + + /** + * Creates a list view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param valueBinding is the binding used to translate between value + * buffers and value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public StoredList(Database database, EntryBinding valueBinding, + boolean writeAllowed) { + + super(new DataView(database, DEFAULT_KEY_BINDING, valueBinding, null, + writeAllowed, null)); + } + + /** + * Creates a list entity view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param valueEntityBinding is the binding used to translate between + * key/value buffers and entity value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public StoredList(Database database, EntityBinding valueEntityBinding, + boolean writeAllowed) { + + super(new DataView(database, DEFAULT_KEY_BINDING, null, + valueEntityBinding, writeAllowed, null)); + } + + /** + * Creates a list view of a {@link Database} with a {@link + * PrimaryKeyAssigner}. Writing is allowed for the created list. + * + * @param database is the Database underlying the new collection. + * + * @param valueBinding is the binding used to translate between value + * buffers and value objects. + * + * @param keyAssigner is used by the {@link #add} and {@link #append} + * methods to assign primary keys. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public StoredList(Database database, EntryBinding valueBinding, + PrimaryKeyAssigner keyAssigner) { + + super(new DataView(database, DEFAULT_KEY_BINDING, valueBinding, + null, true, keyAssigner)); + } + + /** + * Creates a list entity view of a {@link Database} with a {@link + * PrimaryKeyAssigner}. Writing is allowed for the created list. + * + * @param database is the Database underlying the new collection. + * + * @param valueEntityBinding is the binding used to translate between + * key/value buffers and entity value objects. + * + * @param keyAssigner is used by the {@link #add} and {@link #append} + * methods to assign primary keys. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public StoredList(Database database, EntityBinding valueEntityBinding, + PrimaryKeyAssigner keyAssigner) { + + super(new DataView(database, DEFAULT_KEY_BINDING, null, + valueEntityBinding, true, keyAssigner)); + } + + private StoredList(DataView view, int baseIndex) { + + super(view); + this.baseIndex = baseIndex; + this.isSubList = true; + } + + /** + * Inserts the specified element at the specified position in this list + * (optional operation). + * This method conforms to the {@link List#add(int, Object)} interface. + * + * @throws UnsupportedOperationException if the collection is a sublist, or + * if the collection is indexed, or if the collection is read-only, or if + * the RECNO-RENUMBER access method was not used. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public void add(int index, Object value) { + + checkIterAddAllowed(); + DataCursor cursor = null; + boolean doAutoCommit = beginAutoCommit(); + try { + cursor = new DataCursor(view, true); + OperationStatus status = + cursor.getSearchKey(new Long(index), null, false); + if (status == OperationStatus.SUCCESS) { + cursor.putBefore(value); + closeCursor(cursor); + } else { + closeCursor(cursor); + cursor = null; + view.append(value, null, null); + } + commitAutoCommit(doAutoCommit); + } catch (Exception e) { + closeCursor(cursor); + throw handleException(e, doAutoCommit); + } + } + + /** + * Appends the specified element to the end of this list (optional + * operation). + * This method conforms to the {@link List#add(Object)} interface. + * + * @throws UnsupportedOperationException if the collection is a sublist, or + * if the collection is indexed, or if the collection is read-only, or if + * the RECNO-RENUMBER access method was not used. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean add(Object value) { + + checkIterAddAllowed(); + boolean doAutoCommit = beginAutoCommit(); + try { + view.append(value, null, null); + commitAutoCommit(doAutoCommit); + return true; + } catch (Exception e) { + throw handleException(e, doAutoCommit); + } + } + + /** + * Appends a given value returning the newly assigned index. + * If a {@link com.sleepycat.collections.PrimaryKeyAssigner} is associated + * with Store for this list, it will be used to assigned the returned + * index. Otherwise the Store must be a QUEUE or RECNO database and the + * next available record number is assigned as the index. This method does + * not exist in the standard {@link List} interface. + * + * @param value the value to be appended. + * + * @return the assigned index. + * + * @throws UnsupportedOperationException if the collection is indexed, or + * if the collection is read-only, or if the Store has no {@link + * com.sleepycat.collections.PrimaryKeyAssigner} and is not a QUEUE or + * RECNO database. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public int append(Object value) { + + boolean doAutoCommit = beginAutoCommit(); + try { + Object[] key = new Object[1]; + view.append(value, key, null); + commitAutoCommit(doAutoCommit); + return ((Number) key[0]).intValue(); + } catch (Exception e) { + throw handleException(e, doAutoCommit); + } + } + + void checkIterAddAllowed() + throws UnsupportedOperationException { + + if (isSubList) { + throw new UnsupportedOperationException("cannot add to subList"); + } + if (!view.keysRenumbered) { // RECNO-RENUM + throw new UnsupportedOperationException( + "requires renumbered keys"); + } + } + + /** + * Inserts all of the elements in the specified collection into this list + * at the specified position (optional operation). + * This method conforms to the {@link List#addAll(int, Collection)} + * interface. + * + * @throws UnsupportedOperationException if the collection is a sublist, or + * if the collection is indexed, or if the collection is read-only, or if + * the RECNO-RENUMBER access method was not used. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean addAll(int index, Collection coll) { + + checkIterAddAllowed(); + DataCursor cursor = null; + Iterator i = null; + boolean doAutoCommit = beginAutoCommit(); + try { + i = coll.iterator(); + if (!i.hasNext()) { + return false; + } + cursor = new DataCursor(view, true); + OperationStatus status = + cursor.getSearchKey(new Long(index), null, false); + if (status == OperationStatus.SUCCESS) { + while (i.hasNext()) { + cursor.putBefore(i.next()); + } + closeCursor(cursor); + } else { + closeCursor(cursor); + cursor = null; + while (i.hasNext()) { + view.append(i.next(), null, null); + } + } + StoredIterator.close(i); + commitAutoCommit(doAutoCommit); + return true; + } catch (Exception e) { + closeCursor(cursor); + StoredIterator.close(i); + throw handleException(e, doAutoCommit); + } + } + + /** + * Returns true if this list contains the specified element. + * This method conforms to the {@link List#contains} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean contains(Object value) { + + return containsValue(value); + } + + /** + * Returns the element at the specified position in this list. + * This method conforms to the {@link List#get} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public Object get(int index) { + + return super.get(new Long(index)); + } + + /** + * Returns the index in this list of the first occurrence of the specified + * element, or -1 if this list does not contain this element. + * This method conforms to the {@link List#indexOf} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public int indexOf(Object value) { + + return indexOf(value, true); + } + + /** + * Returns the index in this list of the last occurrence of the specified + * element, or -1 if this list does not contain this element. + * This method conforms to the {@link List#lastIndexOf} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public int lastIndexOf(Object value) { + + return indexOf(value, false); + } + + private int indexOf(Object value, boolean findFirst) { + + DataCursor cursor = null; + try { + cursor = new DataCursor(view, false); + OperationStatus status = cursor.find(value, findFirst); + return (status == OperationStatus.SUCCESS) + ? (cursor.getCurrentRecordNumber() - baseIndex) + : (-1); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } finally { + closeCursor(cursor); + } + } + + int getIndexOffset() { + + return baseIndex; + } + + /** + * Returns a list iterator of the elements in this list (in proper + * sequence). + * The iterator will be read-only if the collection is read-only. + * This method conforms to the {@link List#listIterator()} interface. + * + * @return a {@link StoredIterator} for this collection. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + * + * @see #isWriteAllowed + */ + public ListIterator listIterator() { + + return iterator(isWriteAllowed()); + } + + /** + * Returns a list iterator of the elements in this list (in proper + * sequence), starting at the specified position in this list. + * The iterator will be read-only if the collection is read-only. + * This method conforms to the {@link List#listIterator(int)} interface. + * + * @return a {@link StoredIterator} for this collection. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + * + * @see #isWriteAllowed + */ + public ListIterator listIterator(int index) { + + StoredIterator i = iterator(isWriteAllowed()); + if (i.moveToIndex(index)) { + return i; + } else { + i.close(); + throw new IndexOutOfBoundsException(String.valueOf(index)); + } + } + + /** + * Removes the element at the specified position in this list (optional + * operation). + * This method conforms to the {@link List#remove(int)} interface. + * + * @throws UnsupportedOperationException if the collection is a sublist, or + * if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public Object remove(int index) { + + try { + Object[] oldVal = new Object[1]; + removeKey(new Long(index), oldVal); + return oldVal[0]; + } catch (IllegalArgumentException e) { + throw new IndexOutOfBoundsException(e.getMessage()); + } + } + + /** + * Removes the first occurrence in this list of the specified element + * (optional operation). + * This method conforms to the {@link List#remove(Object)} interface. + * + * @throws UnsupportedOperationException if the collection is a sublist, or + * if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean remove(Object value) { + + return removeValue(value); + } + + /** + * Replaces the element at the specified position in this list with the + * specified element (optional operation). + * This method conforms to the {@link List#set} interface. + * + * @throws UnsupportedOperationException if the collection is indexed, or + * if the collection is read-only. + * + * @throws IllegalArgumentException if an entity value binding is used and + * the primary key of the value given is different than the existing stored + * primary key. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public Object set(int index, Object value) { + + try { + return put(new Long(index), value); + } catch (IllegalArgumentException e) { + throw new IndexOutOfBoundsException(e.getMessage()); + } + } + + /** + * Returns a view of the portion of this list between the specified + * fromIndex, inclusive, and toIndex, exclusive. + * Note that add() and remove() may not be called for the returned sublist. + * This method conforms to the {@link List#subList} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public List subList(int fromIndex, int toIndex) { + + if (fromIndex < 0 || fromIndex > toIndex) { + throw new IndexOutOfBoundsException(String.valueOf(fromIndex)); + } + try { + int newBaseIndex = baseIndex + fromIndex; + return new StoredList( + view.subView(new Long(fromIndex), true, + new Long(toIndex), false, + new IndexKeyBinding(newBaseIndex)), + newBaseIndex); + } catch (KeyRangeException e) { + throw new IndexOutOfBoundsException(e.getMessage()); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Compares the specified object with this list for equality. + * A value comparison is performed by this method and the stored values + * are compared rather than calling the equals() method of each element. + * This method conforms to the {@link List#equals} interface. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean equals(Object other) { + + if (!(other instanceof List)) return false; + List otherList = (List) other; + ListIterator i1 = null; + ListIterator i2 = null; + try { + i1 = listIterator(); + i2 = otherList.listIterator(); + while (i1.hasNext()) { + if (!i2.hasNext()) return false; + if (i1.nextIndex() != i2.nextIndex()) return false; + Object o1 = i1.next(); + Object o2 = i2.next(); + if (o1 == null) { + if (o2 != null) return false; + } else { + if (!o1.equals(o2)) return false; + } + } + if (i2.hasNext()) return false; + return true; + } finally { + StoredIterator.close(i1); + StoredIterator.close(i2); + } + } + + /* + * Add this in to keep FindBugs from whining at us about implementing + * equals(), but not hashCode(). + */ + public int hashCode() { + return super.hashCode(); + } + + Object makeIteratorData(StoredIterator iterator, DataCursor cursor) + throws DatabaseException { + + return cursor.getCurrentValue(); + } + + boolean hasValues() { + + return true; + } + + private static class IndexKeyBinding extends RecordNumberBinding { + + private int baseIndex; + + private IndexKeyBinding(int baseIndex) { + + this.baseIndex = baseIndex; + } + + public Object entryToObject(DatabaseEntry data) { + + return new Long(entryToRecordNumber(data) - baseIndex); + } + + public void objectToEntry(Object object, DatabaseEntry data) { + + recordNumberToEntry(((Number) object).intValue() + baseIndex, + data); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredMap.java b/db/java/src/com/sleepycat/collections/StoredMap.java new file mode 100644 index 000000000..1c0e1dcf1 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredMap.java @@ -0,0 +1,511 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredMap.java,v 1.4 2004/09/22 18:01:03 bostic Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Collection; +import java.util.Collections; +import java.util.Iterator; +import java.util.Map; +import java.util.Set; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.db.Database; + +/** + * A Map view of a {@link Database}. + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * <p>In addition to the standard Map methods, this class provides the + * following methods for stored maps only. Note that the use of these methods + * is not compatible with the standard Java collections interface.</p> + * <ul> + * <li>{@link #duplicates(Object)}</li> + * <li>{@link #append(Object)}</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredMap extends StoredContainer implements Map { + + private StoredKeySet keySet; + private boolean keySetInitialized = false; + private StoredEntrySet entrySet; + private boolean entrySetInitialized = false; + private StoredValueSet valueSet; + private boolean valueSetInitialized = false; + + /** + * Creates a map view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param valueBinding is the binding used to translate between value + * buffers and value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredMap(Database database, EntryBinding keyBinding, + EntryBinding valueBinding, boolean writeAllowed) { + + super(new DataView(database, keyBinding, valueBinding, null, + writeAllowed, null)); + } + + /** + * Creates a map view of a {@link Database} with a {@link + * PrimaryKeyAssigner}. Writing is allowed for the created map. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param valueBinding is the binding used to translate between value + * buffers and value objects. + * + * @param keyAssigner is used by the {@link #append} method to assign + * primary keys. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredMap(Database database, EntryBinding keyBinding, + EntryBinding valueBinding, + PrimaryKeyAssigner keyAssigner) { + + super(new DataView(database, keyBinding, valueBinding, null, + true, keyAssigner)); + } + + protected Object clone() + throws CloneNotSupportedException { + + // cached collections must be cleared and recreated with the new view + // of the map to inherit the new view's properties + StoredMap other = (StoredMap) super.clone(); + other.keySet = null; + other.keySetInitialized = false; + other.entrySet = null; + other.entrySetInitialized = false; + other.valueSet = null; + other.valueSetInitialized = false; + return other; + } + + /** + * Creates a map entity view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param valueEntityBinding is the binding used to translate between + * key/value buffers and entity value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredMap(Database database, EntryBinding keyBinding, + EntityBinding valueEntityBinding, boolean writeAllowed) { + + super(new DataView(database, keyBinding, null, valueEntityBinding, + writeAllowed, null)); + } + + /** + * Creates a map entity view of a {@link Database} with a {@link + * PrimaryKeyAssigner}. Writing is allowed for the created map. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param valueEntityBinding is the binding used to translate between + * key/value buffers and entity value objects. + * + * @param keyAssigner is used by the {@link #append} method to assign + * primary keys. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredMap(Database database, EntryBinding keyBinding, + EntityBinding valueEntityBinding, + PrimaryKeyAssigner keyAssigner) { + + super(new DataView(database, keyBinding, null, valueEntityBinding, + true, keyAssigner)); + } + + StoredMap(DataView view) { + + super(view); + } + + /** + * Returns the value to which this map maps the specified key. If + * duplicates are allowed, this method returns the first duplicate, in the + * order in which duplicates are configured, that maps to the specified + * key. + * + * This method conforms to the {@link Map#get} interface. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object get(Object key) { + + return super.get(key); + } + + /** + * Associates the specified value with the specified key in this map + * (optional operation). If duplicates are allowed and the specified key + * is already mapped to a value, this method appends the new duplicate + * after the existing duplicates. This method conforms to the {@link + * Map#put} interface. + * + * <p>The key parameter may be null if an entity binding is used and the + * key will be derived from the value (entity) parameter. If an entity + * binding is used and the key parameter is non-null, then the key + * parameter must be equal to the key derived from the value parameter.</p> + * + * @return the previous value associated with specified key, or null if + * there was no mapping for the key or if duplicates are allowed. + * + * @throws UnsupportedOperationException if the collection is indexed, or + * if the collection is read-only. + * + * @throws IllegalArgumentException if an entity value binding is used and + * the primary key of the value given is different than the existing stored + * primary key. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object put(Object key, Object value) { + + return super.put(key, value); + } + + /** + * Appends a given value returning the newly assigned key. If a {@link + * PrimaryKeyAssigner} is associated with Store for this map, it will be + * used to assigned the returned key. Otherwise the Store must be a QUEUE + * or RECNO database and the next available record number is assigned as + * the key. This method does not exist in the standard {@link Map} + * interface. + * + * @param value the value to be appended. + * + * @return the assigned key. + * + * @throws UnsupportedOperationException if the collection is indexed, or + * if the collection is read-only, or if the Store has no {@link + * PrimaryKeyAssigner} and is not a QUEUE or RECNO database. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object append(Object value) { + + boolean doAutoCommit = beginAutoCommit(); + try { + Object[] key = new Object[1]; + view.append(value, key, null); + commitAutoCommit(doAutoCommit); + return key[0]; + } catch (Exception e) { + throw handleException(e, doAutoCommit); + } + } + + /** + * Removes the mapping for this key from this map if present (optional + * operation). If duplicates are allowed, this method removes all + * duplicates for the given key. This method conforms to the {@link + * Map#remove} interface. + * + * @throws UnsupportedOperationException if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object remove(Object key) { + + Object[] oldVal = new Object[1]; + removeKey(key, oldVal); + return oldVal[0]; + } + + /** + * Returns true if this map contains the specified key. This method + * conforms to the {@link Map#containsKey} interface. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public boolean containsKey(Object key) { + + return super.containsKey(key); + } + + /** + * Returns true if this map contains the specified value. When an entity + * binding is used, this method returns whether the map contains the + * primary key and value mapping of the entity. This method conforms to + * the {@link Map#containsValue} interface. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public boolean containsValue(Object value) { + + return super.containsValue(value); + } + + /** + * Copies all of the mappings from the specified map to this map (optional + * operation). When duplicates are allowed, the mappings in the specified + * map are effectively appended to the existing mappings in this map, that + * is no previously existing mappings in this map are replaced. This + * method conforms to the {@link Map#putAll} interface. + * + * @throws UnsupportedOperationException if the collection is read-only, or + * if the collection is indexed. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public void putAll(Map map) { + + boolean doAutoCommit = beginAutoCommit(); + Iterator entries = null; + try { + entries = map.entrySet().iterator(); + while (entries.hasNext()) { + Map.Entry entry = (Map.Entry) entries.next(); + put(entry.getKey(), entry.getValue()); + } + StoredIterator.close(entries); + commitAutoCommit(doAutoCommit); + } catch (Exception e) { + StoredIterator.close(entries); + throw handleException(e, doAutoCommit); + } + } + + /** + * Returns a set view of the keys contained in this map. A {@link + * java.util.SortedSet} is returned if the map is ordered. The returned + * collection will be read-only if the map is read-only. This method + * conforms to the {@link Map#keySet()} interface. + * + * @return a {@link StoredKeySet} or a {@link StoredSortedKeySet} for this + * map. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + * + * @see #isOrdered + * @see #isWriteAllowed + */ + public Set keySet() { + + if (!keySetInitialized) { + synchronized (this) { + if (!keySetInitialized) { + DataView newView = view.keySetView(); + if (isOrdered()) { + keySet = new StoredSortedKeySet(newView); + } else { + keySet = new StoredKeySet(newView); + } + keySetInitialized = true; + } + } + } + return keySet; + } + + /** + * Returns a set view of the mappings contained in this map. A {@link + * java.util.SortedSet} is returned if the map is ordered. The returned + * collection will be read-only if the map is read-only. This method + * conforms to the {@link Map#entrySet()} interface. + * + * @return a {@link StoredEntrySet} or a {@link StoredSortedEntrySet} for + * this map. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + * + * @see #isOrdered + * @see #isWriteAllowed + */ + public Set entrySet() { + + if (!entrySetInitialized) { + synchronized (this) { + if (!entrySetInitialized) { + if (isOrdered()) { + entrySet = new StoredSortedEntrySet(view); + } else { + entrySet = new StoredEntrySet(view); + } + entrySetInitialized = true; + } + } + } + return entrySet; + } + + /** + * Returns a collection view of the values contained in this map. A {@link + * java.util.SortedSet} is returned if the map is ordered and the + * value/entity binding can be used to derive the map's key from its + * value/entity object. The returned collection will be read-only if the + * map is read-only. This method conforms to the {@link Map#entrySet()} + * interface. + * + * @return a {@link StoredValueSet} or a {@link StoredSortedValueSet} for + * this map. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + * + * @see #isOrdered + * @see #isWriteAllowed + */ + public Collection values() { + + if (!valueSetInitialized) { + synchronized (this) { + if (!valueSetInitialized) { + DataView newView = view.valueSetView(); + if (isOrdered() && newView.canDeriveKeyFromValue()) { + valueSet = new StoredSortedValueSet(newView); + } else { + valueSet = new StoredValueSet(newView); + } + valueSetInitialized = true; + } + } + } + return valueSet; + } + + /** + * Returns a new collection containing the values mapped to the given key + * in this map. This collection's iterator() method is particularly useful + * for iterating over the duplicates for a given key, since this is not + * supported by the standard Map interface. This method does not exist in + * the standard {@link Map} interface. + * + * <p>If no mapping for the given key is present, an empty collection is + * returned. If duplicates are not allowed, at most a single value will be + * in the collection returned. If duplicates are allowed, the returned + * collection's add() method may be used to add values for the given + * key.</p> + * + * @param key is the key for which values are to be returned. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Collection duplicates(Object key) { + + try { + DataView newView = view.valueSetView(key); + return new StoredValueSet(newView, true); + } catch (KeyRangeException e) { + return Collections.EMPTY_SET; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } + + /** + * Compares the specified object with this map for equality. A value + * comparison is performed by this method and the stored values are + * compared rather than calling the equals() method of each element. This + * method conforms to the {@link Map#equals} interface. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public boolean equals(Object other) { + + if (other instanceof Map) { + return entrySet().equals(((Map) other).entrySet()); + } else { + return false; + } + } + + /* + * Add this in to keep FindBugs from whining at us about implementing + * equals(), but not hashCode(). + */ + public int hashCode() { + return super.hashCode(); + } + + /** + * Converts the map to a string representation for debugging. WARNING: All + * mappings will be converted to strings and returned and therefore the + * returned string may be very large. + * + * @return the string representation. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public String toString() { + + return entrySet().toString(); + } +} + diff --git a/db/java/src/com/sleepycat/collections/StoredMapEntry.java b/db/java/src/com/sleepycat/collections/StoredMapEntry.java new file mode 100644 index 000000000..020e878e4 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredMapEntry.java @@ -0,0 +1,41 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredMapEntry.java,v 1.1 2004/04/09 16:34:09 mark Exp $ + */ + +package com.sleepycat.collections; + +/** + * @author Mark Hayes + */ +final class StoredMapEntry extends MapEntryParameter { + + private StoredIterator iter; + private StoredCollection coll; + + StoredMapEntry(Object key, Object value, StoredCollection coll, + StoredIterator iter) { + + super(key, value); + // Assert: coll, coll.keyBinding/valueBinding + this.coll = coll; + this.iter = iter; + } + + public Object setValue(Object newValue) { + + Object oldValue; + if (iter != null && iter.isCurrentData(this)) { + oldValue = getValue(); + iter.set(newValue); + } else { + oldValue = coll.put(getKey(), newValue); + } + setValueInternal(newValue); + return oldValue; + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredSortedEntrySet.java b/db/java/src/com/sleepycat/collections/StoredSortedEntrySet.java new file mode 100644 index 000000000..99fceab9b --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredSortedEntrySet.java @@ -0,0 +1,220 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredSortedEntrySet.java,v 1.2 2004/06/02 20:59:39 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Comparator; +import java.util.Map; +import java.util.SortedSet; + +/** + * The SortedSet returned by Map.entrySet(). This class may not be + * instantiated directly. Contrary to what is stated by {@link Map#entrySet} + * this class does support the {@link #add} and {@link #addAll} methods. + * + * <p>The {@link java.util.Map.Entry#setValue} method of the Map.Entry objects + * that are returned by this class and its iterators behaves just as the {@link + * StoredIterator#set} method does.</p> + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * <p>In addition to the standard SortedSet methods, this class provides the + * following methods for stored sorted sets only. Note that the use of these + * methods is not compatible with the standard Java collections interface.</p> + * <ul> + * <li>{@link #headSet(Object, boolean)}</li> + * <li>{@link #tailSet(Object, boolean)}</li> + * <li>{@link #subSet(Object, boolean, Object, boolean)}</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredSortedEntrySet extends StoredEntrySet implements SortedSet { + + StoredSortedEntrySet(DataView mapView) { + + super(mapView); + } + + /** + * Returns null since comparators are not supported. The natural ordering + * of a stored collection is data byte order, whether the data classes + * implement the {@link java.lang.Comparable} interface or not. + * This method does not conform to the {@link SortedSet#comparator} + * interface. + * + * @return null. + */ + public Comparator comparator() { + + return null; + } + + /** + * Returns the first (lowest) element currently in this sorted set. + * This method conforms to the {@link SortedSet#first} interface. + * + * @return the first element. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object first() { + + return getFirstOrLast(true); + } + + /** + * Returns the last (highest) element currently in this sorted set. + * This method conforms to the {@link SortedSet#last} interface. + * + * @return the last element. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object last() { + + return getFirstOrLast(false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly less than toMapEntry. + * This method conforms to the {@link SortedSet#headSet} interface. + * + * @param toMapEntry the upper bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet headSet(Object toMapEntry) { + + return subSet(null, false, toMapEntry, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly less than toMapEntry, optionally including toMapEntry. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param toMapEntry is the upper bound. + * + * @param toInclusive is true to include toMapEntry. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet headSet(Object toMapEntry, boolean toInclusive) { + + return subSet(null, false, toMapEntry, toInclusive); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * greater than or equal to fromMapEntry. + * This method conforms to the {@link SortedSet#tailSet} interface. + * + * @param fromMapEntry is the lower bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet tailSet(Object fromMapEntry) { + + return subSet(fromMapEntry, true, null, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly greater than fromMapEntry, optionally including fromMapEntry. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param fromMapEntry is the lower bound. + * + * @param fromInclusive is true to include fromMapEntry. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet tailSet(Object fromMapEntry, boolean fromInclusive) { + + return subSet(fromMapEntry, fromInclusive, null, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements range + * from fromMapEntry, inclusive, to toMapEntry, exclusive. + * This method conforms to the {@link SortedSet#subSet} interface. + * + * @param fromMapEntry is the lower bound. + * + * @param toMapEntry is the upper bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet subSet(Object fromMapEntry, Object toMapEntry) { + + return subSet(fromMapEntry, true, toMapEntry, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly greater than fromMapEntry and strictly less than toMapEntry, + * optionally including fromMapEntry and toMapEntry. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param fromMapEntry is the lower bound. + * + * @param fromInclusive is true to include fromMapEntry. + * + * @param toMapEntry is the upper bound. + * + * @param toInclusive is true to include toMapEntry. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet subSet(Object fromMapEntry, boolean fromInclusive, + Object toMapEntry, boolean toInclusive) { + + Object fromKey = (fromMapEntry != null) ? + ((Map.Entry) fromMapEntry).getKey() : null; + Object toKey = (toMapEntry != null) ? + ((Map.Entry) toMapEntry).getKey() : null; + try { + return new StoredSortedEntrySet( + view.subView(fromKey, fromInclusive, toKey, toInclusive, null)); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredSortedKeySet.java b/db/java/src/com/sleepycat/collections/StoredSortedKeySet.java new file mode 100644 index 000000000..44b12d55a --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredSortedKeySet.java @@ -0,0 +1,241 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredSortedKeySet.java,v 1.2 2004/06/02 20:59:39 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Comparator; +import java.util.SortedSet; + +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.db.Database; + +/** + * The SortedSet returned by Map.keySet() and which can also be constructed + * directly if a Map is not needed. + * Since this collection is a set it only contains one element for each key, + * even when duplicates are allowed. Key set iterators are therefore + * particularly useful for enumerating the unique keys of a store or index that + * allows duplicates. + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * <p>In addition to the standard SortedSet methods, this class provides the + * following methods for stored sorted sets only. Note that the use of these + * methods is not compatible with the standard Java collections interface.</p> + * <ul> + * <li>{@link #headSet(Object, boolean)}</li> + * <li>{@link #tailSet(Object, boolean)}</li> + * <li>{@link #subSet(Object, boolean, Object, boolean)}</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredSortedKeySet extends StoredKeySet implements SortedSet { + + /** + * Creates a sorted key set view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredSortedKeySet(Database database, EntryBinding keyBinding, + boolean writeAllowed) { + + super(new DataView(database, keyBinding, null, null, + writeAllowed, null)); + } + + StoredSortedKeySet(DataView keySetView) { + + super(keySetView); + } + + /** + * Returns null since comparators are not supported. The natural ordering + * of a stored collection is data byte order, whether the data classes + * implement the {@link java.lang.Comparable} interface or not. + * This method does not conform to the {@link SortedSet#comparator} + * interface. + * + * @return null. + */ + public Comparator comparator() { + + return null; + } + + /** + * Returns the first (lowest) element currently in this sorted set. + * This method conforms to the {@link SortedSet#first} interface. + * + * @return the first element. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object first() { + + return getFirstOrLast(true); + } + + /** + * Returns the last (highest) element currently in this sorted set. + * This method conforms to the {@link SortedSet#last} interface. + * + * @return the last element. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object last() { + + return getFirstOrLast(false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly less than toKey. + * This method conforms to the {@link SortedSet#headSet} interface. + * + * @param toKey is the upper bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet headSet(Object toKey) { + + return subSet(null, false, toKey, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly less than toKey, optionally including toKey. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param toKey is the upper bound. + * + * @param toInclusive is true to include toKey. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet headSet(Object toKey, boolean toInclusive) { + + return subSet(null, false, toKey, toInclusive); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * greater than or equal to fromKey. + * This method conforms to the {@link SortedSet#tailSet} interface. + * + * @param fromKey is the lower bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet tailSet(Object fromKey) { + + return subSet(fromKey, true, null, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly greater than fromKey, optionally including fromKey. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param fromKey is the lower bound. + * + * @param fromInclusive is true to include fromKey. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet tailSet(Object fromKey, boolean fromInclusive) { + + return subSet(fromKey, fromInclusive, null, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements range + * from fromKey, inclusive, to toKey, exclusive. + * This method conforms to the {@link SortedSet#subSet} interface. + * + * @param fromKey is the lower bound. + * + * @param toKey is the upper bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet subSet(Object fromKey, Object toKey) { + + return subSet(fromKey, true, toKey, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly greater than fromKey and strictly less than toKey, + * optionally including fromKey and toKey. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param fromKey is the lower bound. + * + * @param fromInclusive is true to include fromKey. + * + * @param toKey is the upper bound. + * + * @param toInclusive is true to include toKey. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet subSet(Object fromKey, boolean fromInclusive, + Object toKey, boolean toInclusive) { + + try { + return new StoredSortedKeySet( + view.subView(fromKey, fromInclusive, toKey, toInclusive, null)); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredSortedMap.java b/db/java/src/com/sleepycat/collections/StoredSortedMap.java new file mode 100644 index 000000000..142e75ff4 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredSortedMap.java @@ -0,0 +1,348 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredSortedMap.java,v 1.3 2004/09/22 18:01:03 bostic Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Comparator; +import java.util.SortedMap; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.db.Database; +import com.sleepycat.db.OperationStatus; + +/** + * A SortedMap view of a {@link Database}. + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * <p>In addition to the standard SortedMap methods, this class provides the + * following methods for stored sorted maps only. Note that the use of these + * methods is not compatible with the standard Java collections interface.</p> + * <ul> + * <li>{@link #duplicates(Object)}</li> + * <li>{@link #headMap(Object, boolean)}</li> + * <li>{@link #tailMap(Object, boolean)}</li> + * <li>{@link #subMap(Object, boolean, Object, boolean)}</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredSortedMap extends StoredMap implements SortedMap { + + /** + * Creates a sorted map view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param valueBinding is the binding used to translate between value + * buffers and value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredSortedMap(Database database, EntryBinding keyBinding, + EntryBinding valueBinding, boolean writeAllowed) { + + super(new DataView(database, keyBinding, valueBinding, null, + writeAllowed, null)); + } + + /** + * Creates a sorted map view of a {@link Database} with a {@link + * PrimaryKeyAssigner}. Writing is allowed for the created map. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param valueBinding is the binding used to translate between value + * buffers and value objects. + * + * @param keyAssigner is used by the {@link #append} method to assign + * primary keys. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredSortedMap(Database database, EntryBinding keyBinding, + EntryBinding valueBinding, + PrimaryKeyAssigner keyAssigner) { + + super(new DataView(database, keyBinding, valueBinding, null, + true, keyAssigner)); + } + + /** + * Creates a sorted map entity view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param valueEntityBinding is the binding used to translate between + * key/value buffers and entity value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredSortedMap(Database database, EntryBinding keyBinding, + EntityBinding valueEntityBinding, + boolean writeAllowed) { + + super(new DataView(database, keyBinding, null, valueEntityBinding, + writeAllowed, null)); + } + + /** + * Creates a sorted map entity view of a {@link Database} with a {@link + * PrimaryKeyAssigner}. Writing is allowed for the created map. + * + * @param database is the Database underlying the new collection. + * + * @param keyBinding is the binding used to translate between key buffers + * and key objects. + * + * @param valueEntityBinding is the binding used to translate between + * key/value buffers and entity value objects. + * + * @param keyAssigner is used by the {@link #append} method to assign + * primary keys. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredSortedMap(Database database, EntryBinding keyBinding, + EntityBinding valueEntityBinding, + PrimaryKeyAssigner keyAssigner) { + + super(new DataView(database, keyBinding, null, valueEntityBinding, + true, keyAssigner)); + } + + StoredSortedMap(DataView mapView) { + + super(mapView); + } + + /** + * Returns null since comparators are not supported. The natural ordering + * of a stored collection is data byte order, whether the data classes + * implement the {@link java.lang.Comparable} interface or not. + * This method does not conform to the {@link SortedMap#comparator} + * interface. + * + * @return null. + */ + public Comparator comparator() { + + return null; + } + + /** + * Returns the first (lowest) key currently in this sorted map. + * This method conforms to the {@link SortedMap#firstKey} interface. + * + * @return the first key. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object firstKey() { + + return getFirstOrLastKey(true); + } + + /** + * Returns the last (highest) element currently in this sorted map. + * This method conforms to the {@link SortedMap#lastKey} interface. + * + * @return the last key. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object lastKey() { + + return getFirstOrLastKey(false); + } + + private Object getFirstOrLastKey(boolean doGetFirst) { + + DataCursor cursor = null; + try { + cursor = new DataCursor(view, false); + OperationStatus status; + if (doGetFirst) { + status = cursor.getFirst(false); + } else { + status = cursor.getLast(false); + } + return (status == OperationStatus.SUCCESS) ? + cursor.getCurrentKey() : null; + } catch (Exception e) { + throw StoredContainer.convertException(e); + } finally { + closeCursor(cursor); + } + } + + /** + * Returns a view of the portion of this sorted set whose keys are + * strictly less than toKey. + * This method conforms to the {@link SortedMap#headMap} interface. + * + * @param toKey is the upper bound. + * + * @return the submap. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedMap headMap(Object toKey) { + + return subMap(null, false, toKey, false); + } + + /** + * Returns a view of the portion of this sorted map whose elements are + * strictly less than toKey, optionally including toKey. + * This method does not exist in the standard {@link SortedMap} interface. + * + * @param toKey is the upper bound. + * + * @param toInclusive is true to include toKey. + * + * @return the submap. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedMap headMap(Object toKey, boolean toInclusive) { + + return subMap(null, false, toKey, toInclusive); + } + + /** + * Returns a view of the portion of this sorted map whose elements are + * greater than or equal to fromKey. + * This method conforms to the {@link SortedMap#tailMap} interface. + * + * @param fromKey is the lower bound. + * + * @return the submap. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedMap tailMap(Object fromKey) { + + return subMap(fromKey, true, null, false); + } + + /** + * Returns a view of the portion of this sorted map whose elements are + * strictly greater than fromKey, optionally including fromKey. + * This method does not exist in the standard {@link SortedMap} interface. + * + * @param fromKey is the lower bound. + * + * @param fromInclusive is true to include fromKey. + * + * @return the submap. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedMap tailMap(Object fromKey, boolean fromInclusive) { + + return subMap(fromKey, fromInclusive, null, false); + } + + /** + * Returns a view of the portion of this sorted map whose elements range + * from fromKey, inclusive, to toKey, exclusive. + * This method conforms to the {@link SortedMap#subMap} interface. + * + * @param fromKey is the lower bound. + * + * @param toKey is the upper bound. + * + * @return the submap. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedMap subMap(Object fromKey, Object toKey) { + + return subMap(fromKey, true, toKey, false); + } + + /** + * Returns a view of the portion of this sorted map whose elements are + * strictly greater than fromKey and strictly less than toKey, + * optionally including fromKey and toKey. + * This method does not exist in the standard {@link SortedMap} interface. + * + * @param fromKey is the lower bound. + * + * @param fromInclusive is true to include fromKey. + * + * @param toKey is the upper bound. + * + * @param toInclusive is true to include toKey. + * + * @return the submap. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedMap subMap(Object fromKey, boolean fromInclusive, + Object toKey, boolean toInclusive) { + + try { + return new StoredSortedMap( + view.subView(fromKey, fromInclusive, toKey, toInclusive, null)); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredSortedValueSet.java b/db/java/src/com/sleepycat/collections/StoredSortedValueSet.java new file mode 100644 index 000000000..96c534137 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredSortedValueSet.java @@ -0,0 +1,255 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredSortedValueSet.java,v 1.2 2004/06/02 20:59:39 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Comparator; +import java.util.SortedSet; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.db.Database; + +/** + * The SortedSet returned by Map.values() and which can also be constructed + * directly if a Map is not needed. + * Although this collection is a set it may contain duplicate values. Only if + * an entity value binding is used are all elements guaranteed to be unique. + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * <p>In addition to the standard SortedSet methods, this class provides the + * following methods for stored sorted value sets only. Note that the use of + * these methods is not compatible with the standard Java collections + * interface.</p> + * <ul> + * <li>{@link #headSet(Object, boolean)}</li> + * <li>{@link #tailSet(Object, boolean)}</li> + * <li>{@link #subSet(Object, boolean, Object, boolean)}</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredSortedValueSet extends StoredValueSet implements SortedSet { + + /* + * No valueBinding ctor is possible since key cannot be derived. + */ + + /** + * Creates a sorted value set entity view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param valueEntityBinding is the binding used to translate between + * key/value buffers and entity value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public StoredSortedValueSet(Database database, + EntityBinding valueEntityBinding, + boolean writeAllowed) { + + super(new DataView(database, null, null, valueEntityBinding, + writeAllowed, null)); + checkKeyDerivation(); + } + + StoredSortedValueSet(DataView valueSetView) { + + super(valueSetView); + checkKeyDerivation(); + } + + private void checkKeyDerivation() { + + if (!view.canDeriveKeyFromValue()) { + throw new IllegalArgumentException("Cannot derive key from value"); + } + } + + /** + * Returns null since comparators are not supported. The natural ordering + * of a stored collection is data byte order, whether the data classes + * implement the {@link java.lang.Comparable} interface or not. + * This method does not conform to the {@link SortedSet#comparator} + * interface. + * + * @return null. + */ + public Comparator comparator() { + + return null; + } + + /** + * Returns the first (lowest) element currently in this sorted set. + * This method conforms to the {@link SortedSet#first} interface. + * + * @return the first element. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object first() { + + return getFirstOrLast(true); + } + + /** + * Returns the last (highest) element currently in this sorted set. + * This method conforms to the {@link SortedSet#last} interface. + * + * @return the last element. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public Object last() { + + return getFirstOrLast(false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly less than toValue. + * This method conforms to the {@link SortedSet#headSet} interface. + * + * @param toValue the upper bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet headSet(Object toValue) { + + return subSet(null, false, toValue, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly less than toValue, optionally including toValue. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param toValue is the upper bound. + * + * @param toInclusive is true to include toValue. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet headSet(Object toValue, boolean toInclusive) { + + return subSet(null, false, toValue, toInclusive); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * greater than or equal to fromValue. + * This method conforms to the {@link SortedSet#tailSet} interface. + * + * @param fromValue is the lower bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet tailSet(Object fromValue) { + + return subSet(fromValue, true, null, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly greater than fromValue, optionally including fromValue. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param fromValue is the lower bound. + * + * @param fromInclusive is true to include fromValue. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet tailSet(Object fromValue, boolean fromInclusive) { + + return subSet(fromValue, fromInclusive, null, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements range + * from fromValue, inclusive, to toValue, exclusive. + * This method conforms to the {@link SortedSet#subSet} interface. + * + * @param fromValue is the lower bound. + * + * @param toValue is the upper bound. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet subSet(Object fromValue, Object toValue) { + + return subSet(fromValue, true, toValue, false); + } + + /** + * Returns a view of the portion of this sorted set whose elements are + * strictly greater than fromValue and strictly less than toValue, + * optionally including fromValue and toValue. + * This method does not exist in the standard {@link SortedSet} interface. + * + * @param fromValue is the lower bound. + * + * @param fromInclusive is true to include fromValue. + * + * @param toValue is the upper bound. + * + * @param toInclusive is true to include toValue. + * + * @return the subset. + * + * @throws RuntimeExceptionWrapper if a {@link + * com.sleepycat.db.DatabaseException} is thrown. + */ + public SortedSet subSet(Object fromValue, boolean fromInclusive, + Object toValue, boolean toInclusive) { + + try { + return new StoredSortedValueSet( + view.subView(fromValue, fromInclusive, toValue, toInclusive, + null)); + } catch (Exception e) { + throw StoredContainer.convertException(e); + } + } +} diff --git a/db/java/src/com/sleepycat/collections/StoredValueSet.java b/db/java/src/com/sleepycat/collections/StoredValueSet.java new file mode 100644 index 000000000..a4d30bc87 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/StoredValueSet.java @@ -0,0 +1,220 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: StoredValueSet.java,v 1.3 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.collections; + +import java.util.Set; + +import com.sleepycat.bind.EntityBinding; +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.db.Database; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.OperationStatus; + +/** + * The Set returned by Map.values() and Map.duplicates(), and which can also be + * constructed directly if a Map is not needed. + * Although this collection is a set it may contain duplicate values. Only if + * an entity value binding is used are all elements guaranteed to be unique. + * + * <p><em>Note that this class does not conform to the standard Java + * collections interface in the following ways:</em></p> + * <ul> + * <li>The {@link #size} method always throws + * <code>UnsupportedOperationException</code> because, for performance reasons, + * databases do not maintain their total record count.</li> + * <li>All iterators must be explicitly closed using {@link + * StoredIterator#close()} or {@link StoredIterator#close(java.util.Iterator)} + * to release the underlying database cursor resources.</li> + * </ul> + * + * @author Mark Hayes + */ +public class StoredValueSet extends StoredCollection implements Set { + + /* + * This class is also used internally for the set returned by duplicates(). + */ + + private boolean isSingleKey; + + /** + * Creates a value set view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param valueBinding is the binding used to translate between value + * buffers and value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public StoredValueSet(Database database, + EntryBinding valueBinding, + boolean writeAllowed) { + + super(new DataView(database, null, valueBinding, null, + writeAllowed, null)); + } + + /** + * Creates a value set entity view of a {@link Database}. + * + * @param database is the Database underlying the new collection. + * + * @param valueEntityBinding is the binding used to translate between + * key/value buffers and entity value objects. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + * + * @throws IllegalArgumentException if formats are not consistently + * defined or a parameter is invalid. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public StoredValueSet(Database database, + EntityBinding valueEntityBinding, + boolean writeAllowed) { + + super(new DataView(database, null, null, valueEntityBinding, + writeAllowed, null)); + } + + StoredValueSet(DataView valueSetView) { + + super(valueSetView); + } + + StoredValueSet(DataView valueSetView, boolean isSingleKey) { + + super(valueSetView); + this.isSingleKey = isSingleKey; + } + + /** + * Adds the specified entity to this set if it is not already present + * (optional operation). + * This method conforms to the {@link Set#add} interface. + * + * @param entity is the entity to be added. + * + * @return true if the entity was added, that is the key-value pair + * represented by the entity was not previously present in the collection. + * + * @throws UnsupportedOperationException if the collection is read-only, + * if the collection is indexed, or if an entity binding is not used. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean add(Object entity) { + + if (view.isSecondary()) { + throw new UnsupportedOperationException( + "add() not allowed with index"); + } else if (isSingleKey) { + /* entity is actually just a value in this case */ + if (!view.dupsAllowed) { + throw new UnsupportedOperationException("duplicates required"); + } + DataCursor cursor = null; + boolean doAutoCommit = beginAutoCommit(); + try { + cursor = new DataCursor(view, true); + cursor.useRangeKey(); + OperationStatus status = + cursor.putNoDupData(null, entity, null, true); + closeCursor(cursor); + commitAutoCommit(doAutoCommit); + return (status == OperationStatus.SUCCESS); + } catch (Exception e) { + closeCursor(cursor); + throw handleException(e, doAutoCommit); + } + } else if (view.entityBinding == null) { + throw new UnsupportedOperationException( + "add() requires entity binding"); + } else { + return add(null, entity); + } + } + + /** + * Returns true if this set contains the specified element. + * This method conforms to the {@link java.util.Set#contains} + * interface. + * + * @param value the value to check. + * + * @return whether the set contains the given value. + */ + public boolean contains(Object value) { + + return containsValue(value); + } + + /** + * Removes the specified value from this set if it is present (optional + * operation). + * If an entity binding is used, the key-value pair represented by the + * given entity is removed. If an entity binding is used, the first + * occurrence of a key-value pair with the given value is removed. + * This method conforms to the {@link Set#remove} interface. + * + * @throws UnsupportedOperationException if the collection is read-only. + * + * @throws RuntimeExceptionWrapper if a {@link DatabaseException} is + * thrown. + */ + public boolean remove(Object value) { + + return removeValue(value); + } + + // javadoc is inherited + public int size() { + + if (!isSingleKey) { + return super.size(); + } + DataCursor cursor = null; + try { + cursor = new DataCursor(view, false); + OperationStatus status = cursor.getFirst(false); + if (status == OperationStatus.SUCCESS) { + return cursor.count(); + } else { + return 0; + } + } catch (Exception e) { + throw StoredContainer.convertException(e); + } finally { + closeCursor(cursor); + } + } + + Object makeIteratorData(StoredIterator iterator, DataCursor cursor) + throws DatabaseException { + + return cursor.getCurrentValue(); + } + + boolean hasValues() { + + return true; + } +} diff --git a/db/java/src/com/sleepycat/collections/TransactionRunner.java b/db/java/src/com/sleepycat/collections/TransactionRunner.java new file mode 100644 index 000000000..270d265fb --- /dev/null +++ b/db/java/src/com/sleepycat/collections/TransactionRunner.java @@ -0,0 +1,221 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TransactionRunner.java,v 1.2 2004/09/22 18:01:03 bostic Exp $ + */ + +package com.sleepycat.collections; + +import com.sleepycat.compat.DbCompat; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.DeadlockException; +import com.sleepycat.db.Environment; +import com.sleepycat.db.Transaction; +import com.sleepycat.db.TransactionConfig; +import com.sleepycat.util.ExceptionUnwrapper; + +/** + * Starts a transaction, calls {@link TransactionWorker#doWork}, and handles + * transaction retry and exceptions. + * + * @author Mark Hayes + */ +public class TransactionRunner { + + /** The default maximum number of retries. */ + public static final int DEFAULT_MAX_RETRIES = 10; + + private Environment env; + private CurrentTransaction currentTxn; + private int maxRetries; + private TransactionConfig config; + private boolean allowNestedTxn; + + /** + * Creates a transaction runner for a given Berkeley DB environment. + * The default maximum number of retries ({@link #DEFAULT_MAX_RETRIES}) and + * a null (default) {@link TransactionConfig} will be used. + * + * @param env is the environment for running transactions. + */ + public TransactionRunner(Environment env) { + + this(env, DEFAULT_MAX_RETRIES, null); + } + + /** + * Creates a transaction runner for a given Berkeley DB environment and + * with a given number of maximum retries. + * + * @param env is the environment for running transactions. + * + * @param maxRetries is the maximum number of retries that will be + * performed when deadlocks are detected. + * + * @param config the transaction configuration used for calling + * {@link Environment#beginTransaction}, or null to use the default + * configuration. The configuration object is not cloned, and + * any modifications to it will impact subsequent transactions. + */ + public TransactionRunner(Environment env, int maxRetries, + TransactionConfig config) { + + this.env = env; + this.currentTxn = CurrentTransaction.getInstance(env); + this.maxRetries = maxRetries; + this.config = config; + } + + /** + * Returns the maximum number of retries that will be performed when + * deadlocks are detected. + */ + public int getMaxRetries() { + + return maxRetries; + } + + /** + * Changes the maximum number of retries that will be performed when + * deadlocks are detected. + * Calling this method does not impact transactions already running. + */ + public void setMaxRetries(int maxRetries) { + + this.maxRetries = maxRetries; + } + + /** + * Returns whether nested transactions will be created if + * <code>run()</code> is called when a transaction is already active for + * the current thread. + * By default this property is false. + */ + public boolean getAllowNestedTransactions() { + + return allowNestedTxn; + } + + /** + * Changes whether nested transactions will be created if + * <code>run()</code> is called when a transaction is already active for + * the current thread. + * Calling this method does not impact transactions already running. + */ + public void setAllowNestedTransactions(boolean allowNestedTxn) { + + if (allowNestedTxn && !DbCompat.NESTED_TRANSACTIONS) { + throw new UnsupportedOperationException( + "Nested transactions are not supported."); + } + this.allowNestedTxn = allowNestedTxn; + } + + /** + * Returns the transaction configuration used for calling + * {@link Environment#beginTransaction}. + * + * <p>If this property is null, the default configuration is used. The + * configuration object is not cloned, and any modifications to it will + * impact subsequent transactions.</p> + * + * @return the transaction configuration. + */ + public TransactionConfig getTransactionConfig() { + + return config; + } + + /** + * Changes the transaction configuration used for calling + * {@link Environment#beginTransaction}. + * + * <p>If this property is null, the default configuration is used. The + * configuration object is not cloned, and any modifications to it will + * impact subsequent transactions.</p> + * + * @param config the transaction configuration. + */ + public void setTransactionConfig(TransactionConfig config) { + + this.config = config; + } + + /** + * Calls the {@link TransactionWorker#doWork} method and, for transactional + * environments, begins and ends a transaction. If the environment given + * is non-transactional, a transaction will not be used but the doWork() + * method will still be called. + * + * <p> In a transactional environment, a new transaction is started before + * calling doWork(). This will start a nested transaction if one is + * already active. If DeadlockException is thrown by doWork(), the + * transaction will be aborted and the process will be repeated up to the + * maximum number of retries specified. If another exception is thrown by + * doWork() or the maximum number of retries has occurred, the transaction + * will be aborted and the exception will be rethrown by this method. If + * no exception is thrown by doWork(), the transaction will be committed. + * This method will not attempt to commit or abort a transaction if it has + * already been committed or aborted by doWork(). </p> + * + * @throws DeadlockException when it is thrown by doWork() and the + * maximum number of retries has occurred. The transaction will have been + * aborted by this method. + * + * @throws Exception when any other exception is thrown by doWork(). The + * exception will first be unwrapped by calling {@link + * ExceptionUnwrapper#unwrap}. The transaction will have been aborted by + * this method. + */ + public void run(TransactionWorker worker) + throws DatabaseException, Exception { + + if (currentTxn != null && + (allowNestedTxn || currentTxn.getTransaction() == null)) { + /* + * Transactional and (not nested or nested txns allowed). + */ + for (int i = 0;; i += 1) { + Transaction txn = null; + try { + txn = currentTxn.beginTransaction(config); + worker.doWork(); + if (txn != null && txn == currentTxn.getTransaction()) { + currentTxn.commitTransaction(); + } + return; + } catch (Exception e) { + e = ExceptionUnwrapper.unwrap(e); + if (txn != null && txn == currentTxn.getTransaction()) { + try { + currentTxn.abortTransaction(); + } catch (Exception e2) { + /* + * XXX We should really throw a 3rd exception that + * wraps both e and e2, to give the user a complete + * set of error information. + */ + e2.printStackTrace(); + throw e; + } + } + if (i >= maxRetries || !(e instanceof DeadlockException)) { + throw e; + } + } + } + } else { + /* + * Non-transactional or (nested and no nested txns allowed). + */ + try { + worker.doWork(); + } catch (Exception e) { + throw ExceptionUnwrapper.unwrap(e); + } + } + } +} diff --git a/db/java/src/com/sleepycat/collections/TransactionWorker.java b/db/java/src/com/sleepycat/collections/TransactionWorker.java new file mode 100644 index 000000000..eb69c7095 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/TransactionWorker.java @@ -0,0 +1,28 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TransactionWorker.java,v 1.1 2004/04/09 16:34:10 mark Exp $ + */ + +package com.sleepycat.collections; + +/** + * The interface implemented to perform the work within a transaction. + * To run a transaction, an instance of this interface is passed to the + * {@link TransactionRunner#run} method. + * + * @author Mark Hayes + */ +public interface TransactionWorker { + + /** + * Perform the work for a single transaction. + * + * @see TransactionRunner#run + */ + void doWork() + throws Exception; +} diff --git a/db/java/src/com/sleepycat/collections/TupleSerialFactory.java b/db/java/src/com/sleepycat/collections/TupleSerialFactory.java new file mode 100644 index 000000000..b8382a1a8 --- /dev/null +++ b/db/java/src/com/sleepycat/collections/TupleSerialFactory.java @@ -0,0 +1,135 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TupleSerialFactory.java,v 1.1 2004/04/09 16:34:10 mark Exp $ + */ + +package com.sleepycat.collections; + +import com.sleepycat.bind.EntryBinding; +import com.sleepycat.bind.serial.ClassCatalog; +import com.sleepycat.bind.serial.TupleSerialMarshalledBinding; +import com.sleepycat.bind.serial.TupleSerialMarshalledKeyCreator; +import com.sleepycat.bind.tuple.TupleBinding; +import com.sleepycat.bind.tuple.TupleMarshalledBinding; +import com.sleepycat.db.Database; + +/** + * Creates stored collections having tuple keys and serialized entity values. + * The entity classes must implement the java.io.Serializable and + * MarshalledTupleKeyEntity interfaces. The key classes must either implement + * the MarshalledTupleEntry interface or be one of the Java primitive type + * classes. Underlying binding objects are created automatically. + * + * @author Mark Hayes + */ +public class TupleSerialFactory { + + private ClassCatalog catalog; + + /** + * Creates a tuple-serial factory for given environment and class catalog. + */ + public TupleSerialFactory(ClassCatalog catalog) { + + this.catalog = catalog; + } + + /** + * Returns the class catalog associated with this factory. + */ + public final ClassCatalog getCatalog() { + + return catalog; + } + + /** + * Creates a map from a previously opened Database object. + * + * @param db the previously opened Database object. + * + * @param keyClass is the class used for map keys. It must implement the + * {@link com.sleepycat.bind.tuple.MarshalledTupleEntry} interface or be + * one of the Java primitive type classes. + * + * @param valueBaseClass the base class of the entity values for this + * store. It must implement the {@link + * com.sleepycat.bind.tuple.MarshalledTupleKeyEntity} interface. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + */ + public StoredMap newMap(Database db, Class keyClass, Class valueBaseClass, + boolean writeAllowed) { + + return new StoredMap(db, + getKeyBinding(keyClass), + getEntityBinding(valueBaseClass), + writeAllowed); + } + + /** + * Creates a sorted map from a previously opened Database object. + * + * @param db the previously opened Database object. + * + * @param keyClass is the class used for map keys. It must implement the + * {@link com.sleepycat.bind.tuple.MarshalledTupleEntry} interface or be + * one of the Java primitive type classes. + * + * @param valueBaseClass the base class of the entity values for this + * store. It must implement the {@link + * com.sleepycat.bind.tuple.MarshalledTupleKeyEntity} interface. + * + * @param writeAllowed is true to create a read-write collection or false + * to create a read-only collection. + */ + public StoredSortedMap newSortedMap(Database db, Class keyClass, + Class valueBaseClass, + boolean writeAllowed) { + + return new StoredSortedMap(db, + getKeyBinding(keyClass), + getEntityBinding(valueBaseClass), + writeAllowed); + } + + /** + * Creates a <code>SecondaryKeyCreator</code> object for use in configuring + * a <code>SecondaryDatabase</code>. The returned object implements + * the {@link com.sleepycat.db.SecondaryKeyCreator} interface. + * + * @param valueBaseClass the base class of the entity values for this + * store. It must implement the {@link + * com.sleepycat.bind.tuple.MarshalledTupleKeyEntity} interface. + * + * @param keyName is the key name passed to the {@link + * com.sleepycat.bind.tuple.MarshalledTupleKeyEntity#marshalSecondaryKey} + * method to identify the secondary key. + */ + public TupleSerialMarshalledKeyCreator getKeyCreator(Class valueBaseClass, + String keyName) { + + return new TupleSerialMarshalledKeyCreator( + getEntityBinding(valueBaseClass), + keyName); + } + + private TupleSerialMarshalledBinding getEntityBinding(Class baseClass) { + + return new TupleSerialMarshalledBinding(catalog, baseClass); + } + + private EntryBinding getKeyBinding(Class keyClass) { + + EntryBinding binding = TupleBinding.getPrimitiveBinding(keyClass); + if (binding == null) { + binding = new TupleMarshalledBinding(keyClass); + } + return binding; + } +} + diff --git a/db/java/src/com/sleepycat/collections/package.html b/db/java/src/com/sleepycat/collections/package.html new file mode 100644 index 000000000..865f36b0f --- /dev/null +++ b/db/java/src/com/sleepycat/collections/package.html @@ -0,0 +1,21 @@ +<!-- $Id: package.html,v 1.1 2004/08/02 18:52:06 mjc Exp $ --> +<html> +<head> +<!-- + + See the file LICENSE for redistribution information. + + Copyright (c) 2002-2004 + Sleepycat Software. All rights reserved. + + $Id: package.html,v 1.1 2004/08/02 18:52:06 mjc Exp $ + +--> +</head> +<body> +Data access based on the standard Java collections API<br> +<a href="{@docRoot}/%2e%2e/ref/bdb/cs_bdb_collection%2ehtml">[reference guide]</a>. +<p> +Examples can be found in je/examples/com/sleepycat/examples/collections. Build and run directions are in the installation notes. +</body> +</html> diff --git a/db/java/src/com/sleepycat/compat/DbCompat.java b/db/java/src/com/sleepycat/compat/DbCompat.java new file mode 100644 index 000000000..84e68bace --- /dev/null +++ b/db/java/src/com/sleepycat/compat/DbCompat.java @@ -0,0 +1,255 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: DbCompat.java,v 1.5 2004/09/22 18:01:03 bostic Exp $ + */ + +package com.sleepycat.compat; + +import java.io.FileNotFoundException; +import java.util.Comparator; + +import com.sleepycat.db.Cursor; +import com.sleepycat.db.CursorConfig; +import com.sleepycat.db.Database; +import com.sleepycat.db.DatabaseConfig; +import com.sleepycat.db.DatabaseEntry; +import com.sleepycat.db.DatabaseException; +import com.sleepycat.db.DatabaseType; +import com.sleepycat.db.Environment; +import com.sleepycat.db.EnvironmentConfig; +import com.sleepycat.db.LockDetectMode; +import com.sleepycat.db.LockMode; +import com.sleepycat.db.OperationStatus; +import com.sleepycat.db.SecondaryConfig; +import com.sleepycat.db.SecondaryCursor; +import com.sleepycat.db.SecondaryDatabase; +import com.sleepycat.db.Transaction; + +/** + * A minimal set of DB-JE compatibility methods for internal use only. + * Two versions are maintained in parallel in the DB and JE source trees. + * Used by the collections package. + */ +public class DbCompat { + + /* Capabilities */ + + public static final boolean CDB = true; + public static final boolean JOIN = true; + public static final boolean NESTED_TRANSACTIONS = true; + public static final boolean INSERTION_ORDERED_DUPLICATES = true; + public static final boolean SEPARATE_DATABASE_FILES = true; + public static final boolean MEMORY_SUBSYSTEM = true; + public static final boolean LOCK_SUBSYSTEM = true; + public static final boolean HASH_METHOD = true; + public static final boolean RECNO_METHOD = true; + public static final boolean QUEUE_METHOD = true; + public static final boolean BTREE_RECNUM_METHOD = true; + public static final boolean OPTIONAL_DIRTY_READ = true; + public static final boolean SECONDARIES = true; + + /* Methods used by the collections package. */ + + public static boolean getInitializeLocking(EnvironmentConfig config) { + return config.getInitializeLocking(); + } + + public static boolean getInitializeCDB(EnvironmentConfig config) { + return config.getInitializeCDB(); + } + + public static boolean isTypeBtree(DatabaseConfig dbConfig) { + return dbConfig.getType() == DatabaseType.BTREE; + } + + public static boolean isTypeHash(DatabaseConfig dbConfig) { + return dbConfig.getType() == DatabaseType.HASH; + } + + public static boolean isTypeQueue(DatabaseConfig dbConfig) { + return dbConfig.getType() == DatabaseType.QUEUE; + } + + public static boolean isTypeRecno(DatabaseConfig dbConfig) { + return dbConfig.getType() == DatabaseType.RECNO; + } + + public static boolean getBtreeRecordNumbers(DatabaseConfig dbConfig) { + return dbConfig.getBtreeRecordNumbers(); + } + + public static boolean getDirtyRead(DatabaseConfig dbConfig) { + return dbConfig.getDirtyRead(); + } + + public static boolean getRenumbering(DatabaseConfig dbConfig) { + return dbConfig.getRenumbering(); + } + + public static boolean getSortedDuplicates(DatabaseConfig dbConfig) { + return dbConfig.getSortedDuplicates(); + } + + public static boolean getUnsortedDuplicates(DatabaseConfig dbConfig) { + return dbConfig.getUnsortedDuplicates(); + } + + public static void setWriteCursor(CursorConfig config, boolean val) { + config.setWriteCursor(val); + } + + public static void setRecordNumber(DatabaseEntry entry, int recNum) { + entry.setRecordNumber(recNum); + } + + public static int getRecordNumber(DatabaseEntry entry) { + return entry.getRecordNumber(); + } + + public static String getDatabaseFile(Database db) + throws DatabaseException { + return db.getDatabaseFile(); + } + + public static OperationStatus getCurrentRecordNumber(Cursor cursor, + DatabaseEntry key, + LockMode lockMode) + throws DatabaseException { + return cursor.getRecordNumber(key, lockMode); + } + + public static OperationStatus getSearchRecordNumber(Cursor cursor, + DatabaseEntry key, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + return cursor.getSearchRecordNumber(key, data, lockMode); + } + + public static OperationStatus getSearchRecordNumber(SecondaryCursor cursor, + DatabaseEntry key, + DatabaseEntry pKey, + DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + return cursor.getSearchRecordNumber(key, pKey, data, lockMode); + } + + public static OperationStatus putAfter(Cursor cursor, DatabaseEntry data) + throws DatabaseException { + return cursor.putAfter(data); + } + + public static OperationStatus putBefore(Cursor cursor, DatabaseEntry data) + throws DatabaseException { + return cursor.putBefore(data); + } + + public static OperationStatus append(Database db, + Transaction txn, + DatabaseEntry key, + DatabaseEntry data) + throws DatabaseException { + return db.append(txn, key, data); + } + + /* Methods used by the collections tests. */ + + public static void setInitializeCache(EnvironmentConfig config, + boolean val) { + config.setInitializeCache(val); + } + + public static void setInitializeLocking(EnvironmentConfig config, + boolean val) { + config.setInitializeLocking(val); + } + + public static void setInitializeCDB(EnvironmentConfig config, + boolean val) { + config.setInitializeCDB(val); + } + + public static void setLockDetectModeOldest(EnvironmentConfig config) { + + config.setLockDetectMode(LockDetectMode.OLDEST); + } + + public static void setBtreeComparator(DatabaseConfig dbConfig, + Comparator comparator) { + dbConfig.setBtreeComparator(comparator); + } + + public static void setTypeBtree(DatabaseConfig dbConfig) { + dbConfig.setType(DatabaseType.BTREE); + } + + public static void setTypeHash(DatabaseConfig dbConfig) { + dbConfig.setType(DatabaseType.HASH); + } + + public static void setTypeRecno(DatabaseConfig dbConfig) { + dbConfig.setType(DatabaseType.RECNO); + } + + public static void setTypeQueue(DatabaseConfig dbConfig) { + dbConfig.setType(DatabaseType.QUEUE); + } + + public static void setBtreeRecordNumbers(DatabaseConfig dbConfig, + boolean val) { + dbConfig.setBtreeRecordNumbers(val); + } + + public static void setDirtyRead(DatabaseConfig dbConfig, + boolean val) { + dbConfig.setDirtyRead(val); + } + + public static void setRenumbering(DatabaseConfig dbConfig, + boolean val) { + dbConfig.setRenumbering(val); + } + + public static void setSortedDuplicates(DatabaseConfig dbConfig, + boolean val) { + dbConfig.setSortedDuplicates(val); + } + + public static void setUnsortedDuplicates(DatabaseConfig dbConfig, + boolean val) { + dbConfig.setUnsortedDuplicates(val); + } + + public static void setRecordLength(DatabaseConfig dbConfig, int val) { + dbConfig.setRecordLength(val); + } + + public static void setRecordPad(DatabaseConfig dbConfig, int val) { + dbConfig.setRecordPad(val); + } + + public static Database openDatabase(Environment env, + Transaction txn, + String file, + String name, + DatabaseConfig config) + throws DatabaseException, FileNotFoundException { + return env.openDatabase(txn, file, name, config); + } + + public static SecondaryDatabase + openSecondaryDatabase(Environment env, + Transaction txn, + String file, + String name, + Database primary, + SecondaryConfig config) + throws DatabaseException, FileNotFoundException { + return env.openSecondaryDatabase(txn, file, name, primary, config); + } +} diff --git a/db/java/src/com/sleepycat/db/BtreePrefixCalculator.java b/db/java/src/com/sleepycat/db/BtreePrefixCalculator.java new file mode 100644 index 000000000..ca86f0a68 --- /dev/null +++ b/db/java/src/com/sleepycat/db/BtreePrefixCalculator.java @@ -0,0 +1,14 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: BtreePrefixCalculator.java,v 1.1 2004/04/06 20:43:36 mjc Exp $ + */ + +package com.sleepycat.db; + +public interface BtreePrefixCalculator { + int prefix(Database db, DatabaseEntry dbt1, DatabaseEntry dbt2); +} diff --git a/db/java/src/com/sleepycat/db/BtreeStats.java b/db/java/src/com/sleepycat/db/BtreeStats.java new file mode 100644 index 000000000..50c8f9d7a --- /dev/null +++ b/db/java/src/com/sleepycat/db/BtreeStats.java @@ -0,0 +1,146 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class BtreeStats extends DatabaseStats { + // no public constructor + protected BtreeStats() {} + + private int bt_magic; + public int getMagic() { + return bt_magic; + } + + private int bt_version; + public int getVersion() { + return bt_version; + } + + private int bt_metaflags; + public int getMetaFlags() { + return bt_metaflags; + } + + private int bt_nkeys; + public int getNumKeys() { + return bt_nkeys; + } + + private int bt_ndata; + public int getNumData() { + return bt_ndata; + } + + private int bt_pagesize; + public int getPageSize() { + return bt_pagesize; + } + + private int bt_maxkey; + public int getMaxKey() { + return bt_maxkey; + } + + private int bt_minkey; + public int getMinKey() { + return bt_minkey; + } + + private int bt_re_len; + public int getReLen() { + return bt_re_len; + } + + private int bt_re_pad; + public int getRePad() { + return bt_re_pad; + } + + private int bt_levels; + public int getLevels() { + return bt_levels; + } + + private int bt_int_pg; + public int getIntPages() { + return bt_int_pg; + } + + private int bt_leaf_pg; + public int getLeafPages() { + return bt_leaf_pg; + } + + private int bt_dup_pg; + public int getDupPages() { + return bt_dup_pg; + } + + private int bt_over_pg; + public int getOverPages() { + return bt_over_pg; + } + + private int bt_empty_pg; + public int getEmptyPages() { + return bt_empty_pg; + } + + private int bt_free; + public int getFree() { + return bt_free; + } + + private int bt_int_pgfree; + public int getIntPagesFree() { + return bt_int_pgfree; + } + + private int bt_leaf_pgfree; + public int getLeafPagesFree() { + return bt_leaf_pgfree; + } + + private int bt_dup_pgfree; + public int getDupPagesFree() { + return bt_dup_pgfree; + } + + private int bt_over_pgfree; + public int getOverPagesFree() { + return bt_over_pgfree; + } + + public String toString() { + return "BtreeStats:" + + "\n bt_magic=" + bt_magic + + "\n bt_version=" + bt_version + + "\n bt_metaflags=" + bt_metaflags + + "\n bt_nkeys=" + bt_nkeys + + "\n bt_ndata=" + bt_ndata + + "\n bt_pagesize=" + bt_pagesize + + "\n bt_maxkey=" + bt_maxkey + + "\n bt_minkey=" + bt_minkey + + "\n bt_re_len=" + bt_re_len + + "\n bt_re_pad=" + bt_re_pad + + "\n bt_levels=" + bt_levels + + "\n bt_int_pg=" + bt_int_pg + + "\n bt_leaf_pg=" + bt_leaf_pg + + "\n bt_dup_pg=" + bt_dup_pg + + "\n bt_over_pg=" + bt_over_pg + + "\n bt_empty_pg=" + bt_empty_pg + + "\n bt_free=" + bt_free + + "\n bt_int_pgfree=" + bt_int_pgfree + + "\n bt_leaf_pgfree=" + bt_leaf_pgfree + + "\n bt_dup_pgfree=" + bt_dup_pgfree + + "\n bt_over_pgfree=" + bt_over_pgfree + ; + } +} diff --git a/db/java/src/com/sleepycat/db/CacheFile.java b/db/java/src/com/sleepycat/db/CacheFile.java new file mode 100644 index 000000000..2fa44a19c --- /dev/null +++ b/db/java/src/com/sleepycat/db/CacheFile.java @@ -0,0 +1,70 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: CacheFile.java,v 1.3 2004/09/23 17:56:39 mjc Exp $ + */ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbMpoolFile; + +public class CacheFile { + private DbMpoolFile mpf; + + /* package */ + CacheFile(final DbMpoolFile mpf) { + this.mpf = mpf; + } + + public CacheFilePriority getPriority() + throws DatabaseException { + + return CacheFilePriority.fromFlag(mpf.get_priority()); + } + + public void setPriority(final CacheFilePriority priority) + throws DatabaseException { + + mpf.set_priority(priority.getFlag()); + } + + public long getMaximumSize() + throws DatabaseException { + + return mpf.get_maxsize(); + } + + public void setMaximumSize(final long bytes) + throws DatabaseException { + + mpf.set_maxsize(bytes); + } + + public boolean getNoFile() + throws DatabaseException { + + return (mpf.get_flags() & DbConstants.DB_MPOOL_NOFILE) != 0; + } + + public void setNoFile(final boolean onoff) + throws DatabaseException { + + mpf.set_flags(DbConstants.DB_MPOOL_NOFILE, onoff); + } + + public boolean getUnlink() + throws DatabaseException { + + return (mpf.get_flags() & DbConstants.DB_MPOOL_UNLINK) != 0; + } + + public void setUnlink(boolean onoff) + throws DatabaseException { + + mpf.set_flags(DbConstants.DB_MPOOL_UNLINK, onoff); + } +} diff --git a/db/java/src/com/sleepycat/db/CacheFilePriority.java b/db/java/src/com/sleepycat/db/CacheFilePriority.java new file mode 100644 index 000000000..89f08c8f2 --- /dev/null +++ b/db/java/src/com/sleepycat/db/CacheFilePriority.java @@ -0,0 +1,61 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: CacheFilePriority.java,v 1.2 2004/04/21 01:09:09 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public final class CacheFilePriority { + public static final CacheFilePriority DEFAULT = + new CacheFilePriority("DEFAULT", DbConstants.DB_PRIORITY_DEFAULT); + public static final CacheFilePriority HIGH = + new CacheFilePriority("HIGH", DbConstants.DB_PRIORITY_HIGH); + public static final CacheFilePriority LOW = + new CacheFilePriority("LOW", DbConstants.DB_PRIORITY_LOW); + public static final CacheFilePriority VERY_HIGH = + new CacheFilePriority("VERY_HIGH", DbConstants.DB_PRIORITY_VERY_HIGH); + public static final CacheFilePriority VERY_LOW = + new CacheFilePriority("VERY_LOW", DbConstants.DB_PRIORITY_VERY_LOW); + + /* package */ + static CacheFilePriority fromFlag(int flag) { + switch (flag) { + case DbConstants.DB_PRIORITY_DEFAULT: + return DEFAULT; + case DbConstants.DB_PRIORITY_HIGH: + return HIGH; + case DbConstants.DB_PRIORITY_LOW: + return LOW; + case DbConstants.DB_PRIORITY_VERY_HIGH: + return VERY_HIGH; + case DbConstants.DB_PRIORITY_VERY_LOW: + return VERY_LOW; + default: + throw new IllegalArgumentException( + "Unknown cache priority: " + flag); + } + } + + private final String priorityName; + private final int flag; + + private CacheFilePriority(final String priorityName, final int flag) { + this.priorityName = priorityName; + this.flag = flag; + } + + public String toString() { + return "CacheFilePriority." + priorityName; + } + + /* package */ + int getFlag() { + return flag; + } +} diff --git a/db/java/src/com/sleepycat/db/CacheFileStats.java b/db/java/src/com/sleepycat/db/CacheFileStats.java new file mode 100644 index 000000000..7b864ed4e --- /dev/null +++ b/db/java/src/com/sleepycat/db/CacheFileStats.java @@ -0,0 +1,68 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class CacheFileStats { + // no public constructor + protected CacheFileStats() {} + + private String file_name; + public String getFileName() { + return file_name; + } + + private int st_pagesize; + public int getPageSize() { + return st_pagesize; + } + + private int st_map; + public int getMap() { + return st_map; + } + + private int st_cache_hit; + public int getCacheHit() { + return st_cache_hit; + } + + private int st_cache_miss; + public int getCacheMiss() { + return st_cache_miss; + } + + private int st_page_create; + public int getPageCreate() { + return st_page_create; + } + + private int st_page_in; + public int getPageIn() { + return st_page_in; + } + + private int st_page_out; + public int getPageOut() { + return st_page_out; + } + + public String toString() { + return "CacheFileStats:" + + "\n file_name=" + file_name + + "\n st_pagesize=" + st_pagesize + + "\n st_map=" + st_map + + "\n st_cache_hit=" + st_cache_hit + + "\n st_cache_miss=" + st_cache_miss + + "\n st_page_create=" + st_page_create + + "\n st_page_in=" + st_page_in + + "\n st_page_out=" + st_page_out + ; + } +} diff --git a/db/java/src/com/sleepycat/db/CacheStats.java b/db/java/src/com/sleepycat/db/CacheStats.java new file mode 100644 index 000000000..b9e16c96a --- /dev/null +++ b/db/java/src/com/sleepycat/db/CacheStats.java @@ -0,0 +1,224 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class CacheStats { + // no public constructor + protected CacheStats() {} + + private int st_gbytes; + public int getGbytes() { + return st_gbytes; + } + + private int st_bytes; + public int getBytes() { + return st_bytes; + } + + private int st_ncache; + public int getNumCache() { + return st_ncache; + } + + private int st_regsize; + public int getRegSize() { + return st_regsize; + } + + private int st_mmapsize; + public int getMmapSize() { + return st_mmapsize; + } + + private int st_maxopenfd; + public int getMaxOpenfd() { + return st_maxopenfd; + } + + private int st_maxwrite; + public int getMaxWrite() { + return st_maxwrite; + } + + private int st_maxwrite_sleep; + public int getMaxWriteSleep() { + return st_maxwrite_sleep; + } + + private int st_map; + public int getMap() { + return st_map; + } + + private int st_cache_hit; + public int getCacheHit() { + return st_cache_hit; + } + + private int st_cache_miss; + public int getCacheMiss() { + return st_cache_miss; + } + + private int st_page_create; + public int getPageCreate() { + return st_page_create; + } + + private int st_page_in; + public int getPageIn() { + return st_page_in; + } + + private int st_page_out; + public int getPageOut() { + return st_page_out; + } + + private int st_ro_evict; + public int getRoEvict() { + return st_ro_evict; + } + + private int st_rw_evict; + public int getRwEvict() { + return st_rw_evict; + } + + private int st_page_trickle; + public int getPageTrickle() { + return st_page_trickle; + } + + private int st_pages; + public int getPages() { + return st_pages; + } + + private int st_page_clean; + public int getPageClean() { + return st_page_clean; + } + + private int st_page_dirty; + public int getPageDirty() { + return st_page_dirty; + } + + private int st_hash_buckets; + public int getHashBuckets() { + return st_hash_buckets; + } + + private int st_hash_searches; + public int getHashSearches() { + return st_hash_searches; + } + + private int st_hash_longest; + public int getHashLongest() { + return st_hash_longest; + } + + private int st_hash_examined; + public int getHashExamined() { + return st_hash_examined; + } + + private int st_hash_nowait; + public int getHashNowait() { + return st_hash_nowait; + } + + private int st_hash_wait; + public int getHashWait() { + return st_hash_wait; + } + + private int st_hash_max_wait; + public int getHashMaxWait() { + return st_hash_max_wait; + } + + private int st_region_nowait; + public int getRegionNowait() { + return st_region_nowait; + } + + private int st_region_wait; + public int getRegionWait() { + return st_region_wait; + } + + private int st_alloc; + public int getAlloc() { + return st_alloc; + } + + private int st_alloc_buckets; + public int getAllocBuckets() { + return st_alloc_buckets; + } + + private int st_alloc_max_buckets; + public int getAllocMaxBuckets() { + return st_alloc_max_buckets; + } + + private int st_alloc_pages; + public int getAllocPages() { + return st_alloc_pages; + } + + private int st_alloc_max_pages; + public int getAllocMaxPages() { + return st_alloc_max_pages; + } + + public String toString() { + return "CacheStats:" + + "\n st_gbytes=" + st_gbytes + + "\n st_bytes=" + st_bytes + + "\n st_ncache=" + st_ncache + + "\n st_regsize=" + st_regsize + + "\n st_mmapsize=" + st_mmapsize + + "\n st_maxopenfd=" + st_maxopenfd + + "\n st_maxwrite=" + st_maxwrite + + "\n st_maxwrite_sleep=" + st_maxwrite_sleep + + "\n st_map=" + st_map + + "\n st_cache_hit=" + st_cache_hit + + "\n st_cache_miss=" + st_cache_miss + + "\n st_page_create=" + st_page_create + + "\n st_page_in=" + st_page_in + + "\n st_page_out=" + st_page_out + + "\n st_ro_evict=" + st_ro_evict + + "\n st_rw_evict=" + st_rw_evict + + "\n st_page_trickle=" + st_page_trickle + + "\n st_pages=" + st_pages + + "\n st_page_clean=" + st_page_clean + + "\n st_page_dirty=" + st_page_dirty + + "\n st_hash_buckets=" + st_hash_buckets + + "\n st_hash_searches=" + st_hash_searches + + "\n st_hash_longest=" + st_hash_longest + + "\n st_hash_examined=" + st_hash_examined + + "\n st_hash_nowait=" + st_hash_nowait + + "\n st_hash_wait=" + st_hash_wait + + "\n st_hash_max_wait=" + st_hash_max_wait + + "\n st_region_nowait=" + st_region_nowait + + "\n st_region_wait=" + st_region_wait + + "\n st_alloc=" + st_alloc + + "\n st_alloc_buckets=" + st_alloc_buckets + + "\n st_alloc_max_buckets=" + st_alloc_max_buckets + + "\n st_alloc_pages=" + st_alloc_pages + + "\n st_alloc_max_pages=" + st_alloc_max_pages + ; + } +} diff --git a/db/java/src/com/sleepycat/db/CheckpointConfig.java b/db/java/src/com/sleepycat/db/CheckpointConfig.java new file mode 100644 index 000000000..ab9d6bc83 --- /dev/null +++ b/db/java/src/com/sleepycat/db/CheckpointConfig.java @@ -0,0 +1,60 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: CheckpointConfig.java,v 1.3 2004/04/21 01:09:09 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbEnv; +import com.sleepycat.db.internal.DbConstants; + +public class CheckpointConfig { + public static final CheckpointConfig DEFAULT = new CheckpointConfig(); + + private boolean force = false; + private int kBytes = 0; + private int minutes = 0; + + public CheckpointConfig() { + } + + /* package */ + static CheckpointConfig checkNull(CheckpointConfig config) { + return (config == null) ? DEFAULT : config; + } + + public void setKBytes(final int kBytes) { + this.kBytes = kBytes; + } + + public int getKBytes() { + return kBytes; + } + + public void setMinutes(final int minutes) { + this.minutes = minutes; + } + + public int getMinutes() { + return minutes; + } + + public void setForce(final boolean force) { + this.force = force; + } + + public boolean getForce() { + return force; + } + + /* package */ + void runCheckpoint(final DbEnv dbenv) + throws DatabaseException { + + dbenv.txn_checkpoint(kBytes, minutes, force ? DbConstants.DB_FORCE : 0); + } +} diff --git a/db/java/src/com/sleepycat/db/Cursor.java b/db/java/src/com/sleepycat/db/Cursor.java new file mode 100644 index 000000000..6b1e8e7f8 --- /dev/null +++ b/db/java/src/com/sleepycat/db/Cursor.java @@ -0,0 +1,349 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: Cursor.java,v 1.5 2004/06/02 21:28:43 mark Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.Dbc; + +public class Cursor { + /* package */ Dbc dbc; + protected Database database; + protected CursorConfig config; + + protected Cursor() { + } + + Cursor(final Database database, final Dbc dbc, final CursorConfig config) + throws DatabaseException { + + this.dbc = dbc; + this.database = database; + this.config = config; + } + + public synchronized void close() + throws DatabaseException { + + if (dbc != null) { + try { + dbc.close(); + } finally { + dbc = null; + } + } + } + + public Cursor dup(final boolean samePosition) + throws DatabaseException { + + return new Cursor(database, + dbc.dup(samePosition ? DbConstants.DB_POSITION : 0), config); + } + + public CursorConfig getConfig() { + return config; + } + + public Database getDatabase() { + return database; + } + + public int count() + throws DatabaseException { + + return dbc.count(0); + } + + public OperationStatus delete() + throws DatabaseException { + + return OperationStatus.fromInt(dbc.del(0)); + } + + public OperationStatus getCurrent(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_CURRENT | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getFirst(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_FIRST | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getLast(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_LAST | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getNext(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_NEXT | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getNextDup(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_NEXT_DUP | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getNextNoDup(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_NEXT_NODUP | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getPrev(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_PREV | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getPrevDup(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + /* + * "Get the previous duplicate" isn't directly supported by the C API, + * so here's how to get it: dup the cursor and call getPrev, then dup + * the result and call getNextDup. If both succeed then there was a + * previous duplicate and the first dup is sitting on it. Keep that, + * and call getCurrent to fill in the user's buffers. + */ + Dbc dup1 = dbc.dup(DbConstants.DB_POSITION); + try { + int errCode = dup1.get(DatabaseEntry.IGNORE, DatabaseEntry.IGNORE, + DbConstants.DB_PREV | LockMode.getFlag(lockMode)); + if (errCode == 0) { + Dbc dup2 = dup1.dup(DbConstants.DB_POSITION); + try { + errCode = dup2.get(DatabaseEntry.IGNORE, + DatabaseEntry.IGNORE, + DbConstants.DB_NEXT_DUP | LockMode.getFlag(lockMode)); + } finally { + dup2.close(); + } + } + if (errCode == 0) + errCode = dup1.get(key, data, + DbConstants.DB_CURRENT | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag())); + if (errCode == 0) { + Dbc tdbc = dbc; + dbc = dup1; + dup1 = tdbc; + } + return OperationStatus.fromInt(errCode); + } finally { + dup1.close(); + } + } + + public OperationStatus getPrevNoDup(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_PREV_NODUP | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getRecordNumber(final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(DatabaseEntry.IGNORE, data, + DbConstants.DB_GET_RECNO | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchKey(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_SET | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchKeyRange(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_SET_RANGE | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchBoth(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_GET_BOTH | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchBothRange(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, + DbConstants.DB_GET_BOTH_RANGE | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchRecordNumber(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, DbConstants.DB_SET_RECNO | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus put(final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.put(key, data, DbConstants.DB_KEYLAST)); + } + + public OperationStatus putAfter(final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.put(DatabaseEntry.UNUSED, data, DbConstants.DB_AFTER)); + } + + public OperationStatus putBefore(final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.put(DatabaseEntry.UNUSED, data, DbConstants.DB_BEFORE)); + } + + public OperationStatus putNoOverwrite(final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + /* + * The tricks here are making sure the cursor doesn't move on error and + * noticing that if the key exists, that's an error and we don't want + * to return the data. + */ + Dbc tempDbc = dbc.dup(0); + try { + int errCode = tempDbc.get(key, DatabaseEntry.IGNORE, + DbConstants.DB_SET | database.rmwFlag); + if (errCode == 0) + return OperationStatus.KEYEXIST; + else if (errCode != DbConstants.DB_NOTFOUND && + errCode != DbConstants.DB_KEYEMPTY) + return OperationStatus.fromInt(errCode); + else { + Dbc tdbc = dbc; + dbc = tempDbc; + tempDbc = tdbc; + + return OperationStatus.fromInt( + dbc.put(key, data, DbConstants.DB_KEYLAST)); + } + } finally { + tempDbc.close(); + } + } + + public OperationStatus putKeyFirst(final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.put(key, data, DbConstants.DB_KEYFIRST)); + } + + public OperationStatus putKeyLast(final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.put(key, data, DbConstants.DB_KEYLAST)); + } + + public OperationStatus putNoDupData(final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.put(key, data, DbConstants.DB_NODUPDATA)); + } + + public OperationStatus putCurrent(final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.put(DatabaseEntry.UNUSED, data, DbConstants.DB_CURRENT)); + } +} diff --git a/db/java/src/com/sleepycat/db/CursorConfig.java b/db/java/src/com/sleepycat/db/CursorConfig.java new file mode 100644 index 000000000..f674de3c8 --- /dev/null +++ b/db/java/src/com/sleepycat/db/CursorConfig.java @@ -0,0 +1,76 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: CursorConfig.java,v 1.4 2004/09/28 19:30:36 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.Db; +import com.sleepycat.db.internal.Dbc; +import com.sleepycat.db.internal.DbTxn; + +public class CursorConfig implements Cloneable { + public static final CursorConfig DEFAULT = new CursorConfig(); + + public static final CursorConfig DIRTY_READ = new CursorConfig(); + static { DIRTY_READ.setDirtyRead(true); } + + public static final CursorConfig DEGREE_2 = new CursorConfig(); + static { DEGREE_2.setDegree2(true); } + + public static final CursorConfig WRITECURSOR = new CursorConfig(); + static { WRITECURSOR.setWriteCursor(true); } + + + private boolean dirtyRead = false; + private boolean degree2 = false; + private boolean writeCursor = false; + + public CursorConfig() { + } + + /* package */ + static CursorConfig checkNull(CursorConfig config) { + return (config == null) ? DEFAULT : config; + } + + public void setDegree2(final boolean degree2) { + this.degree2 = degree2; + } + + public boolean getDegree2() { + return degree2; + } + + public void setDirtyRead(final boolean dirtyRead) { + this.dirtyRead = dirtyRead; + } + + public boolean getDirtyRead() { + return dirtyRead; + } + + public void setWriteCursor(final boolean writeCursor) { + this.writeCursor = writeCursor; + } + + public boolean getWriteCursor() { + return writeCursor; + } + + /* package */ + Dbc openCursor(final Db db, final DbTxn txn) + throws DatabaseException { + + int flags = 0; + flags |= dirtyRead ? DbConstants.DB_DIRTY_READ : 0; + flags |= degree2 ? DbConstants.DB_DEGREE_2 : 0; + flags |= writeCursor ? DbConstants.DB_WRITECURSOR : 0; + return db.cursor(txn, flags); + } +} diff --git a/db/java/src/com/sleepycat/db/Database.java b/db/java/src/com/sleepycat/db/Database.java new file mode 100644 index 000000000..186e71e21 --- /dev/null +++ b/db/java/src/com/sleepycat/db/Database.java @@ -0,0 +1,314 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: Database.java,v 1.12 2004/09/28 19:30:37 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.Db; +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbSequence; +import com.sleepycat.db.internal.Dbc; + +public class Database { + Db db; + private int autoCommitFlag; + int rmwFlag; + + /* package */ + Database(final Db db) + throws DatabaseException { + + this.db = db; + db.wrapper = this; + this.autoCommitFlag = + db.get_transactional() ? DbConstants.DB_AUTO_COMMIT : 0; + rmwFlag = ((db.get_env().get_open_flags() & + DbConstants.DB_INIT_LOCK) != 0) ? DbConstants.DB_RMW : 0; + } + + public Database(final String filename, + final String databaseName, + final DatabaseConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + this(DatabaseConfig.checkNull(config).openDatabase(null, null, + filename, databaseName)); + // Set up dbenv.wrapper + new Environment(db.get_env()); + } + + public void close(final boolean noSync) + throws DatabaseException { + + db.close(noSync ? DbConstants.DB_NOSYNC : 0); + } + + public void close() + throws DatabaseException { + + close(false); + } + + public Cursor openCursor(final Transaction txn, CursorConfig config) + throws DatabaseException { + + return new Cursor(this, CursorConfig.checkNull(config).openCursor( + db, (txn == null) ? null : txn.txn), config); + } + + public Sequence openSequence(final Transaction txn, + final DatabaseEntry key, + final SequenceConfig config) + throws DatabaseException { + + return new Sequence(SequenceConfig.checkNull(config).openSequence( + db, (txn == null) ? null : txn.txn, key), config); + } + + public void removeSequence(final Transaction txn, + final DatabaseEntry key, + SequenceConfig config) + throws DatabaseException { + + config = SequenceConfig.checkNull(config); + final DbSequence seq = config.openSequence( + db, (txn == null) ? null : txn.txn, key); + seq.remove((txn == null) ? null : txn.txn, + (txn == null && db.get_transactional()) ? + DbConstants.DB_AUTO_COMMIT | (config.getAutoCommitNoSync() ? + DbConstants.DB_TXN_NOSYNC : 0) : 0); + } + + public String getDatabaseFile() + throws DatabaseException { + + return db.get_filename(); + } + + public String getDatabaseName() + throws DatabaseException { + + return db.get_dbname(); + } + + public DatabaseConfig getConfig() + throws DatabaseException { + + return new DatabaseConfig(db); + } + + public void setConfig(DatabaseConfig config) + throws DatabaseException { + + config.configureDatabase(db, getConfig()); + } + + public Environment getEnvironment() + throws DatabaseException { + + return db.get_env().wrapper; + } + + public CacheFile getCacheFile() + throws DatabaseException { + + return new CacheFile(db.get_mpf()); + } + + public OperationStatus append(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + db.put((txn == null) ? null : txn.txn, key, data, + DbConstants.DB_APPEND | ((txn == null) ? autoCommitFlag : 0))); + } + + public OperationStatus consume(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry data, + final boolean wait) + throws DatabaseException { + + return OperationStatus.fromInt( + db.get((txn == null) ? null : txn.txn, + key, data, + (wait ? DbConstants.DB_CONSUME_WAIT : DbConstants.DB_CONSUME) | + ((txn == null) ? autoCommitFlag : 0))); + } + + public OperationStatus delete(final Transaction txn, + final DatabaseEntry key) + throws DatabaseException { + + return OperationStatus.fromInt( + db.del((txn == null) ? null : txn.txn, key, + ((txn == null) ? autoCommitFlag : 0))); + } + + public OperationStatus get(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry data, + final LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + db.get((txn == null) ? null : txn.txn, + key, data, + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public KeyRange getKeyRange(final Transaction txn, + final DatabaseEntry key) + throws DatabaseException { + + final KeyRange range = new KeyRange(); + db.key_range((txn == null) ? null : txn.txn, key, range, 0); + return range; + } + + public OperationStatus getSearchBoth(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry data, + final LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + db.get((txn == null) ? null : txn.txn, + key, data, + DbConstants.DB_GET_BOTH | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchRecordNumber(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry data, + final LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + db.get((txn == null) ? null : txn.txn, + key, data, + DbConstants.DB_SET_RECNO | + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus put(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + db.put((txn == null) ? null : txn.txn, + key, data, + ((txn == null) ? autoCommitFlag : 0))); + } + + public OperationStatus putNoDupData(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + db.put((txn == null) ? null : txn.txn, + key, data, + DbConstants.DB_NODUPDATA | + ((txn == null) ? autoCommitFlag : 0))); + } + + public OperationStatus putNoOverwrite(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + db.put((txn == null) ? null : txn.txn, + key, data, + DbConstants.DB_NOOVERWRITE | + ((txn == null) ? autoCommitFlag : 0))); + } + + public JoinCursor join(final Cursor[] cursList, JoinConfig config) + throws DatabaseException { + + config = JoinConfig.checkNull(config); + + final Dbc[] dbcList = new Dbc[cursList.length]; + for (int i = 0; i < cursList.length; i++) + dbcList[i] = (cursList[i] == null) ? null : cursList[i].dbc; + + return new JoinCursor(this, + db.join(dbcList, config.getFlags()), config); + } + + public int truncate(final Transaction txn, boolean countRecords) + throws DatabaseException { + + // XXX: implement countRecords in C + int count = db.truncate((txn == null) ? null : txn.txn, + ((txn == null) ? autoCommitFlag : 0)); + + return countRecords ? count : -1; + } + + public DatabaseStats getStats(final Transaction txn, StatsConfig config) + throws DatabaseException { + + return (DatabaseStats)db.stat((txn == null) ? null : txn.txn, + StatsConfig.checkNull(config).getFlags()); + } + + public static void remove(final String fileName, + final String databaseName, + DatabaseConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + final Db db = DatabaseConfig.checkNull(config).createDatabase(null); + db.remove(fileName, databaseName, 0); + } + + public static void rename(final String fileName, + final String oldDatabaseName, + final String newDatabaseName, + DatabaseConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + final Db db = DatabaseConfig.checkNull(config).createDatabase(null); + db.rename(fileName, oldDatabaseName, newDatabaseName, 0); + } + + public void sync() + throws DatabaseException { + + db.sync(0); + } + + public static void upgrade(final String fileName, + DatabaseConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + final Db db = DatabaseConfig.checkNull(config).createDatabase(null); + db.upgrade(fileName, + config.getSortedDuplicates() ? DbConstants.DB_DUPSORT : 0); + db.close(0); + } + + public boolean verify(final String fileName, + final String databaseName, + final java.io.PrintStream dumpStream, + VerifyConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + return db.verify(fileName, databaseName, dumpStream, + VerifyConfig.checkNull(config).getFlags()); + } +} diff --git a/db/java/src/com/sleepycat/db/DatabaseConfig.java b/db/java/src/com/sleepycat/db/DatabaseConfig.java new file mode 100644 index 000000000..2a66f8410 --- /dev/null +++ b/db/java/src/com/sleepycat/db/DatabaseConfig.java @@ -0,0 +1,628 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: DatabaseConfig.java,v 1.8 2004/07/30 14:52:21 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.Db; +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbEnv; +import com.sleepycat.db.internal.DbTxn; +import com.sleepycat.db.internal.DbUtil; + +public class DatabaseConfig implements Cloneable { + /* + * For internal use, final to allow null as a valid value for + * the config parameter. + */ + public static final DatabaseConfig DEFAULT = new DatabaseConfig(); + + /* package */ + static DatabaseConfig checkNull(DatabaseConfig config) { + return (config == null) ? DEFAULT : config; + } + + /* Parameters */ + private DatabaseType type = DatabaseType.UNKNOWN; + private int mode = 0644; + private int btMinKey = 0; + private int byteOrder = 0; + private long cacheSize = 0L; + private int cacheCount = 0; + private java.io.OutputStream errorStream = null; + private String errorPrefix = null; + private int hashFillFactor = 0; + private int hashNumElements = 0; + private java.io.OutputStream messageStream = null; + private int pageSize = 0; + private String password = null; + private int queueExtentSize = 0; + private int recordDelimiter = 0; + private int recordLength = 0; + private int recordPad = -1; // Zero is a valid, non-default value. + private java.io.File recordSource = null; + + /* Flags */ + private boolean allowCreate = false; + private boolean btreeRecordNumbers = false; + private boolean checksum = false; + private boolean dirtyRead = false; + private boolean encrypted = false; + private boolean exclusiveCreate = false; + private boolean noMMap = false; + private boolean queueInOrder = false; + private boolean readOnly = false; + private boolean renumbering = false; + private boolean reverseSplitOff = false; + private boolean sortedDuplicates = false; + private boolean snapshot = false; + private boolean unsortedDuplicates = false; + private boolean transactional = false; + private boolean transactionNotDurable = false; + private boolean truncate = false; + private boolean xaCreate = false; + + private java.util.Comparator btreeComparator = null; + private BtreePrefixCalculator btreePrefixCalculator = null; + private java.util.Comparator duplicateComparator = null; + private FeedbackHandler feedbackHandler = null; + private ErrorHandler errorHandler = null; + private MessageHandler messageHandler = null; + private Hasher hasher = null; + private RecordNumberAppender recnoAppender = null; + private PanicHandler panicHandler = null; + + public DatabaseConfig() { + } + + public void setAllowCreate(final boolean allowCreate) { + this.allowCreate = allowCreate; + } + + public boolean getAllowCreate() { + return allowCreate; + } + + public void setBtreeComparator(final java.util.Comparator btreeComparator) { + this.btreeComparator = btreeComparator; + } + + public java.util.Comparator getBtreeComparator() { + return btreeComparator; + } + + public void setBtreeMinKey(final int btMinKey) { + this.btMinKey = btMinKey; + } + + public int getBtreeMinKey() { + return btMinKey; + } + + public void setByteOrder(final int byteOrder) { + this.byteOrder = byteOrder; + } + + public int getByteOrder() { + return byteOrder; + } + + public boolean getByteSwapped() { + return byteOrder != 0 && byteOrder != DbUtil.default_lorder(); + } + + public void setBtreePrefixCalculator( + final BtreePrefixCalculator btreePrefixCalculator) { + this.btreePrefixCalculator = btreePrefixCalculator; + } + + public BtreePrefixCalculator getBtreePrefixCalculator() { + return btreePrefixCalculator; + } + + public void setCacheSize(final long cacheSize) { + this.cacheSize = cacheSize; + } + + public long getCacheSize() { + return cacheSize; + } + + public void setCacheCount(final int cacheCount) { + this.cacheCount = cacheCount; + } + + public int getCacheCount() { + return cacheCount; + } + + public void setChecksum(final boolean checksum) { + this.checksum = checksum; + } + + public boolean getChecksum() { + return checksum; + } + + public void setDirtyRead(final boolean dirtyRead) { + this.dirtyRead = dirtyRead; + } + + public boolean getDirtyRead() { + return dirtyRead; + } + + public void setDuplicateComparator(final java.util.Comparator duplicateComparator) { + this.duplicateComparator = duplicateComparator; + } + + public java.util.Comparator getDuplicateComparator() { + return duplicateComparator; + } + + public void setEncrypted(final String password) { + this.password = password; + } + + public boolean getEncrypted() { + return (password != null); + } + + public void setErrorHandler(final ErrorHandler errorHandler) { + this.errorHandler = errorHandler; + } + + public ErrorHandler getErrorHandler() { + return errorHandler; + } + + public void setErrorPrefix(final String errorPrefix) { + this.errorPrefix = errorPrefix; + } + + public String getErrorPrefix() { + return errorPrefix; + } + + public void setErrorStream(final java.io.OutputStream errorStream) { + this.errorStream = errorStream; + } + + public java.io.OutputStream getErrorStream() { + return errorStream; + } + + public void setExclusiveCreate(final boolean exclusiveCreate) { + this.exclusiveCreate = exclusiveCreate; + } + + public boolean getExclusiveCreate() { + return exclusiveCreate; + } + + public void setFeedbackHandler(final FeedbackHandler feedbackHandler) { + this.feedbackHandler = feedbackHandler; + } + + public FeedbackHandler getFeedbackHandler() { + return feedbackHandler; + } + + public void setHashFillFactor(final int hashFillFactor) { + this.hashFillFactor = hashFillFactor; + } + + public int getHashFillFactor() { + return hashFillFactor; + } + + public void setHasher(final Hasher hasher) { + this.hasher = hasher; + } + + public Hasher getHasher() { + return hasher; + } + + public void setHashNumElements(final int hashNumElements) { + this.hashNumElements = hashNumElements; + } + + public int getHashNumElements() { + return hashNumElements; + } + + public void setMessageHandler(final MessageHandler messageHandler) { + this.messageHandler = messageHandler; + } + + public MessageHandler getMessageHandler() { + return messageHandler; + } + + public void setMessageStream(final java.io.OutputStream messageStream) { + this.messageStream = messageStream; + } + + public java.io.OutputStream getMessageStream() { + return messageStream; + } + + public void setMode(final int mode) { + this.mode = mode; + } + + public long getMode() { + return mode; + } + + public void setNoMMap(final boolean noMMap) { + this.noMMap = noMMap; + } + + public boolean getNoMMap() { + return noMMap; + } + + public void setPageSize(final int pageSize) { + this.pageSize = pageSize; + } + + public int getPageSize() { + return pageSize; + } + + public void setPanicHandler(final PanicHandler panicHandler) { + this.panicHandler = panicHandler; + } + + public PanicHandler getPanicHandler() { + return panicHandler; + } + + public void setQueueExtentSize(final int queueExtentSize) { + this.queueExtentSize = queueExtentSize; + } + + public int getQueueExtentSize() { + return queueExtentSize; + } + + public void setQueueInOrder(final boolean queueInOrder) { + this.queueInOrder = queueInOrder; + } + + public boolean getQueueInOrder() { + return queueInOrder; + } + + public void setReadOnly(final boolean readOnly) { + this.readOnly = readOnly; + } + + public boolean getReadOnly() { + return readOnly; + } + + public void setRecordNumberAppender(final RecordNumberAppender recnoAppender) { + this.recnoAppender = recnoAppender; + } + + public RecordNumberAppender getRecordNumberAppender() { + return recnoAppender; + } + + public void setRecordDelimiter(final int recordDelimiter) { + this.recordDelimiter = recordDelimiter; + } + + public int getRecordDelimiter() { + return recordDelimiter; + } + + public void setRecordLength(final int recordLength) { + this.recordLength = recordLength; + } + + public int getRecordLength() { + return recordLength; + } + + public void setBtreeRecordNumbers(final boolean btreeRecordNumbers) { + this.btreeRecordNumbers = btreeRecordNumbers; + } + + public boolean getBtreeRecordNumbers() { + return btreeRecordNumbers; + } + + public void setRecordPad(final int recordPad) { + this.recordPad = recordPad; + } + + public int getRecordPad() { + return recordPad; + } + + public void setRecordSource(final java.io.File recordSource) { + this.recordSource = recordSource; + } + + public java.io.File getRecordSource() { + return recordSource; + } + + public void setRenumbering(final boolean renumbering) { + this.renumbering = renumbering; + } + + public boolean getRenumbering() { + return renumbering; + } + + public void setReverseSplitOff(final boolean reverseSplitOff) { + this.reverseSplitOff = reverseSplitOff; + } + + public boolean getReverseSplitOff() { + return reverseSplitOff; + } + + public void setSortedDuplicates(final boolean sortedDuplicates) { + this.sortedDuplicates = sortedDuplicates; + } + + public boolean getSortedDuplicates() { + return sortedDuplicates; + } + + public void setUnsortedDuplicates(final boolean unsortedDuplicates) { + this.unsortedDuplicates = unsortedDuplicates; + } + + public boolean getUnsortedDuplicates() { + return unsortedDuplicates; + } + + public void setSnapshot(final boolean snapshot) { + this.snapshot = snapshot; + } + + public boolean getSnapshot() { + return snapshot; + } + + public boolean getTransactional() { + return transactional; + } + + public void setTransactional(final boolean transactional) { + this.transactional = transactional; + } + + public void setTransactionNotDurable(final boolean transactionNotDurable) { + this.transactionNotDurable = transactionNotDurable; + } + + public boolean getTransactionNotDurable() { + return transactionNotDurable; + } + + public void setTruncate(final boolean truncate) { + this.truncate = truncate; + } + + public boolean getTruncate() { + return truncate; + } + + public void setType(final DatabaseType type) { + this.type = type; + } + + public DatabaseType getType() { + return type; + } + + public void setXACreate(final boolean xaCreate) { + this.xaCreate = xaCreate; + } + + public boolean getXACreate() { + return xaCreate; + } + + /* package */ + Db createDatabase(final DbEnv dbenv) + throws DatabaseException { + + int createFlags = 0; + + createFlags |= xaCreate ? DbConstants.DB_XA_CREATE : 0; + return new Db(dbenv, createFlags); + } + + /* package */ + Db openDatabase(final DbEnv dbenv, + final DbTxn txn, + final String fileName, + final String databaseName) + throws DatabaseException, java.io.FileNotFoundException { + + final Db db = createDatabase(dbenv); + // The DB_THREAD flag is inherited from the environment + // (defaulting to ON if no environment handle is supplied). + boolean threaded = (dbenv == null || + (dbenv.get_open_flags() & DbConstants.DB_THREAD) != 0); + + int openFlags = 0; + openFlags |= allowCreate ? DbConstants.DB_CREATE : 0; + openFlags |= dirtyRead ? DbConstants.DB_DIRTY_READ : 0; + openFlags |= exclusiveCreate ? DbConstants.DB_EXCL : 0; + openFlags |= noMMap ? DbConstants.DB_NOMMAP : 0; + openFlags |= readOnly ? DbConstants.DB_RDONLY : 0; + openFlags |= threaded ? DbConstants.DB_THREAD : 0; + openFlags |= truncate ? DbConstants.DB_TRUNCATE : 0; + + if (transactional && txn == null) + openFlags |= DbConstants.DB_AUTO_COMMIT; + + configureDatabase(db, DEFAULT); + boolean succeeded = false; + try { + db.open(txn, fileName, databaseName, type.getId(), openFlags, mode); + succeeded = true; + return db; + } finally { + if (!succeeded) + try { + db.close(0); + } catch (Throwable t) { + // Ignore it -- an exception is already in flight. + } + } + } + + /* package */ + void configureDatabase(final Db db, final DatabaseConfig oldConfig) + throws DatabaseException { + + int dbFlags = 0; + dbFlags |= checksum ? DbConstants.DB_CHKSUM : 0; + dbFlags |= (password != null) ? DbConstants.DB_ENCRYPT : 0; + dbFlags |= btreeRecordNumbers ? DbConstants.DB_RECNUM : 0; + dbFlags |= queueInOrder ? DbConstants.DB_INORDER : 0; + dbFlags |= renumbering ? DbConstants.DB_RENUMBER : 0; + dbFlags |= reverseSplitOff ? DbConstants.DB_REVSPLITOFF : 0; + dbFlags |= sortedDuplicates ? DbConstants.DB_DUPSORT : 0; + dbFlags |= snapshot ? DbConstants.DB_SNAPSHOT : 0; + dbFlags |= unsortedDuplicates ? DbConstants.DB_DUP : 0; + dbFlags |= transactionNotDurable ? DbConstants.DB_TXN_NOT_DURABLE : 0; + + if (dbFlags != 0) + db.set_flags(dbFlags); + + if (btMinKey != oldConfig.btMinKey) + db.set_bt_minkey(btMinKey); + if (byteOrder != oldConfig.byteOrder) + db.set_lorder(byteOrder); + if (cacheSize != oldConfig.cacheSize || + cacheCount != oldConfig.cacheCount) + db.set_cachesize(cacheSize, cacheCount); + if (errorStream != oldConfig.errorStream) + db.set_error_stream(errorStream); + if (errorPrefix != oldConfig.errorPrefix) + db.set_errpfx(errorPrefix); + if (hashFillFactor != oldConfig.hashFillFactor) + db.set_h_ffactor(hashFillFactor); + if (hashNumElements != oldConfig.hashNumElements) + db.set_h_nelem(hashNumElements); + if (messageStream != oldConfig.messageStream) + db.set_message_stream(messageStream); + if (pageSize != oldConfig.pageSize) + db.set_pagesize(pageSize); + if (password != oldConfig.password) + db.set_encrypt(password, DbConstants.DB_ENCRYPT_AES); + if (queueExtentSize != oldConfig.queueExtentSize) + db.set_q_extentsize(queueExtentSize); + if (recordDelimiter != oldConfig.recordDelimiter) + db.set_re_delim(recordDelimiter); + if (recordLength != oldConfig.recordLength) + db.set_re_len(recordLength); + if (recordPad != oldConfig.recordPad) + db.set_re_pad(recordPad); + if (recordSource != oldConfig.recordSource) + db.set_re_source(recordSource.toString()); + + if (btreeComparator != oldConfig.btreeComparator) + db.set_bt_compare(btreeComparator); + if (btreePrefixCalculator != oldConfig.btreePrefixCalculator) + db.set_bt_prefix(btreePrefixCalculator); + if (duplicateComparator != oldConfig.duplicateComparator) + db.set_dup_compare(duplicateComparator); + if (feedbackHandler != oldConfig.feedbackHandler) + db.set_feedback(feedbackHandler); + if (errorHandler != oldConfig.errorHandler) + db.set_errcall(errorHandler); + if (hasher != oldConfig.hasher) + db.set_h_hash(hasher); + if (messageHandler != oldConfig.messageHandler) + db.set_msgcall(messageHandler); + if (recnoAppender != oldConfig.recnoAppender) + db.set_append_recno(recnoAppender); + if (panicHandler != oldConfig.panicHandler) + db.set_paniccall(panicHandler); + } + + /* package */ + DatabaseConfig(final Db db) + throws DatabaseException { + + type = DatabaseType.fromInt(db.get_type()); + + final int openFlags = db.get_open_flags(); + allowCreate = (openFlags & DbConstants.DB_CREATE) != 0; + dirtyRead = (openFlags & DbConstants.DB_DIRTY_READ) != 0; + exclusiveCreate = (openFlags & DbConstants.DB_EXCL) != 0; + noMMap = (openFlags & DbConstants.DB_NOMMAP) != 0; + readOnly = (openFlags & DbConstants.DB_RDONLY) != 0; + truncate = (openFlags & DbConstants.DB_TRUNCATE) != 0; + + final int dbFlags = db.get_flags(); + checksum = (dbFlags & DbConstants.DB_CHKSUM) != 0; + btreeRecordNumbers = (dbFlags & DbConstants.DB_RECNUM) != 0; + queueInOrder = (dbFlags & DbConstants.DB_INORDER) != 0; + renumbering = (dbFlags & DbConstants.DB_RENUMBER) != 0; + reverseSplitOff = (dbFlags & DbConstants.DB_REVSPLITOFF) != 0; + sortedDuplicates = (dbFlags & DbConstants.DB_DUPSORT) != 0; + snapshot = (dbFlags & DbConstants.DB_SNAPSHOT) != 0; + unsortedDuplicates = (dbFlags & DbConstants.DB_DUP) != 0; + transactionNotDurable = (dbFlags & DbConstants.DB_TXN_NOT_DURABLE) != 0; + + if (type == DatabaseType.BTREE) { + btMinKey = db.get_bt_minkey(); + } + byteOrder = db.get_lorder(); + // Call get_cachesize* on the DbEnv to avoid this error: + // DB->get_cachesize: method not permitted in shared environment + cacheSize = db.get_env().get_cachesize(); + cacheCount = db.get_env().get_cachesize_ncache(); + errorStream = db.get_error_stream(); + errorPrefix = db.get_errpfx(); + if (type == DatabaseType.HASH) { + hashFillFactor = db.get_h_ffactor(); + hashNumElements = db.get_h_nelem(); + } + messageStream = db.get_message_stream(); + // Not available by design + password = ((dbFlags & DbConstants.DB_ENCRYPT) != 0) ? "" : null; + if (type == DatabaseType.QUEUE) { + queueExtentSize = db.get_q_extentsize(); + } + if (type == DatabaseType.QUEUE || type == DatabaseType.RECNO) { + recordLength = db.get_re_len(); + recordPad = db.get_re_pad(); + } + if (type == DatabaseType.RECNO) { + recordDelimiter = db.get_re_delim(); + recordSource = (db.get_re_source() == null) ? null : + new java.io.File(db.get_re_source()); + } + transactional = db.get_transactional(); + + btreeComparator = db.get_bt_compare(); + btreePrefixCalculator = db.get_bt_prefix(); + duplicateComparator = db.get_dup_compare(); + feedbackHandler = db.get_feedback(); + errorHandler = db.get_errcall(); + hasher = db.get_h_hash(); + messageHandler = db.get_msgcall(); + recnoAppender = db.get_append_recno(); + panicHandler = db.get_paniccall(); + } +} diff --git a/db/java/src/com/sleepycat/db/DatabaseEntry.java b/db/java/src/com/sleepycat/db/DatabaseEntry.java new file mode 100644 index 000000000..be67fe024 --- /dev/null +++ b/db/java/src/com/sleepycat/db/DatabaseEntry.java @@ -0,0 +1,181 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: DatabaseEntry.java,v 1.7 2004/09/22 18:01:03 bostic Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbUtil; + +public class DatabaseEntry { + + /* Currently, JE stores all data records as byte array */ + protected byte[] data; + protected int dlen = 0; + protected int doff = 0; + protected int flags = 0; + protected int offset = 0; + protected int size = 0; + protected int ulen = 0; + + /* + * IGNORE is used to avoid returning data that is not needed. It may not + * be used as the key DBT in a put since the PARTIAL flag is not allowed; + * use UNUSED for that instead. + */ + + /* package */ + static final DatabaseEntry IGNORE = new DatabaseEntry(); + static { + IGNORE.setUserBuffer(0, true); + IGNORE.setPartial(0, 0, true); // dlen == 0, so no data ever returned + } + /* package */ + static final DatabaseEntry UNUSED = new DatabaseEntry(); + + protected static final int INT32SZ = 4; + + /* + * Constructors + */ + + public DatabaseEntry() { + } + + public DatabaseEntry(final byte[] data) { + this.data = data; + if (data != null) { + this.size = data.length; + } + } + + public DatabaseEntry(final byte[] data, final int offset, final int size) { + this.data = data; + this.offset = offset; + this.size = size; + } + + /* + * Accessors + */ + + public byte[] getData() { + return data; + } + + public void setData(final byte[] data, final int offset, final int size) { + this.data = data; + this.offset = offset; + this.size = size; + } + + public void setData(final byte[] data) { + setData(data, 0, (data == null) ? 0 : data.length); + } + + /* package */ + int getMultiFlag() { + return 0; + } + + public int getOffset() { + return offset; + } + + public void setOffset(final int offset) { + this.offset = offset; + } + + public int getPartialLength() { + return dlen; + } + + public int getPartialOffset() { + return doff; + } + + public boolean getPartial() { + return (flags & DbConstants.DB_DBT_PARTIAL) != 0; + } + + public void setPartialOffset(final int doff) { + this.doff = doff; + } + + public void setPartialLength(final int dlen) { + this.dlen = dlen; + } + + public void setPartial(final boolean partial) { + if (partial) + flags |= DbConstants.DB_DBT_PARTIAL; + else + flags &= ~DbConstants.DB_DBT_PARTIAL; + } + + public void setPartial(final int doff, + final int dlen, + final boolean partial) { + setPartialOffset(doff); + setPartialLength(dlen); + setPartial(partial); + } + + public int getRecordNumber() { + return DbUtil.array2int(data, offset); + } + + public void setRecordNumber(final int recno) { + if (data == null || data.length < INT32SZ) { + data = new byte[INT32SZ]; + size = INT32SZ; + ulen = 0; + offset = 0; + } + DbUtil.int2array(recno, data, 0); + } + + public boolean getReuseBuffer() { + return 0 == + (flags & (DbConstants.DB_DBT_MALLOC | DbConstants.DB_DBT_USERMEM)); + } + + public void setReuseBuffer(boolean reuse) { + if (reuse) + flags &= ~(DbConstants.DB_DBT_MALLOC | DbConstants.DB_DBT_USERMEM); + else { + flags &= ~DbConstants.DB_DBT_USERMEM; + flags |= DbConstants.DB_DBT_MALLOC; + } + } + + public int getSize() { + return size; + } + + public void setSize(final int size) { + this.size = size; + } + + public boolean getUserBuffer() { + return (flags & DbConstants.DB_DBT_USERMEM) != 0; + } + + public int getUserBufferLength() { + return ulen; + } + + public void setUserBuffer(final int length, final boolean usermem) { + this.ulen = length; + if (usermem) { + flags &= ~DbConstants.DB_DBT_MALLOC; + flags |= DbConstants.DB_DBT_USERMEM; + } else + flags &= ~DbConstants.DB_DBT_USERMEM; + } +} diff --git a/db/java/src/com/sleepycat/db/DatabaseException.java b/db/java/src/com/sleepycat/db/DatabaseException.java new file mode 100644 index 000000000..17ffb5432 --- /dev/null +++ b/db/java/src/com/sleepycat/db/DatabaseException.java @@ -0,0 +1,54 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: DatabaseException.java,v 1.1 2004/04/06 20:43:36 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbEnv; + +public class DatabaseException extends Exception { + private Environment dbenv; + private int errno; + + public DatabaseException(final String s) { + this(s, 0, (Environment)null); + } + + public DatabaseException(final String s, final int errno) { + this(s, errno, (Environment)null); + } + + public DatabaseException(final String s, + final int errno, + final Environment dbenv) { + super(s); + this.errno = errno; + this.dbenv = dbenv; + } + + protected DatabaseException(final String s, + final int errno, + final DbEnv dbenv) { + this(s, errno, (dbenv == null) ? null : dbenv.wrapper); + } + + public Environment getEnvironment() { + return dbenv; + } + + public int getErrno() { + return errno; + } + + public String toString() { + String s = super.toString(); + if (errno != 0) + s += ": " + DbEnv.strerror(errno); + return s; + } +} diff --git a/db/java/src/com/sleepycat/db/DatabaseStats.java b/db/java/src/com/sleepycat/db/DatabaseStats.java new file mode 100644 index 000000000..c372ca435 --- /dev/null +++ b/db/java/src/com/sleepycat/db/DatabaseStats.java @@ -0,0 +1,15 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: DatabaseStats.java,v 1.2 2004/09/28 19:30:37 mjc Exp $ + */ +package com.sleepycat.db; + +public abstract class DatabaseStats { + // no public constructor + protected DatabaseStats() {} +} diff --git a/db/java/src/com/sleepycat/db/DatabaseType.java b/db/java/src/com/sleepycat/db/DatabaseType.java new file mode 100644 index 000000000..2407498cf --- /dev/null +++ b/db/java/src/com/sleepycat/db/DatabaseType.java @@ -0,0 +1,65 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: DatabaseType.java,v 1.2 2004/04/21 01:09:09 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public final class DatabaseType { + public static final DatabaseType BTREE = + new DatabaseType("BTREE", DbConstants.DB_BTREE); + + public static final DatabaseType HASH = + new DatabaseType("HASH", DbConstants.DB_HASH); + + public static final DatabaseType QUEUE = + new DatabaseType("QUEUE", DbConstants.DB_QUEUE); + + public static final DatabaseType RECNO = + new DatabaseType("RECNO", DbConstants.DB_RECNO); + + public static final DatabaseType UNKNOWN = + new DatabaseType("UNKNOWN", DbConstants.DB_UNKNOWN); + + /* package */ + static DatabaseType fromInt(int type) { + switch(type) { + case DbConstants.DB_BTREE: + return BTREE; + case DbConstants.DB_HASH: + return HASH; + case DbConstants.DB_QUEUE: + return QUEUE; + case DbConstants.DB_RECNO: + return DatabaseType.RECNO; + case DbConstants.DB_UNKNOWN: + return DatabaseType.UNKNOWN; + default: + throw new IllegalArgumentException( + "Unknown database type: " + type); + } + } + + private String statusName; + private int id; + + private DatabaseType(final String statusName, final int id) { + this.statusName = statusName; + this.id = id; + } + + /* package */ + int getId() { + return id; + } + + public String toString() { + return "DatabaseType." + statusName; + } +} diff --git a/db/java/src/com/sleepycat/db/DeadlockException.java b/db/java/src/com/sleepycat/db/DeadlockException.java new file mode 100644 index 000000000..afe9b498a --- /dev/null +++ b/db/java/src/com/sleepycat/db/DeadlockException.java @@ -0,0 +1,20 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: DeadlockException.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbEnv; + +public class DeadlockException extends DatabaseException { + protected DeadlockException(final String s, + final int errno, + final DbEnv dbenv) { + super(s, errno, dbenv); + } +} diff --git a/db/java/src/com/sleepycat/db/Environment.java b/db/java/src/com/sleepycat/db/Environment.java new file mode 100644 index 000000000..b45c56d58 --- /dev/null +++ b/db/java/src/com/sleepycat/db/Environment.java @@ -0,0 +1,354 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: Environment.java,v 1.5 2004/08/05 19:20:34 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbEnv; + +public class Environment { + private DbEnv dbenv; + private java.io.File home; + private int autoCommitFlag; + + /* package */ + Environment(final DbEnv dbenv) + throws DatabaseException { + + this.dbenv = dbenv; + dbenv.wrapper = this; + } + + public Environment(final java.io.File home, EnvironmentConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + this(EnvironmentConfig.checkNull(config).openEnvironment(home)); + this.home = home; + this.autoCommitFlag = + ((dbenv.get_open_flags() & DbConstants.DB_INIT_TXN) == 0) ? 0 : + DbConstants.DB_AUTO_COMMIT; + } + + public void close() + throws DatabaseException { + + dbenv.close(0); + } + + /* package */ + DbEnv unwrap() { + return dbenv; + } + + public static void remove(final java.io.File home, + final boolean force, + EnvironmentConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + config = EnvironmentConfig.checkNull(config); + int flags = force ? DbConstants.DB_FORCE : 0; + flags |= config.getUseEnvironment() ? + DbConstants.DB_USE_ENVIRON : 0; + flags |= config.getUseEnvironmentRoot() ? + DbConstants.DB_USE_ENVIRON_ROOT : 0; + final DbEnv dbenv = config.createEnvironment(); + dbenv.remove(home.toString(), flags); + } + + public void setConfig(final EnvironmentConfig config) + throws DatabaseException { + + config.configureEnvironment(dbenv, new EnvironmentConfig(dbenv)); + } + + public EnvironmentConfig getConfig() + throws DatabaseException { + + return new EnvironmentConfig(dbenv); + } + + /* Manage databases. */ + public Database openDatabase(final Transaction txn, + final String fileName, + final String databaseName, + DatabaseConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + return new Database( + DatabaseConfig.checkNull(config).openDatabase(dbenv, + (txn == null) ? null : txn.txn, + fileName, databaseName)); + } + + public SecondaryDatabase openSecondaryDatabase( + final Transaction txn, + final String fileName, + final String databaseName, + final Database primaryDatabase, + SecondaryConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + return new SecondaryDatabase( + SecondaryConfig.checkNull(config).openSecondaryDatabase( + dbenv, (txn == null) ? null : txn.txn, + fileName, databaseName, primaryDatabase.db), + primaryDatabase); + } + + public void removeDatabase(final Transaction txn, + final String fileName, + final String databaseName) + throws DatabaseException, java.io.FileNotFoundException { + + dbenv.dbremove((txn == null) ? null : txn.txn, + fileName, databaseName, + (txn == null) ? autoCommitFlag : 0); + } + + public void renameDatabase(final Transaction txn, + final String fileName, + final String databaseName, + final String newName) + throws DatabaseException, java.io.FileNotFoundException { + + dbenv.dbrename((txn == null) ? null : txn.txn, + fileName, databaseName, newName, + (txn == null) ? autoCommitFlag : 0); + } + + public java.io.File getHome() + throws DatabaseException { + + return home; + } + + /* Cache management. */ + public int trickleCacheWrite(int percent) + throws DatabaseException { + + return dbenv.memp_trickle(percent); + } + + /* Locking */ + public int detectDeadlocks(LockDetectMode mode) + throws DatabaseException { + + return dbenv.lock_detect(0, mode.getFlag()); + } + + public Lock getLock(int locker, + boolean noWait, + DatabaseEntry object, + LockRequestMode mode) + throws DatabaseException { + + return Lock.wrap( + dbenv.lock_get(locker, noWait ? DbConstants.DB_LOCK_NOWAIT : 0, + object, mode.getFlag())); + } + + public void putLock(Lock lock) + throws DatabaseException { + + dbenv.lock_put(lock.unwrap()); + } + + public int createLockerID() + throws DatabaseException { + + return dbenv.lock_id(); + } + + public void freeLockerID(int id) + throws DatabaseException { + + dbenv.lock_id_free(id); + } + + public void lockVector(int locker, boolean noWait, LockRequest[] list) + throws DatabaseException { + + dbenv.lock_vec(locker, noWait ? DbConstants.DB_LOCK_NOWAIT : 0, + list, 0, list.length); + } + + /* Logging */ + public LogCursor openLogCursor() + throws DatabaseException { + + return LogCursor.wrap(dbenv.log_cursor(0)); + } + + public String getLogFileName(LogSequenceNumber lsn) + throws DatabaseException { + + return dbenv.log_file(lsn); + } + + /* Replication support */ + public int electReplicationMaster(int nsites, + int nvotes, + int priority, + int timeout) + throws DatabaseException { + + return dbenv.rep_elect(nsites, nvotes, priority, timeout, + 0 /* unused flags */); + } + + public ReplicationStatus processReplicationMessage(DatabaseEntry control, + DatabaseEntry rec, + int envid) + throws DatabaseException { + + final DbEnv.RepProcessMessage wrappedID = new DbEnv.RepProcessMessage(); + wrappedID.envid = envid; + // Create a new entry so that rec isn't overwritten + final DatabaseEntry cdata = + new DatabaseEntry(rec.getData(), rec.getOffset(), rec.getSize()); + final LogSequenceNumber lsn = new LogSequenceNumber(); + final int ret = + dbenv.rep_process_message(control, cdata, wrappedID, lsn); + return ReplicationStatus.getStatus(ret, cdata, wrappedID.envid, lsn); + } + + public void startReplication(DatabaseEntry cdata, boolean master) + throws DatabaseException { + + dbenv.rep_start(cdata, + master ? DbConstants.DB_REP_MASTER : DbConstants.DB_REP_CLIENT); + } + + /* Statistics */ + public CacheStats getCacheStats(StatsConfig config) + throws DatabaseException { + + return dbenv.memp_stat(StatsConfig.checkNull(config).getFlags()); + } + + public CacheFileStats[] getCacheFileStats(StatsConfig config) + throws DatabaseException { + + return dbenv.memp_fstat(StatsConfig.checkNull(config).getFlags()); + } + + public LogStats getLogStats(StatsConfig config) + throws DatabaseException { + + return dbenv.log_stat(StatsConfig.checkNull(config).getFlags()); + } + + public ReplicationStats getReplicationStats(StatsConfig config) + throws DatabaseException { + + return dbenv.rep_stat(StatsConfig.checkNull(config).getFlags()); + } + + public LockStats getLockStats(StatsConfig config) + throws DatabaseException { + + return dbenv.lock_stat(StatsConfig.checkNull(config).getFlags()); + } + + public TransactionStats getTransactionStats(StatsConfig config) + throws DatabaseException { + + return dbenv.txn_stat(StatsConfig.checkNull(config).getFlags()); + } + + /* Transaction management */ + public Transaction beginTransaction(final Transaction parent, + TransactionConfig config) + throws DatabaseException { + + return new Transaction( + TransactionConfig.checkNull(config).beginTransaction(dbenv, + (parent == null) ? null : parent.txn)); + } + + public void checkpoint(CheckpointConfig config) + throws DatabaseException { + + CheckpointConfig.checkNull(config).runCheckpoint(dbenv); + } + + public void logFlush(LogSequenceNumber lsn) + throws DatabaseException { + + dbenv.log_flush(lsn); + } + + public LogSequenceNumber logPut(DatabaseEntry data, boolean flush) + throws DatabaseException { + + final LogSequenceNumber lsn = new LogSequenceNumber(); + dbenv.log_put(lsn, data, flush ? DbConstants.DB_FLUSH : 0); + return lsn; + } + + public java.io.File[] getArchiveLogFiles(boolean includeInUse) + throws DatabaseException { + + final String[] logNames = + dbenv.log_archive(DbConstants.DB_ARCH_ABS | + (includeInUse ? DbConstants.DB_ARCH_LOG : 0)); + final java.io.File[] logFiles = new java.io.File[logNames.length]; + for (int i = 0; i < logNames.length; i++) + logFiles[i] = new java.io.File(logNames[i]); + return logFiles; + } + + public java.io.File[] getArchiveDatabases() + throws DatabaseException { + + final String[] dbNames = dbenv.log_archive(DbConstants.DB_ARCH_DATA); + final java.io.File[] dbFiles = new java.io.File[dbNames.length]; + for (int i = 0; i < dbNames.length; i++) + dbFiles[i] = new java.io.File(home, dbNames[i]); + return dbFiles; + } + + public void removeOldLogFiles() + throws DatabaseException { + + dbenv.log_archive(DbConstants.DB_ARCH_REMOVE); + } + + public PreparedTransaction[] recover(final int count, + final boolean continued) + throws DatabaseException { + + return dbenv.txn_recover(count, + continued ? DbConstants.DB_NEXT : DbConstants.DB_FIRST); + } + + /* Panic the environment, or stop a panic. */ + public void panic(boolean onoff) + throws DatabaseException { + + dbenv.set_flags(DbConstants.DB_PANIC_ENVIRONMENT, onoff); + } + + /* Version information */ + public static String getVersionString() { + return DbEnv.get_version_string(); + } + + public static int getVersionMajor() { + return DbEnv.get_version_major(); + } + + public static int getVersionMinor() { + return DbEnv.get_version_minor(); + } + + public static int getVersionPatch() { + return DbEnv.get_version_patch(); + } +} diff --git a/db/java/src/com/sleepycat/db/EnvironmentConfig.java b/db/java/src/com/sleepycat/db/EnvironmentConfig.java new file mode 100644 index 000000000..23e6ab309 --- /dev/null +++ b/db/java/src/com/sleepycat/db/EnvironmentConfig.java @@ -0,0 +1,1076 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: EnvironmentConfig.java,v 1.13 2004/09/28 19:30:37 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbEnv; + +public class EnvironmentConfig implements Cloneable { + /* + * For internal use, to allow null as a valid value for + * the config parameter. + */ + public static final EnvironmentConfig DEFAULT = new EnvironmentConfig(); + + /* package */ + static EnvironmentConfig checkNull(EnvironmentConfig config) { + return (config == null) ? DEFAULT : config; + } + + /* Parameters */ + private int mode = 0644; + private int cacheCount = 0; + private long cacheSize = 0L; + private java.util.Vector dataDirs = new java.util.Vector(); + private int envid = 0; + private java.io.OutputStream errorStream = null; + private String errorPrefix = null; + private byte[][] lockConflicts = null; + private LockDetectMode lockDetectMode = LockDetectMode.NONE; + private int maxLocks = 0; + private int maxLockers = 0; + private int maxLockObjects = 0; + private int maxLogFileSize = 0; + private int logBufferSize = 0; + private java.io.OutputStream messageStream = null; + private java.io.File logDirectory = null; + private int logRegionSize = 0; + private long mmapSize = 0L; + private String password = null; + private long replicationLimit = 0L; + private String rpcServer = null; + private long rpcClientTimeout = 0L; + private long rpcServerTimeout = 0L; + private long segmentId = 0L; + private int testAndSetSpins = 0; + private long lockTimeout = 0L; + private int txnMaxActive = 0; + private long txnTimeout = 0L; + private java.util.Date txnTimestamp = null; + private String temporaryDirectory = null; + + /* Open flags */ + private boolean allowCreate = false; + private boolean initializeCache = false; + private boolean initializeCDB = false; + private boolean initializeLocking = false; + private boolean initializeLogging = false; + private boolean initializeReplication = false; + private boolean joinEnvironment = false; + private boolean lockDown = false; + private boolean isPrivate = false; + private boolean readOnly = false; + private boolean runRecovery = false; + private boolean runFatalRecovery = false; + private boolean systemMemory = false; + private boolean threaded = true; // Handles are threaded by default in Java + private boolean transactional = false; + private boolean useEnvironment = false; + private boolean useEnvironmentRoot = false; + + /* Flags */ + private boolean cdbLockAllDatabases = false; + private boolean directDatabaseIO = false; + private boolean directLogIO = false; + private boolean dsyncLog = false; + private boolean initializeRegions = false; + private boolean logAutoRemove = false; + private boolean logInMemory = false; + private boolean noLocking = false; + private boolean noMMap = false; + private boolean noPanic = false; + private boolean overwrite = false; + private boolean txnNoSync = false; + private boolean txnNotDurable = false; + private boolean txnWriteNoSync = false; + private boolean yieldCPU = false; + + /* Verbose Flags */ + private boolean verboseDeadlock = false; + private boolean verboseRecovery = false; + private boolean verboseReplication = false; + private boolean verboseWaitsFor = false; + + /* Callbacks */ + private ErrorHandler errorHandler = null; + private FeedbackHandler feedbackHandler = null; + private LogRecordHandler logRecordHandler = null; + private MessageHandler messageHandler = null; + private PanicHandler panicHandler = null; + private ReplicationTransport replicationTransport = null; + + public EnvironmentConfig() { + } + + public void setAllowCreate(final boolean allowCreate) { + this.allowCreate = allowCreate; + } + + public boolean getAllowCreate() { + return allowCreate; + } + + public void setCacheSize(final long cacheSize) { + this.cacheSize = cacheSize; + } + + public long getCacheSize() { + return cacheSize; + } + + public void setCacheCount(final int cacheCount) { + this.cacheCount = cacheCount; + } + + public int getCacheCount() { + return cacheCount; + } + + public void setCDBLockAllDatabases(final boolean cdbLockAllDatabases) { + this.cdbLockAllDatabases = cdbLockAllDatabases; + } + + public boolean getCDBLockAllDatabases() { + return cdbLockAllDatabases; + } + + public void addDataDir(final String dataDir) { + this.dataDirs.add(dataDir); + } + + public String[] getDataDirs() { + final String[] dirs = new String[dataDirs.size()]; + dataDirs.copyInto(dirs); + return dirs; + } + + public void setDirectDatabaseIO(final boolean directDatabaseIO) { + this.directDatabaseIO = directDatabaseIO; + } + + public boolean getDirectDatabaseIO() { + return directDatabaseIO; + } + + public void setDirectLogIO(final boolean directLogIO) { + this.directLogIO = directLogIO; + } + + public boolean getDirectLogIO() { + return directLogIO; + } + + public void setDsyncLog(final boolean dsyncLog) { + this.dsyncLog = dsyncLog; + } + + public boolean getDsyncLog() { + return dsyncLog; + } + + public void setEncrypted(final String password) { + this.password = password; + } + + public boolean getEncrypted() { + return (password != null); + } + + public void setErrorHandler(final ErrorHandler errorHandler) { + this.errorHandler = errorHandler; + } + + public ErrorHandler getErrorHandler() { + return errorHandler; + } + + public void setErrorPrefix(final String errorPrefix) { + this.errorPrefix = errorPrefix; + } + + public String getErrorPrefix() { + return errorPrefix; + } + + public void setErrorStream(final java.io.OutputStream errorStream) { + this.errorStream = errorStream; + } + + public java.io.OutputStream getErrorStream() { + return errorStream; + } + + public void setFeedbackHandler(final FeedbackHandler feedbackHandler) { + this.feedbackHandler = feedbackHandler; + } + + public FeedbackHandler getFeedbackHandler() { + return feedbackHandler; + } + + public void setInitializeCache(final boolean initializeCache) { + this.initializeCache = initializeCache; + } + + public boolean getInitializeCache() { + return initializeCache; + } + + public void setInitializeCDB(final boolean initializeCDB) { + this.initializeCDB = initializeCDB; + } + + public boolean getInitializeCDB() { + return initializeCDB; + } + + public void setInitializeLocking(final boolean initializeLocking) { + this.initializeLocking = initializeLocking; + } + + public boolean getInitializeLocking() { + return initializeLocking; + } + + public void setInitializeLogging(final boolean initializeLogging) { + this.initializeLogging = initializeLogging; + } + + public boolean getInitializeLogging() { + return initializeLogging; + } + + public void setInitializeRegions(final boolean initializeRegions) { + this.initializeRegions = initializeRegions; + } + + public boolean getInitializeRegions() { + return initializeRegions; + } + + public void setInitializeReplication(final boolean initializeReplication) { + this.initializeReplication = initializeReplication; + } + + public boolean getInitializeReplication() { + return initializeReplication; + } + + public void setJoinEnvironment(final boolean joinEnvironment) { + this.joinEnvironment = joinEnvironment; + } + + public boolean getJoinEnvironment() { + return joinEnvironment; + } + + public void setLockConflicts(final byte[][] lockConflicts) { + this.lockConflicts = lockConflicts; + } + + public byte[][] getLockConflicts() { + return lockConflicts; + } + + public void setLockDetectMode(final LockDetectMode lockDetectMode) { + this.lockDetectMode = lockDetectMode; + } + + public LockDetectMode getLockDetectMode() { + return lockDetectMode; + } + + public void setLockDown(final boolean lockDown) { + this.lockDown = lockDown; + } + + public boolean getLockDown() { + return lockDown; + } + + public void setLockTimeout(final long lockTimeout) { + this.lockTimeout = lockTimeout; + } + + public long getLockTimeout() { + return lockTimeout; + } + + public void setLogAutoRemove(final boolean logAutoRemove) { + this.logAutoRemove = logAutoRemove; + } + + public boolean getLogAutoRemove() { + return logAutoRemove; + } + + public void setLogInMemory(final boolean logInMemory) { + this.logInMemory = logInMemory; + } + + public boolean getLogInMemory() { + return logInMemory; + } + + public void setLogRecordHandler(final LogRecordHandler logRecordHandler) { + this.logRecordHandler = logRecordHandler; + } + + public LogRecordHandler getLogRecordHandler() { + return logRecordHandler; + } + + public void setMaxLocks(final int maxLocks) { + this.maxLocks = maxLocks; + } + + public int getMaxLocks() { + return maxLocks; + } + + public void setMaxLockers(final int maxLockers) { + this.maxLockers = maxLockers; + } + + public int getMaxLockers() { + return maxLockers; + } + + public void setMaxLockObjects(final int maxLockObjects) { + this.maxLockObjects = maxLockObjects; + } + + public int getMaxLockObjects() { + return maxLockObjects; + } + + public void setMaxLogFileSize(final int maxLogFileSize) { + this.maxLogFileSize = maxLogFileSize; + } + + public int getMaxLogFileSize() { + return maxLogFileSize; + } + + public void setLogBufferSize(final int logBufferSize) { + this.logBufferSize = logBufferSize; + } + + public int getLogBufferSize() { + return logBufferSize; + } + + public void setLogDirectory(final java.io.File logDirectory) { + this.logDirectory = logDirectory; + } + + public java.io.File getLogDirectory() { + return logDirectory; + } + + public void setLogRegionSize(final int logRegionSize) { + this.logRegionSize = logRegionSize; + } + + public int getLogRegionSize() { + return logRegionSize; + } + + public void setMessageHandler(final MessageHandler messageHandler) { + this.messageHandler = messageHandler; + } + + public MessageHandler getMessageHandler() { + return messageHandler; + } + + public void setMessageStream(final java.io.OutputStream messageStream) { + this.messageStream = messageStream; + } + + public java.io.OutputStream getMessageStream() { + return messageStream; + } + + public void setMMapSize(final long mmapSize) { + this.mmapSize = mmapSize; + } + + public long getMMapSize() { + return mmapSize; + } + + public void setMode(final int mode) { + this.mode = mode; + } + + public long getMode() { + return mode; + } + + public void setNoLocking(final boolean noLocking) { + this.noLocking = noLocking; + } + + public boolean getNoLocking() { + return noLocking; + } + + public void setNoMMap(final boolean noMMap) { + this.noMMap = noMMap; + } + + public boolean getNoMMap() { + return noMMap; + } + + public void setNoPanic(final boolean noPanic) { + this.noPanic = noPanic; + } + + public boolean getNoPanic() { + return noPanic; + } + + public void setOverwrite(final boolean overwrite) { + this.overwrite = overwrite; + } + + public boolean getOverwrite() { + return overwrite; + } + + public void setPanicHandler(final PanicHandler panicHandler) { + this.panicHandler = panicHandler; + } + + public PanicHandler getPanicHandler() { + return panicHandler; + } + + public void setPrivate(final boolean isPrivate) { + this.isPrivate = isPrivate; + } + + public boolean getPrivate() { + return isPrivate; + } + + public boolean getReadOnly() { + return readOnly; + } + + public void setReadOnly(final boolean readOnly) { + this.readOnly = readOnly; + } + + public void setReplicationLimit(final long replicationLimit) { + this.replicationLimit = replicationLimit; + } + + public long getReplicationLimit() { + return replicationLimit; + } + + public void setReplicationTransport(final int envid, + final ReplicationTransport replicationTransport) { + + this.envid = envid; + this.replicationTransport = replicationTransport; + } + + public ReplicationTransport getReplicationTransport() { + return replicationTransport; + } + + public void setRunFatalRecovery(final boolean runFatalRecovery) { + this.runFatalRecovery = runFatalRecovery; + } + + public boolean getRunFatalRecovery() { + return runFatalRecovery; + } + + public void setRunRecovery(final boolean runRecovery) { + this.runRecovery = runRecovery; + } + + public boolean getRunRecovery() { + return runRecovery; + } + + public void setSystemMemory(final boolean systemMemory) { + this.systemMemory = systemMemory; + } + + public boolean getSystemMemory() { + return systemMemory; + } + + public void setRPCServer(final String rpcServer, + final long rpcClientTimeout, + final long rpcServerTimeout) { + this.rpcServer = rpcServer; + this.rpcClientTimeout = rpcClientTimeout; + this.rpcServerTimeout = rpcServerTimeout; + + // Turn off threading for RPC client handles. + this.threaded = false; + } + + public void setSegmentId(final long segmentId) { + this.segmentId = segmentId; + } + + public long getSegmentId() { + return segmentId; + } + + public void setTemporaryDirectory(final String temporaryDirectory) { + this.temporaryDirectory = temporaryDirectory; + } + + public String getTemporaryDirectory() { + return temporaryDirectory; + } + + public void setTestAndSetSpins(final int testAndSetSpins) { + this.testAndSetSpins = testAndSetSpins; + } + + public int getTestAndSetSpins() { + return testAndSetSpins; + } + + public void setThreaded(final boolean threaded) { + this.threaded = threaded; + } + + public boolean getThreaded() { + return threaded; + } + + public void setTransactional(final boolean transactional) { + this.transactional = transactional; + } + + public boolean getTransactional() { + return transactional; + } + + public void setTxnNoSync(final boolean txnNoSync) { + this.txnNoSync = txnNoSync; + } + + public boolean getTxnNoSync() { + return txnNoSync; + } + + public void setTxnNotDurable(final boolean txnNotDurable) { + this.txnNotDurable = txnNotDurable; + } + + public boolean getTxnNotDurable() { + return txnNotDurable; + } + + public void setTxnMaxActive(final int txnMaxActive) { + this.txnMaxActive = txnMaxActive; + } + + public int getTxnMaxActive() { + return txnMaxActive; + } + + public void setTxnTimeout(final long txnTimeout) { + this.txnTimeout = txnTimeout; + } + + public long getTxnTimeout() { + return txnTimeout; + } + + public void setTxnTimestamp(final java.util.Date txnTimestamp) { + this.txnTimestamp = txnTimestamp; + } + + public java.util.Date getTxnTimestamp() { + return txnTimestamp; + } + + public void setTxnWriteNoSync(final boolean txnWriteNoSync) { + this.txnWriteNoSync = txnWriteNoSync; + } + + public boolean getTxnWriteNoSync() { + return txnWriteNoSync; + } + + public void setUseEnvironment(final boolean useEnvironment) { + this.useEnvironment = useEnvironment; + } + + public boolean getUseEnvironment() { + return useEnvironment; + } + + public void setUseEnvironmentRoot(final boolean useEnvironmentRoot) { + this.useEnvironmentRoot = useEnvironmentRoot; + } + + public boolean getUseEnvironmentRoot() { + return useEnvironmentRoot; + } + + public void setVerboseDeadlock(final boolean verboseDeadlock) { + this.verboseDeadlock = verboseDeadlock; + } + + public boolean getVerboseDeadlock() { + return verboseDeadlock; + } + + public void setVerboseRecovery(final boolean verboseRecovery) { + this.verboseRecovery = verboseRecovery; + } + + public boolean getVerboseRecovery() { + return verboseRecovery; + } + + public void setVerboseReplication(final boolean verboseReplication) { + this.verboseReplication = verboseReplication; + } + + public boolean getVerboseReplication() { + return verboseReplication; + } + + public void setVerboseWaitsFor(final boolean verboseWaitsFor) { + this.verboseWaitsFor = verboseWaitsFor; + } + + public boolean getVerboseWaitsFor() { + return verboseWaitsFor; + } + + public void setYieldCPU(final boolean yieldCPU) { + this.yieldCPU = yieldCPU; + } + + public boolean getYieldCPU() { + return yieldCPU; + } + + private boolean lockConflictsEqual(byte[][] lc1, byte[][]lc2) { + if (lc1 == lc2) + return true; + if (lc1 == null || lc2 == null || lc1.length != lc2.length) + return false; + for (int i = 0; i < lc1.length; i++) { + if (lc1[i].length != lc2[i].length) + return false; + for (int j = 0; j < lc1[i].length; j++) + if (lc1[i][j] != lc2[i][j]) + return false; + } + return true; + } + + /* package */ + DbEnv openEnvironment(final java.io.File home) + throws DatabaseException, java.io.FileNotFoundException { + + final DbEnv dbenv = createEnvironment(); + int openFlags = 0; + + openFlags |= allowCreate ? DbConstants.DB_CREATE : 0; + openFlags |= initializeCache ? DbConstants.DB_INIT_MPOOL : 0; + openFlags |= initializeCDB ? DbConstants.DB_INIT_CDB : 0; + openFlags |= initializeLocking ? DbConstants.DB_INIT_LOCK : 0; + openFlags |= initializeLogging ? DbConstants.DB_INIT_LOG : 0; + openFlags |= initializeReplication ? DbConstants.DB_INIT_REP : 0; + openFlags |= joinEnvironment ? DbConstants.DB_JOINENV : 0; + openFlags |= lockDown ? DbConstants.DB_LOCKDOWN : 0; + openFlags |= isPrivate ? DbConstants.DB_PRIVATE : 0; + openFlags |= readOnly ? DbConstants.DB_RDONLY : 0; + openFlags |= runRecovery ? DbConstants.DB_RECOVER : 0; + openFlags |= runFatalRecovery ? DbConstants.DB_RECOVER_FATAL : 0; + openFlags |= systemMemory ? DbConstants.DB_SYSTEM_MEM : 0; + openFlags |= threaded ? DbConstants.DB_THREAD : 0; + openFlags |= transactional ? DbConstants.DB_INIT_TXN : 0; + openFlags |= useEnvironment ? DbConstants.DB_USE_ENVIRON : 0; + openFlags |= useEnvironmentRoot ? DbConstants.DB_USE_ENVIRON_ROOT : 0; + + boolean succeeded = false; + try { + dbenv.open(home.toString(), openFlags, mode); + succeeded = true; + return dbenv; + } finally { + if (!succeeded) + try { + dbenv.close(0); + } catch (Throwable t) { + // Ignore it -- an exception is already in flight. + } + } + } + + + /* package */ + DbEnv createEnvironment() + throws DatabaseException { + + int createFlags = 0; + + if (rpcServer != null) + createFlags |= DbConstants.DB_RPCCLIENT; + + final DbEnv dbenv = new DbEnv(createFlags); + configureEnvironment(dbenv, DEFAULT); + return dbenv; + } + + /* package */ + void configureEnvironment(final DbEnv dbenv, + final EnvironmentConfig oldConfig) + throws DatabaseException { + + if (errorHandler != oldConfig.errorHandler) + dbenv.set_errcall(errorHandler); + if (errorPrefix != oldConfig.errorPrefix && + errorPrefix != null && !errorPrefix.equals(oldConfig.errorPrefix)) + dbenv.set_errpfx(errorPrefix); + if (errorStream != oldConfig.errorStream) + dbenv.set_error_stream(errorStream); + + if (rpcServer != oldConfig.rpcServer || + rpcClientTimeout != oldConfig.rpcClientTimeout || + rpcServerTimeout != oldConfig.rpcServerTimeout) + dbenv.set_rpc_server(null, rpcServer, + rpcClientTimeout, rpcServerTimeout, 0); + + // We always set DB_TIME_NOTGRANTED in the Java API, because + // LockNotGrantedException extends DeadlockException, so there's no + // reason why an application would prefer one to the other. + int onFlags = DbConstants.DB_TIME_NOTGRANTED; + int offFlags = 0; + + if (cdbLockAllDatabases && !oldConfig.cdbLockAllDatabases) + onFlags |= DbConstants.DB_CDB_ALLDB; + if (!cdbLockAllDatabases && oldConfig.cdbLockAllDatabases) + offFlags |= DbConstants.DB_CDB_ALLDB; + + if (directDatabaseIO && !oldConfig.directDatabaseIO) + onFlags |= DbConstants.DB_DIRECT_DB; + if (!directDatabaseIO && oldConfig.directDatabaseIO) + offFlags |= DbConstants.DB_DIRECT_DB; + + if (directLogIO && !oldConfig.directLogIO) + onFlags |= DbConstants.DB_DIRECT_LOG; + if (!directLogIO && oldConfig.directLogIO) + offFlags |= DbConstants.DB_DIRECT_LOG; + + if (dsyncLog && !oldConfig.dsyncLog) + onFlags |= DbConstants.DB_DSYNC_LOG; + if (!dsyncLog && oldConfig.dsyncLog) + offFlags |= DbConstants.DB_DSYNC_LOG; + + if (initializeRegions && !oldConfig.initializeRegions) + onFlags |= DbConstants.DB_REGION_INIT; + if (!initializeRegions && oldConfig.initializeRegions) + offFlags |= DbConstants.DB_REGION_INIT; + + if (logAutoRemove && !oldConfig.logAutoRemove) + onFlags |= DbConstants.DB_LOG_AUTOREMOVE; + if (!logAutoRemove && oldConfig.logAutoRemove) + offFlags |= DbConstants.DB_LOG_AUTOREMOVE; + + if (logInMemory && !oldConfig.logInMemory) + onFlags |= DbConstants.DB_LOG_INMEMORY; + if (!logInMemory && oldConfig.logInMemory) + offFlags |= DbConstants.DB_LOG_INMEMORY; + + if (noLocking && !oldConfig.noLocking) + onFlags |= DbConstants.DB_NOLOCKING; + if (!noLocking && oldConfig.noLocking) + offFlags |= DbConstants.DB_NOLOCKING; + + if (noMMap && !oldConfig.noMMap) + onFlags |= DbConstants.DB_NOMMAP; + if (!noMMap && oldConfig.noMMap) + offFlags |= DbConstants.DB_NOMMAP; + + if (noPanic && !oldConfig.noPanic) + onFlags |= DbConstants.DB_NOPANIC; + if (!noPanic && oldConfig.noPanic) + offFlags |= DbConstants.DB_NOPANIC; + + if (overwrite && !oldConfig.overwrite) + onFlags |= DbConstants.DB_OVERWRITE; + if (!overwrite && oldConfig.overwrite) + offFlags |= DbConstants.DB_OVERWRITE; + + if (txnNoSync && !oldConfig.txnNoSync) + onFlags |= DbConstants.DB_TXN_NOSYNC; + if (!txnNoSync && oldConfig.txnNoSync) + offFlags |= DbConstants.DB_TXN_NOSYNC; + + if (txnNotDurable && !oldConfig.txnNotDurable) + onFlags |= DbConstants.DB_TXN_NOT_DURABLE; + if (!txnNotDurable && oldConfig.txnNotDurable) + offFlags |= DbConstants.DB_TXN_NOT_DURABLE; + + if (txnWriteNoSync && !oldConfig.txnWriteNoSync) + onFlags |= DbConstants.DB_TXN_WRITE_NOSYNC; + if (!txnWriteNoSync && oldConfig.txnWriteNoSync) + offFlags |= DbConstants.DB_TXN_WRITE_NOSYNC; + + if (yieldCPU && !oldConfig.yieldCPU) + onFlags |= DbConstants.DB_YIELDCPU; + if (!yieldCPU && oldConfig.yieldCPU) + offFlags |= DbConstants.DB_YIELDCPU; + + if (onFlags != 0) + dbenv.set_flags(onFlags, true); + if (offFlags != 0) + dbenv.set_flags(offFlags, false); + + /* Verbose flags */ + onFlags = 0; + offFlags = 0; + + if (verboseDeadlock && !oldConfig.verboseDeadlock) + onFlags |= DbConstants.DB_VERB_DEADLOCK; + if (!verboseDeadlock && oldConfig.verboseDeadlock) + offFlags |= DbConstants.DB_VERB_DEADLOCK; + + if (verboseRecovery && !oldConfig.verboseRecovery) + onFlags |= DbConstants.DB_VERB_RECOVERY; + if (!verboseRecovery && oldConfig.verboseRecovery) + offFlags |= DbConstants.DB_VERB_RECOVERY; + + if (verboseReplication && !oldConfig.verboseReplication) + onFlags |= DbConstants.DB_VERB_REPLICATION; + if (!verboseReplication && oldConfig.verboseReplication) + offFlags |= DbConstants.DB_VERB_REPLICATION; + + if (verboseWaitsFor && !oldConfig.verboseWaitsFor) + onFlags |= DbConstants.DB_VERB_WAITSFOR; + if (!verboseWaitsFor && oldConfig.verboseWaitsFor) + offFlags |= DbConstants.DB_VERB_WAITSFOR; + + if (onFlags != 0) + dbenv.set_verbose(onFlags, true); + if (offFlags != 0) + dbenv.set_verbose(offFlags, false); + + /* Callbacks */ + if (feedbackHandler != oldConfig.feedbackHandler) + dbenv.set_feedback(feedbackHandler); + if (logRecordHandler != oldConfig.logRecordHandler) + dbenv.set_app_dispatch(logRecordHandler); + if (messageHandler != oldConfig.messageHandler) + dbenv.set_msgcall(messageHandler); + if (panicHandler != oldConfig.panicHandler) + dbenv.set_paniccall(panicHandler); + if (replicationTransport != oldConfig.replicationTransport) + dbenv.set_rep_transport(envid, replicationTransport); + + /* Other settings */ + if (cacheSize != oldConfig.cacheSize || + cacheCount != oldConfig.cacheCount) + dbenv.set_cachesize(cacheSize, cacheCount); + for (final java.util.Enumeration e = dataDirs.elements(); + e.hasMoreElements();) { + final String dir = (String)e.nextElement(); + if (!oldConfig.dataDirs.contains(dir)) + dbenv.set_data_dir(dir); + } + if (!lockConflictsEqual(lockConflicts, oldConfig.lockConflicts)) + dbenv.set_lk_conflicts(lockConflicts); + if (lockDetectMode != oldConfig.lockDetectMode) + dbenv.set_lk_detect(lockDetectMode.getFlag()); + if (maxLocks != oldConfig.maxLocks) + dbenv.set_lk_max_locks(maxLocks); + if (maxLockers != oldConfig.maxLockers) + dbenv.set_lk_max_lockers(maxLockers); + if (maxLockObjects != oldConfig.maxLockObjects) + dbenv.set_lk_max_objects(maxLockObjects); + if (maxLogFileSize != oldConfig.maxLogFileSize) + dbenv.set_lg_max(maxLogFileSize); + if (logBufferSize != oldConfig.logBufferSize) + dbenv.set_lg_bsize(logBufferSize); + if (logDirectory != oldConfig.logDirectory && logDirectory != null && + !logDirectory.equals(oldConfig.logDirectory)) + dbenv.set_lg_dir(logDirectory.toString()); + if (logRegionSize != oldConfig.logRegionSize) + dbenv.set_lg_regionmax(logRegionSize); + if (messageStream != oldConfig.messageStream) + dbenv.set_message_stream(messageStream); + if (mmapSize != oldConfig.mmapSize) + dbenv.set_mp_mmapsize(mmapSize); + if (password != null) + dbenv.set_encrypt(password, DbConstants.DB_ENCRYPT_AES); + if (replicationLimit != oldConfig.replicationLimit) + dbenv.set_rep_limit(replicationLimit); + if (segmentId != oldConfig.segmentId) + dbenv.set_shm_key(segmentId); + if (testAndSetSpins != oldConfig.testAndSetSpins) + dbenv.set_tas_spins(testAndSetSpins); + if (lockTimeout != oldConfig.lockTimeout) + dbenv.set_timeout(lockTimeout, DbConstants.DB_SET_LOCK_TIMEOUT); + if (txnMaxActive != oldConfig.txnMaxActive) + dbenv.set_tx_max(txnMaxActive); + if (txnTimeout != oldConfig.txnTimeout) + dbenv.set_timeout(txnTimeout, DbConstants.DB_SET_TXN_TIMEOUT); + if (txnTimestamp != oldConfig.txnTimestamp && txnTimestamp != null && + !txnTimestamp.equals(oldConfig.txnTimestamp)) + dbenv.set_tx_timestamp(txnTimestamp); + if (temporaryDirectory != oldConfig.temporaryDirectory && + temporaryDirectory != null && + !temporaryDirectory.equals(oldConfig.temporaryDirectory)) + dbenv.set_tmp_dir(temporaryDirectory); + } + + /* package */ + EnvironmentConfig(final DbEnv dbenv) + throws DatabaseException { + + final int openFlags = dbenv.get_open_flags(); + + allowCreate = ((openFlags & DbConstants.DB_CREATE) != 0); + initializeCache = ((openFlags & DbConstants.DB_INIT_MPOOL) != 0); + initializeCDB = ((openFlags & DbConstants.DB_INIT_CDB) != 0); + initializeLocking = ((openFlags & DbConstants.DB_INIT_LOCK) != 0); + initializeLogging = ((openFlags & DbConstants.DB_INIT_LOG) != 0); + initializeReplication = ((openFlags & DbConstants.DB_INIT_REP) != 0); + joinEnvironment = ((openFlags & DbConstants.DB_JOINENV) != 0); + lockDown = ((openFlags & DbConstants.DB_LOCKDOWN) != 0); + isPrivate = ((openFlags & DbConstants.DB_PRIVATE) != 0); + readOnly = ((openFlags & DbConstants.DB_RDONLY) != 0); + runRecovery = ((openFlags & DbConstants.DB_RECOVER) != 0); + runFatalRecovery = ((openFlags & DbConstants.DB_RECOVER_FATAL) != 0); + systemMemory = ((openFlags & DbConstants.DB_SYSTEM_MEM) != 0); + threaded = ((openFlags & DbConstants.DB_THREAD) != 0); + transactional = ((openFlags & DbConstants.DB_INIT_TXN) != 0); + useEnvironment = ((openFlags & DbConstants.DB_USE_ENVIRON) != 0); + useEnvironmentRoot = + ((openFlags & DbConstants.DB_USE_ENVIRON_ROOT) != 0); + + final int envFlags = dbenv.get_flags(); + + cdbLockAllDatabases = ((envFlags & DbConstants.DB_CDB_ALLDB) != 0); + directDatabaseIO = ((envFlags & DbConstants.DB_DIRECT_DB) != 0); + directLogIO = ((envFlags & DbConstants.DB_DIRECT_LOG) != 0); + dsyncLog = ((envFlags & DbConstants.DB_DSYNC_LOG) != 0); + initializeRegions = ((envFlags & DbConstants.DB_REGION_INIT) != 0); + logAutoRemove = ((envFlags & DbConstants.DB_LOG_AUTOREMOVE) != 0); + logInMemory = ((envFlags & DbConstants.DB_LOG_INMEMORY) != 0); + noLocking = ((envFlags & DbConstants.DB_NOLOCKING) != 0); + noMMap = ((envFlags & DbConstants.DB_NOMMAP) != 0); + noPanic = ((envFlags & DbConstants.DB_NOPANIC) != 0); + overwrite = ((envFlags & DbConstants.DB_OVERWRITE) != 0); + txnNoSync = ((envFlags & DbConstants.DB_TXN_NOSYNC) != 0); + txnNotDurable = ((envFlags & DbConstants.DB_TXN_NOT_DURABLE) != 0); + txnWriteNoSync = ((envFlags & DbConstants.DB_TXN_WRITE_NOSYNC) != 0); + yieldCPU = ((envFlags & DbConstants.DB_YIELDCPU) != 0); + + /* Verbose flags */ + verboseDeadlock = dbenv.get_verbose(DbConstants.DB_VERB_DEADLOCK); + verboseRecovery = dbenv.get_verbose(DbConstants.DB_VERB_RECOVERY); + verboseReplication = dbenv.get_verbose(DbConstants.DB_VERB_REPLICATION); + verboseWaitsFor = dbenv.get_verbose(DbConstants.DB_VERB_WAITSFOR); + + /* Callbacks */ + errorHandler = dbenv.get_errcall(); + feedbackHandler = dbenv.get_feedback(); + logRecordHandler = dbenv.get_app_dispatch(); + messageHandler = dbenv.get_msgcall(); + panicHandler = dbenv.get_paniccall(); + // XXX: replicationTransport and envid aren't available? + + /* Other settings */ + cacheSize = dbenv.get_cachesize(); + cacheCount = dbenv.get_cachesize_ncache(); + + String[] dataDirArray = dbenv.get_data_dirs(); + if (dataDirArray == null) + dataDirArray = new String[0]; + dataDirs = new java.util.Vector(dataDirArray.length); + for (int i = 0; i < dataDirArray.length; i++) + dataDirs.set(i, dataDirArray[i]); + + errorPrefix = dbenv.get_errpfx(); + errorStream = dbenv.get_error_stream(); + + if (initializeLocking) { + lockConflicts = dbenv.get_lk_conflicts(); + lockDetectMode = LockDetectMode.fromFlag(dbenv.get_lk_detect()); + lockTimeout = dbenv.get_timeout(DbConstants.DB_SET_LOCK_TIMEOUT); + maxLocks = dbenv.get_lk_max_locks(); + maxLockers = dbenv.get_lk_max_lockers(); + maxLockObjects = dbenv.get_lk_max_objects(); + txnTimeout = dbenv.get_timeout(DbConstants.DB_SET_TXN_TIMEOUT); + } else { + lockConflicts = null; + lockDetectMode = LockDetectMode.NONE; + lockTimeout = 0L; + maxLocks = 0; + maxLockers = 0; + maxLockObjects = 0; + txnTimeout = 0L; + } + if (initializeLogging) { + maxLogFileSize = dbenv.get_lg_max(); + logBufferSize = dbenv.get_lg_bsize(); + logDirectory = (dbenv.get_lg_dir() == null) ? null : + new java.io.File(dbenv.get_lg_dir()); + logRegionSize = dbenv.get_lg_regionmax(); + } else { + maxLogFileSize = 0; + logBufferSize = 0; + logDirectory = null; + logRegionSize = 0; + } + messageStream = dbenv.get_message_stream(); + mmapSize = dbenv.get_mp_mmapsize(); + + // XXX: intentional information loss? + password = (dbenv.get_encrypt_flags() == 0) ? null : ""; + + if (initializeReplication) { + replicationLimit = dbenv.get_rep_limit(); + } else { + replicationLimit = 0L; + } + + // XXX: no way to find RPC server? + rpcServer = null; + rpcClientTimeout = 0; + rpcServerTimeout = 0; + + segmentId = dbenv.get_shm_key(); + testAndSetSpins = dbenv.get_tas_spins(); + if (transactional) { + txnMaxActive = dbenv.get_tx_max(); + final long txnTimestampSeconds = dbenv.get_tx_timestamp(); + if (txnTimestampSeconds != 0L) + txnTimestamp = new java.util.Date(txnTimestampSeconds * 1000); + else + txnTimestamp = null; + } else { + txnMaxActive = 0; + txnTimestamp = null; + } + temporaryDirectory = dbenv.get_tmp_dir(); + } +} diff --git a/db/java/src/com/sleepycat/db/ErrorHandler.java b/db/java/src/com/sleepycat/db/ErrorHandler.java new file mode 100644 index 000000000..ad4a9e5c4 --- /dev/null +++ b/db/java/src/com/sleepycat/db/ErrorHandler.java @@ -0,0 +1,14 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ErrorHandler.java,v 1.2 2004/04/20 20:45:11 mjc Exp $ + */ +package com.sleepycat.db; + +public interface ErrorHandler { + void error(Environment dbenv, String errpfx, String msg); +} diff --git a/db/java/src/com/sleepycat/db/FeedbackHandler.java b/db/java/src/com/sleepycat/db/FeedbackHandler.java new file mode 100644 index 000000000..96b2e6eb2 --- /dev/null +++ b/db/java/src/com/sleepycat/db/FeedbackHandler.java @@ -0,0 +1,16 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: FeedbackHandler.java,v 1.2 2004/04/21 01:09:09 mjc Exp $ + */ +package com.sleepycat.db; + +public interface FeedbackHandler { + void recoveryFeedback(Environment dbenv, int percent); + void upgradeFeedback(Database db, int percent); + void verifyFeedback(Database db, int percent); +} diff --git a/db/java/src/com/sleepycat/db/HashStats.java b/db/java/src/com/sleepycat/db/HashStats.java new file mode 100644 index 000000000..3e04452b7 --- /dev/null +++ b/db/java/src/com/sleepycat/db/HashStats.java @@ -0,0 +1,116 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class HashStats extends DatabaseStats { + // no public constructor + protected HashStats() {} + + private int hash_magic; + public int getMagic() { + return hash_magic; + } + + private int hash_version; + public int getVersion() { + return hash_version; + } + + private int hash_metaflags; + public int getMetaFlags() { + return hash_metaflags; + } + + private int hash_nkeys; + public int getNumKeys() { + return hash_nkeys; + } + + private int hash_ndata; + public int getNumData() { + return hash_ndata; + } + + private int hash_pagesize; + public int getPageSize() { + return hash_pagesize; + } + + private int hash_ffactor; + public int getFfactor() { + return hash_ffactor; + } + + private int hash_buckets; + public int getBuckets() { + return hash_buckets; + } + + private int hash_free; + public int getFree() { + return hash_free; + } + + private int hash_bfree; + public int getBFree() { + return hash_bfree; + } + + private int hash_bigpages; + public int getBigPages() { + return hash_bigpages; + } + + private int hash_big_bfree; + public int getBigBFree() { + return hash_big_bfree; + } + + private int hash_overflows; + public int getOverflows() { + return hash_overflows; + } + + private int hash_ovfl_free; + public int getOvflFree() { + return hash_ovfl_free; + } + + private int hash_dup; + public int getDup() { + return hash_dup; + } + + private int hash_dup_free; + public int getDupFree() { + return hash_dup_free; + } + + public String toString() { + return "HashStats:" + + "\n hash_magic=" + hash_magic + + "\n hash_version=" + hash_version + + "\n hash_metaflags=" + hash_metaflags + + "\n hash_nkeys=" + hash_nkeys + + "\n hash_ndata=" + hash_ndata + + "\n hash_pagesize=" + hash_pagesize + + "\n hash_ffactor=" + hash_ffactor + + "\n hash_buckets=" + hash_buckets + + "\n hash_free=" + hash_free + + "\n hash_bfree=" + hash_bfree + + "\n hash_bigpages=" + hash_bigpages + + "\n hash_big_bfree=" + hash_big_bfree + + "\n hash_overflows=" + hash_overflows + + "\n hash_ovfl_free=" + hash_ovfl_free + + "\n hash_dup=" + hash_dup + + "\n hash_dup_free=" + hash_dup_free + ; + } +} diff --git a/db/java/src/com/sleepycat/db/Hasher.java b/db/java/src/com/sleepycat/db/Hasher.java new file mode 100644 index 000000000..cc46a8549 --- /dev/null +++ b/db/java/src/com/sleepycat/db/Hasher.java @@ -0,0 +1,14 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: Hasher.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db; + +public interface Hasher { + int hash(Database db, byte[] data, int len); +} diff --git a/db/java/src/com/sleepycat/db/JoinConfig.java b/db/java/src/com/sleepycat/db/JoinConfig.java new file mode 100644 index 000000000..41689767c --- /dev/null +++ b/db/java/src/com/sleepycat/db/JoinConfig.java @@ -0,0 +1,39 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: JoinConfig.java,v 1.4 2004/09/28 19:30:37 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public class JoinConfig implements Cloneable { + public static final JoinConfig DEFAULT = new JoinConfig(); + + /* package */ + static JoinConfig checkNull(JoinConfig config) { + return (config == null) ? DEFAULT : config; + } + + private boolean noSort; + + public JoinConfig() { + } + + public void setNoSort(final boolean noSort) { + this.noSort = noSort; + } + + public boolean getNoSort() { + return noSort; + } + + /* package */ + int getFlags() { + return noSort ? DbConstants.DB_JOIN_NOSORT : 0; + } +} diff --git a/db/java/src/com/sleepycat/db/JoinCursor.java b/db/java/src/com/sleepycat/db/JoinCursor.java new file mode 100644 index 000000000..5527a6177 --- /dev/null +++ b/db/java/src/com/sleepycat/db/JoinCursor.java @@ -0,0 +1,60 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: JoinCursor.java,v 1.2 2004/04/09 15:08:38 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.Dbc; + +public class JoinCursor { + private Database database; + private Dbc dbc; + private JoinConfig config; + + JoinCursor(final Database database, + final Dbc dbc, + final JoinConfig config) { + this.database = database; + this.dbc = dbc; + this.config = config; + } + + public void close() + throws DatabaseException { + + dbc.close(); + } + + public Database getDatabase() { + return database; + } + + public JoinConfig getConfig() { + return config; + } + + public OperationStatus getNext(final DatabaseEntry key, LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, DatabaseEntry.IGNORE, + DbConstants.DB_JOIN_ITEM | + LockMode.getFlag(lockMode))); + } + + public OperationStatus getNext(final DatabaseEntry key, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.get(key, data, LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } +} diff --git a/db/java/src/com/sleepycat/db/KeyRange.java b/db/java/src/com/sleepycat/db/KeyRange.java new file mode 100644 index 000000000..5ddbb123d --- /dev/null +++ b/db/java/src/com/sleepycat/db/KeyRange.java @@ -0,0 +1,16 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: KeyRange.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db; + +public class KeyRange { + public double equal; + public double greater; + public double less; +} diff --git a/db/java/src/com/sleepycat/db/Lock.java b/db/java/src/com/sleepycat/db/Lock.java new file mode 100644 index 000000000..64559e359 --- /dev/null +++ b/db/java/src/com/sleepycat/db/Lock.java @@ -0,0 +1,31 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: Lock.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbLock; + +public final class Lock { + private DbLock dbLock; + + private Lock(final DbLock dblock) { + this.dbLock = dbLock; + dbLock.wrapper = this; + } + + /* package */ + static Lock wrap(final DbLock dblock) { + return (dblock == null) ? null : new Lock(dblock); + } + + /* package */ + DbLock unwrap() { + return dbLock; + } +} diff --git a/db/java/src/com/sleepycat/db/LockDetectMode.java b/db/java/src/com/sleepycat/db/LockDetectMode.java new file mode 100644 index 000000000..ac163cad3 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LockDetectMode.java @@ -0,0 +1,90 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: LockDetectMode.java,v 1.2 2004/04/21 01:09:09 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public final class LockDetectMode { + public static final LockDetectMode NONE = + new LockDetectMode("NONE", 0); + + public static final LockDetectMode DEFAULT = + new LockDetectMode("DEFAULT", DbConstants.DB_LOCK_DEFAULT); + + public static final LockDetectMode EXPIRE = + new LockDetectMode("EXPIRE", DbConstants.DB_LOCK_EXPIRE); + + public static final LockDetectMode MAXLOCKS = + new LockDetectMode("MAXLOCKS", DbConstants.DB_LOCK_MAXLOCKS); + + public static final LockDetectMode MAXWRITE = + new LockDetectMode("MAXWRITE", DbConstants.DB_LOCK_MAXWRITE); + + public static final LockDetectMode MINLOCKS = + new LockDetectMode("MINLOCKS", DbConstants.DB_LOCK_MINLOCKS); + + public static final LockDetectMode MINWRITE = + new LockDetectMode("MINWRITE", DbConstants.DB_LOCK_MINWRITE); + + public static final LockDetectMode OLDEST = + new LockDetectMode("OLDEST", DbConstants.DB_LOCK_OLDEST); + + public static final LockDetectMode RANDOM = + new LockDetectMode("RANDOM", DbConstants.DB_LOCK_RANDOM); + + public static final LockDetectMode YOUNGEST = + new LockDetectMode("YOUNGEST", DbConstants.DB_LOCK_YOUNGEST); + + /* package */ + static LockDetectMode fromFlag(int flag) { + switch (flag) { + case 0: + return NONE; + case DbConstants.DB_LOCK_DEFAULT: + return DEFAULT; + case DbConstants.DB_LOCK_EXPIRE: + return EXPIRE; + case DbConstants.DB_LOCK_MAXLOCKS: + return MAXLOCKS; + case DbConstants.DB_LOCK_MAXWRITE: + return MAXWRITE; + case DbConstants.DB_LOCK_MINLOCKS: + return MINLOCKS; + case DbConstants.DB_LOCK_MINWRITE: + return MINWRITE; + case DbConstants.DB_LOCK_OLDEST: + return OLDEST; + case DbConstants.DB_LOCK_RANDOM: + return RANDOM; + case DbConstants.DB_LOCK_YOUNGEST: + return YOUNGEST; + default: + throw new IllegalArgumentException( + "Unknown lock detect mode: " + flag); + } + } + + private String modeName; + private int flag; + + private LockDetectMode(final String modeName, final int flag) { + this.modeName = modeName; + this.flag = flag; + } + + /* package */ + int getFlag() { + return flag; + } + + public String toString() { + return "LockDetectMode." + modeName; + } +} diff --git a/db/java/src/com/sleepycat/db/LockMode.java b/db/java/src/com/sleepycat/db/LockMode.java new file mode 100644 index 000000000..ab03d49a5 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LockMode.java @@ -0,0 +1,40 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: LockMode.java,v 1.2 2004/04/09 15:08:38 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public final class LockMode { + private String lockModeName; + private int flag; + + private LockMode(String lockModeName, int flag) { + this.lockModeName = lockModeName; + this.flag = flag; + } + + public static final LockMode DEFAULT = + new LockMode("DEFAULT", 0); + public static final LockMode DIRTY_READ = + new LockMode("DIRTY_READ", DbConstants.DB_DIRTY_READ); + public static final LockMode DEGREE_2 = + new LockMode("DEGREE_2", DbConstants.DB_DEGREE_2); + public static final LockMode RMW = + new LockMode("RMW", DbConstants.DB_RMW); + + public String toString() { + return "LockMode." + lockModeName; + } + + /* package */ + static int getFlag(LockMode mode) { + return ((mode == null) ? DEFAULT : mode).flag; + } +} diff --git a/db/java/src/com/sleepycat/db/LockNotGrantedException.java b/db/java/src/com/sleepycat/db/LockNotGrantedException.java new file mode 100644 index 000000000..a3f4c0b29 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LockNotGrantedException.java @@ -0,0 +1,57 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: LockNotGrantedException.java,v 1.2 2004/09/28 19:30:37 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbEnv; +import com.sleepycat.db.internal.DbLock; + +public class LockNotGrantedException extends DeadlockException { + private int index; + private Lock lock; + private int mode; + private DatabaseEntry obj; + private int op; + + protected LockNotGrantedException(final String message, + final int op, + final int mode, + final DatabaseEntry obj, + final DbLock lock, + final int index, + final DbEnv dbenv) { + super(message, DbConstants.DB_LOCK_NOTGRANTED, dbenv); + this.op = op; + this.mode = mode; + this.obj = obj; + this.lock = lock.wrapper; + this.index = index; + } + + public int getIndex() { + return index; + } + + public Lock getLock() { + return lock; + } + + public int getMode() { + return mode; + } + + public DatabaseEntry getObj() { + return obj; + } + + public int getOp() { + return op; + } +} diff --git a/db/java/src/com/sleepycat/db/LockOperation.java b/db/java/src/com/sleepycat/db/LockOperation.java new file mode 100644 index 000000000..4bda73728 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LockOperation.java @@ -0,0 +1,65 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: LockOperation.java,v 1.2 2004/04/21 01:09:09 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public final class LockOperation { + public static final LockOperation GET = + new LockOperation("GET", DbConstants.DB_LOCK_GET); + public static final LockOperation GET_TIMEOUT = + new LockOperation("GET_TIMEOUT", DbConstants.DB_LOCK_GET_TIMEOUT); + public static final LockOperation PUT = + new LockOperation("PUT", DbConstants.DB_LOCK_PUT); + public static final LockOperation PUT_ALL = + new LockOperation("PUT_ALL", DbConstants.DB_LOCK_PUT_ALL); + public static final LockOperation PUT_OBJ = + new LockOperation("PUT_OBJ", DbConstants.DB_LOCK_PUT_OBJ); + public static final LockOperation TIMEOUT = + new LockOperation("TIMEOUT", DbConstants.DB_LOCK_TIMEOUT); + + /* package */ + static LockOperation fromFlag(int flag) { + switch (flag) { + case DbConstants.DB_LOCK_GET: + return GET; + case DbConstants.DB_LOCK_GET_TIMEOUT: + return GET_TIMEOUT; + case DbConstants.DB_LOCK_PUT: + return PUT; + case DbConstants.DB_LOCK_PUT_ALL: + return PUT_ALL; + case DbConstants.DB_LOCK_PUT_OBJ: + return PUT_OBJ; + case DbConstants.DB_LOCK_TIMEOUT: + return TIMEOUT; + default: + throw new IllegalArgumentException( + "Unknown lock operation: " + flag); + } + } + + private final String operationName; + private final int flag; + + private LockOperation(final String operationName, final int flag) { + this.operationName = operationName; + this.flag = flag; + } + + public String toString() { + return "LockOperation." + operationName; + } + + /* package */ + int getFlag() { + return flag; + } +} diff --git a/db/java/src/com/sleepycat/db/LockRequest.java b/db/java/src/com/sleepycat/db/LockRequest.java new file mode 100644 index 000000000..d5d5a8fa3 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LockRequest.java @@ -0,0 +1,83 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: LockRequest.java,v 1.3 2004/07/26 17:01:51 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbLock; + +public class LockRequest { + private DbLock lock; + private LockRequestMode mode; + private int modeFlag; + private DatabaseEntry obj; + private int op; + private int timeout; + + public LockRequest(final LockOperation op, + final LockRequestMode mode, + final DatabaseEntry obj, + final Lock lock) { + + this(op, mode, obj, lock, 0); + } + + public LockRequest(final LockOperation op, + final LockRequestMode mode, + final DatabaseEntry obj, + final Lock lock, + final int timeout) { + + this.setOp(op); + this.setMode(mode); + this.setObj(obj); + this.setLock(lock); + this.setTimeout(timeout); + } + + public void setLock(final Lock lock) { + this.lock = lock.unwrap(); + } + + public void setMode(final LockRequestMode mode) { + this.mode = mode; + this.modeFlag = mode.getFlag(); + } + + public void setObj(final DatabaseEntry obj) { + this.obj = obj; + } + + public void setOp(final LockOperation op) { + this.op = op.getFlag(); + } + + public void setTimeout(final int timeout) { + this.timeout = timeout; + } + + public Lock getLock() { + return lock.wrapper; + } + + public LockRequestMode getMode() { + return mode; + } + + public DatabaseEntry getObj() { + return obj; + } + + public LockOperation getOp() { + return LockOperation.fromFlag(op); + } + + public int getTimeout() { + return timeout; + } +} diff --git a/db/java/src/com/sleepycat/db/LockRequestMode.java b/db/java/src/com/sleepycat/db/LockRequestMode.java new file mode 100644 index 000000000..14f49d5c6 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LockRequestMode.java @@ -0,0 +1,43 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: LockRequestMode.java,v 1.2 2004/07/26 17:01:51 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public final class LockRequestMode { + public static final LockRequestMode READ = + new LockRequestMode("READ", DbConstants.DB_LOCK_READ); + public static final LockRequestMode WRITE = + new LockRequestMode("WRITE", DbConstants.DB_LOCK_WRITE); + public static final LockRequestMode IWRITE = + new LockRequestMode("IWRITE", DbConstants.DB_LOCK_IWRITE); + public static final LockRequestMode IREAD = + new LockRequestMode("IREAD", DbConstants.DB_LOCK_IREAD); + public static final LockRequestMode IWR = + new LockRequestMode("IWR", DbConstants.DB_LOCK_IWR); + + /* package */ + private final String operationName; + private final int flag; + + public LockRequestMode(final String operationName, final int flag) { + this.operationName = operationName; + this.flag = flag; + } + + public String toString() { + return "LockRequestMode." + operationName; + } + + /* package */ + int getFlag() { + return flag; + } +} diff --git a/db/java/src/com/sleepycat/db/LockStats.java b/db/java/src/com/sleepycat/db/LockStats.java new file mode 100644 index 000000000..a16a22616 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LockStats.java @@ -0,0 +1,164 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class LockStats { + // no public constructor + protected LockStats() {} + + private int st_id; + public int getId() { + return st_id; + } + + private int st_cur_maxid; + public int getCurMaxId() { + return st_cur_maxid; + } + + private int st_maxlocks; + public int getMaxLocks() { + return st_maxlocks; + } + + private int st_maxlockers; + public int getMaxLockers() { + return st_maxlockers; + } + + private int st_maxobjects; + public int getMaxObjects() { + return st_maxobjects; + } + + private int st_nmodes; + public int getNumModes() { + return st_nmodes; + } + + private int st_nlocks; + public int getNumLocks() { + return st_nlocks; + } + + private int st_maxnlocks; + public int getMaxNlocks() { + return st_maxnlocks; + } + + private int st_nlockers; + public int getNumLockers() { + return st_nlockers; + } + + private int st_maxnlockers; + public int getMaxNlockers() { + return st_maxnlockers; + } + + private int st_nobjects; + public int getNobjects() { + return st_nobjects; + } + + private int st_maxnobjects; + public int getMaxNobjects() { + return st_maxnobjects; + } + + private int st_nconflicts; + public int getNumConflicts() { + return st_nconflicts; + } + + private int st_nrequests; + public int getNumRequests() { + return st_nrequests; + } + + private int st_nreleases; + public int getNumReleases() { + return st_nreleases; + } + + private int st_nnowaits; + public int getNumNowaits() { + return st_nnowaits; + } + + private int st_ndeadlocks; + public int getNumDeadlocks() { + return st_ndeadlocks; + } + + private int st_locktimeout; + public int getLockTimeout() { + return st_locktimeout; + } + + private int st_nlocktimeouts; + public int getNumLockTimeouts() { + return st_nlocktimeouts; + } + + private int st_txntimeout; + public int getTxnTimeout() { + return st_txntimeout; + } + + private int st_ntxntimeouts; + public int getNumTxnTimeouts() { + return st_ntxntimeouts; + } + + private int st_region_wait; + public int getRegionWait() { + return st_region_wait; + } + + private int st_region_nowait; + public int getRegionNowait() { + return st_region_nowait; + } + + private int st_regsize; + public int getRegSize() { + return st_regsize; + } + + public String toString() { + return "LockStats:" + + "\n st_id=" + st_id + + "\n st_cur_maxid=" + st_cur_maxid + + "\n st_maxlocks=" + st_maxlocks + + "\n st_maxlockers=" + st_maxlockers + + "\n st_maxobjects=" + st_maxobjects + + "\n st_nmodes=" + st_nmodes + + "\n st_nlocks=" + st_nlocks + + "\n st_maxnlocks=" + st_maxnlocks + + "\n st_nlockers=" + st_nlockers + + "\n st_maxnlockers=" + st_maxnlockers + + "\n st_nobjects=" + st_nobjects + + "\n st_maxnobjects=" + st_maxnobjects + + "\n st_nconflicts=" + st_nconflicts + + "\n st_nrequests=" + st_nrequests + + "\n st_nreleases=" + st_nreleases + + "\n st_nnowaits=" + st_nnowaits + + "\n st_ndeadlocks=" + st_ndeadlocks + + "\n st_locktimeout=" + st_locktimeout + + "\n st_nlocktimeouts=" + st_nlocktimeouts + + "\n st_txntimeout=" + st_txntimeout + + "\n st_ntxntimeouts=" + st_ntxntimeouts + + "\n st_region_wait=" + st_region_wait + + "\n st_region_nowait=" + st_region_nowait + + "\n st_regsize=" + st_regsize + ; + } +} diff --git a/db/java/src/com/sleepycat/db/LogCursor.java b/db/java/src/com/sleepycat/db/LogCursor.java new file mode 100644 index 000000000..e15576ed9 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LogCursor.java @@ -0,0 +1,80 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: LogCursor.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbLogc; + +public class LogCursor { + protected DbLogc logc; + + protected LogCursor(final DbLogc logc) { + this.logc = logc; + } + + /* package */ + static LogCursor wrap(DbLogc logc) { + return (logc == null) ? null : new LogCursor(logc); + } + + public synchronized void close() + throws DatabaseException { + + logc.close(0); + } + + public OperationStatus getCurrent(final LogSequenceNumber lsn, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + logc.get(lsn, data, DbConstants.DB_CURRENT)); + } + + public OperationStatus getNext(final LogSequenceNumber lsn, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + logc.get(lsn, data, DbConstants.DB_NEXT)); + } + + public OperationStatus getFirst(final LogSequenceNumber lsn, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + logc.get(lsn, data, DbConstants.DB_FIRST)); + } + + public OperationStatus getLast(final LogSequenceNumber lsn, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + logc.get(lsn, data, DbConstants.DB_LAST)); + } + + public OperationStatus getPrev(final LogSequenceNumber lsn, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + logc.get(lsn, data, DbConstants.DB_PREV)); + } + + public OperationStatus set(final LogSequenceNumber lsn, + final DatabaseEntry data) + throws DatabaseException { + + return OperationStatus.fromInt( + logc.get(lsn, data, DbConstants.DB_SET)); + } +} diff --git a/db/java/src/com/sleepycat/db/LogRecordHandler.java b/db/java/src/com/sleepycat/db/LogRecordHandler.java new file mode 100644 index 000000000..db4d6368c --- /dev/null +++ b/db/java/src/com/sleepycat/db/LogRecordHandler.java @@ -0,0 +1,17 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: LogRecordHandler.java,v 1.2 2004/04/21 01:09:09 mjc Exp $ + */ +package com.sleepycat.db; + +public interface LogRecordHandler { + int handleLogRecord(Environment dbenv, + DatabaseEntry logRecord, + LogSequenceNumber lsn, + RecoveryOperation operation); +} diff --git a/db/java/src/com/sleepycat/db/LogSequenceNumber.java b/db/java/src/com/sleepycat/db/LogSequenceNumber.java new file mode 100644 index 000000000..7d1d09313 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LogSequenceNumber.java @@ -0,0 +1,38 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: LogSequenceNumber.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbEnv; + +public class LogSequenceNumber { + private int file; + private int offset; + + public LogSequenceNumber(final int file, final int offset) { + this.file = file; + this.offset = offset; + } + + public LogSequenceNumber() { + this(0, 0); + } + + public int getFile() { + return file; + } + + public int getOffset() { + return offset; + } + + public static int compare(LogSequenceNumber lsn1, LogSequenceNumber lsn2) { + return DbEnv.log_compare(lsn1, lsn2); + } +} diff --git a/db/java/src/com/sleepycat/db/LogStats.java b/db/java/src/com/sleepycat/db/LogStats.java new file mode 100644 index 000000000..aacf1e8f0 --- /dev/null +++ b/db/java/src/com/sleepycat/db/LogStats.java @@ -0,0 +1,146 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class LogStats { + // no public constructor + protected LogStats() {} + + private int st_magic; + public int getMagic() { + return st_magic; + } + + private int st_version; + public int getVersion() { + return st_version; + } + + private int st_mode; + public int getMode() { + return st_mode; + } + + private int st_lg_bsize; + public int getLgBSize() { + return st_lg_bsize; + } + + private int st_lg_size; + public int getLgSize() { + return st_lg_size; + } + + private int st_w_bytes; + public int getWBytes() { + return st_w_bytes; + } + + private int st_w_mbytes; + public int getWMbytes() { + return st_w_mbytes; + } + + private int st_wc_bytes; + public int getWcBytes() { + return st_wc_bytes; + } + + private int st_wc_mbytes; + public int getWcMbytes() { + return st_wc_mbytes; + } + + private int st_wcount; + public int getWCount() { + return st_wcount; + } + + private int st_wcount_fill; + public int getWCountFill() { + return st_wcount_fill; + } + + private int st_scount; + public int getSCount() { + return st_scount; + } + + private int st_region_wait; + public int getRegionWait() { + return st_region_wait; + } + + private int st_region_nowait; + public int getRegionNowait() { + return st_region_nowait; + } + + private int st_cur_file; + public int getCurFile() { + return st_cur_file; + } + + private int st_cur_offset; + public int getCurOffset() { + return st_cur_offset; + } + + private int st_disk_file; + public int getDiskFile() { + return st_disk_file; + } + + private int st_disk_offset; + public int getDiskOffset() { + return st_disk_offset; + } + + private int st_regsize; + public int getRegSize() { + return st_regsize; + } + + private int st_maxcommitperflush; + public int getMaxCommitperflush() { + return st_maxcommitperflush; + } + + private int st_mincommitperflush; + public int getMinCommitperflush() { + return st_mincommitperflush; + } + + public String toString() { + return "LogStats:" + + "\n st_magic=" + st_magic + + "\n st_version=" + st_version + + "\n st_mode=" + st_mode + + "\n st_lg_bsize=" + st_lg_bsize + + "\n st_lg_size=" + st_lg_size + + "\n st_w_bytes=" + st_w_bytes + + "\n st_w_mbytes=" + st_w_mbytes + + "\n st_wc_bytes=" + st_wc_bytes + + "\n st_wc_mbytes=" + st_wc_mbytes + + "\n st_wcount=" + st_wcount + + "\n st_wcount_fill=" + st_wcount_fill + + "\n st_scount=" + st_scount + + "\n st_region_wait=" + st_region_wait + + "\n st_region_nowait=" + st_region_nowait + + "\n st_cur_file=" + st_cur_file + + "\n st_cur_offset=" + st_cur_offset + + "\n st_disk_file=" + st_disk_file + + "\n st_disk_offset=" + st_disk_offset + + "\n st_regsize=" + st_regsize + + "\n st_maxcommitperflush=" + st_maxcommitperflush + + "\n st_mincommitperflush=" + st_mincommitperflush + ; + } +} diff --git a/db/java/src/com/sleepycat/db/MemoryException.java b/db/java/src/com/sleepycat/db/MemoryException.java new file mode 100644 index 000000000..7c612494f --- /dev/null +++ b/db/java/src/com/sleepycat/db/MemoryException.java @@ -0,0 +1,41 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: MemoryException.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbEnv; + +public class MemoryException extends DatabaseException { + private DatabaseEntry dbt = null; + private String message; + + protected MemoryException(final String s, + final DatabaseEntry dbt, + final int errno, + final DbEnv dbenv) { + super(s, errno, dbenv); + this.message = s; + this.dbt = dbt; + } + + public DatabaseEntry getDatabaseEntry() { + return dbt; + } + + public String toString() { + return message; + } + + void updateDatabaseEntry(final DatabaseEntry newEntry) { + if (this.dbt == null) { + this.message = "DatabaseEntry not large enough for available data"; + this.dbt = newEntry; + } + } +} diff --git a/db/java/src/com/sleepycat/db/MessageHandler.java b/db/java/src/com/sleepycat/db/MessageHandler.java new file mode 100644 index 000000000..a28e35820 --- /dev/null +++ b/db/java/src/com/sleepycat/db/MessageHandler.java @@ -0,0 +1,14 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: MessageHandler.java,v 1.2 2004/04/20 20:45:11 mjc Exp $ + */ +package com.sleepycat.db; + +public interface MessageHandler { + void message(Environment dbenv, String message); +} diff --git a/db/java/src/com/sleepycat/db/MultipleDataEntry.java b/db/java/src/com/sleepycat/db/MultipleDataEntry.java new file mode 100644 index 000000000..17c2af473 --- /dev/null +++ b/db/java/src/com/sleepycat/db/MultipleDataEntry.java @@ -0,0 +1,57 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: MultipleDataEntry.java,v 1.2 2004/04/09 15:08:38 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbUtil; + +public class MultipleDataEntry extends MultipleEntry { + public MultipleDataEntry() { + super(null, 0, 0); + } + + public MultipleDataEntry(final byte[] data) { + super(data, 0, (data == null) ? 0 : data.length); + } + + public MultipleDataEntry(final byte[] data, + final int offset, + final int size) { + super(data, offset, size); + } + + /* package */ + int getMultiFlag() { + return DbConstants.DB_MULTIPLE; + } + + public boolean next(final DatabaseEntry data) { + if (pos == 0) + pos = ulen - INT32SZ; + + final int dataoff = DbUtil.array2int(this.data, pos); + + // crack out the data offset and length. + if (dataoff < 0) { + return (false); + } + + pos -= INT32SZ; + final int datasz = DbUtil.array2int(this.data, pos); + + pos -= INT32SZ; + + data.setData(this.data); + data.setSize(datasz); + data.setOffset(dataoff); + + return (true); + } +} diff --git a/db/java/src/com/sleepycat/db/MultipleEntry.java b/db/java/src/com/sleepycat/db/MultipleEntry.java new file mode 100644 index 000000000..f3025a5e5 --- /dev/null +++ b/db/java/src/com/sleepycat/db/MultipleEntry.java @@ -0,0 +1,28 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: MultipleEntry.java,v 1.4 2004/09/28 19:30:37 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public abstract class MultipleEntry extends DatabaseEntry { + protected int pos; + + protected MultipleEntry(final byte[] data, final int offset, final int size) { + super(data, offset, size); + setUserBuffer(data.length - offset, true); + this.flags |= DbConstants.DB_DBT_USERMEM; + } + + public void setUserBuffer(final int length, final boolean usermem) { + if (!usermem) + throw new IllegalArgumentException("User buffer required"); + super.setUserBuffer(length, usermem); + } +} diff --git a/db/java/src/com/sleepycat/db/MultipleKeyDataEntry.java b/db/java/src/com/sleepycat/db/MultipleKeyDataEntry.java new file mode 100644 index 000000000..17234b640 --- /dev/null +++ b/db/java/src/com/sleepycat/db/MultipleKeyDataEntry.java @@ -0,0 +1,63 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: MultipleKeyDataEntry.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbUtil; + +public class MultipleKeyDataEntry extends MultipleEntry { + public MultipleKeyDataEntry() { + super(null, 0, 0); + } + + public MultipleKeyDataEntry(final byte[] data) { + super(data, 0, (data == null) ? 0 : data.length); + } + + public MultipleKeyDataEntry(final byte[] data, + final int offset, + final int size) { + super(data, offset, size); + } + + /* package */ + int getMultiFlag() { + return DbConstants.DB_MULTIPLE_KEY; + } + + public boolean next(final DatabaseEntry key, final DatabaseEntry data) { + if (pos == 0) + pos = ulen - INT32SZ; + + final int keyoff = DbUtil.array2int(this.data, pos); + + // crack out the key and data offsets and lengths. + if (keyoff < 0) + return false; + + pos -= INT32SZ; + final int keysz = DbUtil.array2int(this.data, pos); + pos -= INT32SZ; + final int dataoff = DbUtil.array2int(this.data, pos); + pos -= INT32SZ; + final int datasz = DbUtil.array2int(this.data, pos); + pos -= INT32SZ; + + key.setData(this.data); + key.setOffset(keyoff); + key.setSize(keysz); + + data.setData(this.data); + data.setOffset(dataoff); + data.setSize(datasz); + + return true; + } +} diff --git a/db/java/src/com/sleepycat/db/MultipleRecnoDataEntry.java b/db/java/src/com/sleepycat/db/MultipleRecnoDataEntry.java new file mode 100644 index 000000000..016c671d0 --- /dev/null +++ b/db/java/src/com/sleepycat/db/MultipleRecnoDataEntry.java @@ -0,0 +1,61 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: MultipleRecnoDataEntry.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbUtil; + +public class MultipleRecnoDataEntry extends MultipleEntry { + public MultipleRecnoDataEntry() { + super(null, 0, 0); + } + + public MultipleRecnoDataEntry(final byte[] data) { + super(data, 0, (data == null) ? 0 : data.length); + } + + public MultipleRecnoDataEntry(final byte[] data, + final int offset, + final int size) { + super(data, offset, size); + } + + /* package */ + int getMultiFlag() { + return DbConstants.DB_MULTIPLE_KEY; + } + + public boolean next(final DatabaseEntry recno, final DatabaseEntry data) { + if (pos == 0) + pos = ulen - INT32SZ; + + final int keyoff = DbUtil.array2int(this.data, pos); + + // crack out the key offset and the data offset and length. + if (keyoff < 0) + return false; + + pos -= INT32SZ; + final int dataoff = DbUtil.array2int(this.data, pos); + pos -= INT32SZ; + final int datasz = DbUtil.array2int(this.data, pos); + pos -= INT32SZ; + + recno.setData(this.data); + recno.setOffset(keyoff); + recno.setSize(INT32SZ); + + data.setData(this.data); + data.setOffset(dataoff); + data.setSize(datasz); + + return true; + } +} diff --git a/db/java/src/com/sleepycat/db/OperationStatus.java b/db/java/src/com/sleepycat/db/OperationStatus.java new file mode 100644 index 000000000..5739bc6e2 --- /dev/null +++ b/db/java/src/com/sleepycat/db/OperationStatus.java @@ -0,0 +1,54 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: OperationStatus.java,v 1.2 2004/04/21 01:09:09 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbEnv; + +public final class OperationStatus { + public static final OperationStatus SUCCESS = + new OperationStatus("SUCCESS", 0); + public static final OperationStatus KEYEXIST = + new OperationStatus("KEYEXIST", DbConstants.DB_KEYEXIST); + public static final OperationStatus KEYEMPTY = + new OperationStatus("KEYEMPTY", DbConstants.DB_KEYEMPTY); + public static final OperationStatus NOTFOUND = + new OperationStatus("NOTFOUND", DbConstants.DB_NOTFOUND); + + /* package */ + static OperationStatus fromInt(final int errCode) { + switch(errCode) { + case 0: + return SUCCESS; + case DbConstants.DB_KEYEXIST: + return KEYEXIST; + case DbConstants.DB_KEYEMPTY: + return KEYEMPTY; + case DbConstants.DB_NOTFOUND: + return NOTFOUND; + default: + throw new IllegalArgumentException( + "Unknown error code: " + DbEnv.strerror(errCode)); + } + } + + /* For toString */ + private String statusName; + private int errCode; + + private OperationStatus(final String statusName, int errCode) { + this.statusName = statusName; + this.errCode = errCode; + } + + public String toString() { + return "OperationStatus." + statusName; + } +} diff --git a/db/java/src/com/sleepycat/db/PanicHandler.java b/db/java/src/com/sleepycat/db/PanicHandler.java new file mode 100644 index 000000000..7c9b838a8 --- /dev/null +++ b/db/java/src/com/sleepycat/db/PanicHandler.java @@ -0,0 +1,14 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: PanicHandler.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db; + +public interface PanicHandler { + void panic(Environment dbenv, DatabaseException e); +} diff --git a/db/java/src/com/sleepycat/db/PreparedTransaction.java b/db/java/src/com/sleepycat/db/PreparedTransaction.java new file mode 100644 index 000000000..36d103134 --- /dev/null +++ b/db/java/src/com/sleepycat/db/PreparedTransaction.java @@ -0,0 +1,30 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1999-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: PreparedTransaction.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbTxn; + +public class PreparedTransaction { + private byte[] gid; + private Transaction txn; + + PreparedTransaction(final DbTxn txn, final byte[] gid) { + this.txn = new Transaction(txn); + this.gid = gid; + } + + public byte[] getGID() { + return gid; + } + + public Transaction getTransaction() { + return txn; + } +} diff --git a/db/java/src/com/sleepycat/db/QueueStats.java b/db/java/src/com/sleepycat/db/QueueStats.java new file mode 100644 index 000000000..10ad3f768 --- /dev/null +++ b/db/java/src/com/sleepycat/db/QueueStats.java @@ -0,0 +1,98 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class QueueStats extends DatabaseStats { + // no public constructor + protected QueueStats() {} + + private int qs_magic; + public int getMagic() { + return qs_magic; + } + + private int qs_version; + public int getVersion() { + return qs_version; + } + + private int qs_metaflags; + public int getMetaFlags() { + return qs_metaflags; + } + + private int qs_nkeys; + public int getNumKeys() { + return qs_nkeys; + } + + private int qs_ndata; + public int getNumData() { + return qs_ndata; + } + + private int qs_pagesize; + public int getPageSize() { + return qs_pagesize; + } + + private int qs_extentsize; + public int getExtentSize() { + return qs_extentsize; + } + + private int qs_pages; + public int getPages() { + return qs_pages; + } + + private int qs_re_len; + public int getReLen() { + return qs_re_len; + } + + private int qs_re_pad; + public int getRePad() { + return qs_re_pad; + } + + private int qs_pgfree; + public int getPagesFree() { + return qs_pgfree; + } + + private int qs_first_recno; + public int getFirstRecno() { + return qs_first_recno; + } + + private int qs_cur_recno; + public int getCurRecno() { + return qs_cur_recno; + } + + public String toString() { + return "QueueStats:" + + "\n qs_magic=" + qs_magic + + "\n qs_version=" + qs_version + + "\n qs_metaflags=" + qs_metaflags + + "\n qs_nkeys=" + qs_nkeys + + "\n qs_ndata=" + qs_ndata + + "\n qs_pagesize=" + qs_pagesize + + "\n qs_extentsize=" + qs_extentsize + + "\n qs_pages=" + qs_pages + + "\n qs_re_len=" + qs_re_len + + "\n qs_re_pad=" + qs_re_pad + + "\n qs_pgfree=" + qs_pgfree + + "\n qs_first_recno=" + qs_first_recno + + "\n qs_cur_recno=" + qs_cur_recno + ; + } +} diff --git a/db/java/src/com/sleepycat/db/RecordNumberAppender.java b/db/java/src/com/sleepycat/db/RecordNumberAppender.java new file mode 100644 index 000000000..4162a1ead --- /dev/null +++ b/db/java/src/com/sleepycat/db/RecordNumberAppender.java @@ -0,0 +1,15 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: RecordNumberAppender.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db; + +public interface RecordNumberAppender { + void appendRecordNumber(Database db, DatabaseEntry data, int recno) + throws DatabaseException; +} diff --git a/db/java/src/com/sleepycat/db/RecoveryOperation.java b/db/java/src/com/sleepycat/db/RecoveryOperation.java new file mode 100644 index 000000000..938eacd2e --- /dev/null +++ b/db/java/src/com/sleepycat/db/RecoveryOperation.java @@ -0,0 +1,56 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: RecoveryOperation.java,v 1.1 2004/04/21 01:09:09 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public final class RecoveryOperation { + public static final RecoveryOperation BACKWARD_ROLL = + new RecoveryOperation("BACKWARD_ROLL", DbConstants.DB_TXN_BACKWARD_ROLL); + public static final RecoveryOperation FORWARD_ROLL = + new RecoveryOperation("FORWARD_ROLL", DbConstants.DB_TXN_FORWARD_ROLL); + public static final RecoveryOperation ABORT = + new RecoveryOperation("ABORT", DbConstants.DB_TXN_ABORT); + public static final RecoveryOperation APPLY = + new RecoveryOperation("APPLY", DbConstants.DB_TXN_APPLY); + public static final RecoveryOperation PRINT = + new RecoveryOperation("PRINT", DbConstants.DB_TXN_PRINT); + + private String operationName; + private int flag; + + private RecoveryOperation(String operationName, int flag) { + this.operationName = operationName; + this.flag = flag; + } + + public String toString() { + return "RecoveryOperation." + operationName; + } + + /* This is public only so it can be called from internal/DbEnv.java. */ + public static RecoveryOperation fromFlag(int flag) { + switch (flag) { + case DbConstants.DB_TXN_BACKWARD_ROLL: + return BACKWARD_ROLL; + case DbConstants.DB_TXN_FORWARD_ROLL: + return FORWARD_ROLL; + case DbConstants.DB_TXN_ABORT: + return ABORT; + case DbConstants.DB_TXN_APPLY: + return APPLY; + case DbConstants.DB_TXN_PRINT: + return PRINT; + default: + throw new IllegalArgumentException( + "Unknown recover operation: " + flag); + } + } +} diff --git a/db/java/src/com/sleepycat/db/ReplicationHandleDeadException.java b/db/java/src/com/sleepycat/db/ReplicationHandleDeadException.java new file mode 100644 index 000000000..cfcf92ab6 --- /dev/null +++ b/db/java/src/com/sleepycat/db/ReplicationHandleDeadException.java @@ -0,0 +1,20 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ReplicationHandleDeadException.java,v 1.1 2004/09/23 17:56:39 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbEnv; + +public class ReplicationHandleDeadException extends DatabaseException { + protected ReplicationHandleDeadException(final String s, + final int errno, + final DbEnv dbenv) { + super(s, errno, dbenv); + } +} diff --git a/db/java/src/com/sleepycat/db/ReplicationStats.java b/db/java/src/com/sleepycat/db/ReplicationStats.java new file mode 100644 index 000000000..70c2bf032 --- /dev/null +++ b/db/java/src/com/sleepycat/db/ReplicationStats.java @@ -0,0 +1,278 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class ReplicationStats { + // no public constructor + protected ReplicationStats() {} + + private int st_status; + public int getStatus() { + return st_status; + } + + private LogSequenceNumber st_next_lsn; + public LogSequenceNumber getNextLsn() { + return st_next_lsn; + } + + private LogSequenceNumber st_waiting_lsn; + public LogSequenceNumber getWaitingLsn() { + return st_waiting_lsn; + } + + private int st_next_pg; + public int getNextPages() { + return st_next_pg; + } + + private int st_waiting_pg; + public int getWaitingPages() { + return st_waiting_pg; + } + + private int st_dupmasters; + public int getDupmasters() { + return st_dupmasters; + } + + private int st_env_id; + public int getEnvId() { + return st_env_id; + } + + private int st_env_priority; + public int getEnvPriority() { + return st_env_priority; + } + + private int st_gen; + public int getGen() { + return st_gen; + } + + private int st_egen; + public int getEgen() { + return st_egen; + } + + private int st_log_duplicated; + public int getLogDuplicated() { + return st_log_duplicated; + } + + private int st_log_queued; + public int getLogQueued() { + return st_log_queued; + } + + private int st_log_queued_max; + public int getLogQueuedMax() { + return st_log_queued_max; + } + + private int st_log_queued_total; + public int getLogQueuedTotal() { + return st_log_queued_total; + } + + private int st_log_records; + public int getLogRecords() { + return st_log_records; + } + + private int st_log_requested; + public int getLogRequested() { + return st_log_requested; + } + + private int st_master; + public int getMaster() { + return st_master; + } + + private int st_master_changes; + public int getMasterChanges() { + return st_master_changes; + } + + private int st_msgs_badgen; + public int getMsgsBadgen() { + return st_msgs_badgen; + } + + private int st_msgs_processed; + public int getMsgsProcessed() { + return st_msgs_processed; + } + + private int st_msgs_recover; + public int getMsgsRecover() { + return st_msgs_recover; + } + + private int st_msgs_send_failures; + public int getMsgsSendFailures() { + return st_msgs_send_failures; + } + + private int st_msgs_sent; + public int getMsgsSent() { + return st_msgs_sent; + } + + private int st_newsites; + public int getNewsites() { + return st_newsites; + } + + private int st_nsites; + public int getNumSites() { + return st_nsites; + } + + private int st_nthrottles; + public int getNumThrottles() { + return st_nthrottles; + } + + private int st_outdated; + public int getOutdated() { + return st_outdated; + } + + private int st_pg_duplicated; + public int getPagesDuplicated() { + return st_pg_duplicated; + } + + private int st_pg_records; + public int getPagesRecords() { + return st_pg_records; + } + + private int st_pg_requested; + public int getPagesRequested() { + return st_pg_requested; + } + + private int st_startup_complete; + public int getStartupComplete() { + return st_startup_complete; + } + + private int st_txns_applied; + public int getTxnsApplied() { + return st_txns_applied; + } + + private int st_elections; + public int getElections() { + return st_elections; + } + + private int st_elections_won; + public int getElectionsWon() { + return st_elections_won; + } + + private int st_election_cur_winner; + public int getElectionCurWinner() { + return st_election_cur_winner; + } + + private int st_election_gen; + public int getElectionGen() { + return st_election_gen; + } + + private LogSequenceNumber st_election_lsn; + public LogSequenceNumber getElectionLsn() { + return st_election_lsn; + } + + private int st_election_nsites; + public int getElectionNumSites() { + return st_election_nsites; + } + + private int st_election_nvotes; + public int getElectionNumVotes() { + return st_election_nvotes; + } + + private int st_election_priority; + public int getElectionPriority() { + return st_election_priority; + } + + private int st_election_status; + public int getElectionStatus() { + return st_election_status; + } + + private int st_election_tiebreaker; + public int getElectionTiebreaker() { + return st_election_tiebreaker; + } + + private int st_election_votes; + public int getElectionVotes() { + return st_election_votes; + } + + public String toString() { + return "ReplicationStats:" + + "\n st_status=" + st_status + + "\n st_next_lsn=" + st_next_lsn + + "\n st_waiting_lsn=" + st_waiting_lsn + + "\n st_next_pg=" + st_next_pg + + "\n st_waiting_pg=" + st_waiting_pg + + "\n st_dupmasters=" + st_dupmasters + + "\n st_env_id=" + st_env_id + + "\n st_env_priority=" + st_env_priority + + "\n st_gen=" + st_gen + + "\n st_egen=" + st_egen + + "\n st_log_duplicated=" + st_log_duplicated + + "\n st_log_queued=" + st_log_queued + + "\n st_log_queued_max=" + st_log_queued_max + + "\n st_log_queued_total=" + st_log_queued_total + + "\n st_log_records=" + st_log_records + + "\n st_log_requested=" + st_log_requested + + "\n st_master=" + st_master + + "\n st_master_changes=" + st_master_changes + + "\n st_msgs_badgen=" + st_msgs_badgen + + "\n st_msgs_processed=" + st_msgs_processed + + "\n st_msgs_recover=" + st_msgs_recover + + "\n st_msgs_send_failures=" + st_msgs_send_failures + + "\n st_msgs_sent=" + st_msgs_sent + + "\n st_newsites=" + st_newsites + + "\n st_nsites=" + st_nsites + + "\n st_nthrottles=" + st_nthrottles + + "\n st_outdated=" + st_outdated + + "\n st_pg_duplicated=" + st_pg_duplicated + + "\n st_pg_records=" + st_pg_records + + "\n st_pg_requested=" + st_pg_requested + + "\n st_startup_complete=" + st_startup_complete + + "\n st_txns_applied=" + st_txns_applied + + "\n st_elections=" + st_elections + + "\n st_elections_won=" + st_elections_won + + "\n st_election_cur_winner=" + st_election_cur_winner + + "\n st_election_gen=" + st_election_gen + + "\n st_election_lsn=" + st_election_lsn + + "\n st_election_nsites=" + st_election_nsites + + "\n st_election_nvotes=" + st_election_nvotes + + "\n st_election_priority=" + st_election_priority + + "\n st_election_status=" + st_election_status + + "\n st_election_tiebreaker=" + st_election_tiebreaker + + "\n st_election_votes=" + st_election_votes + ; + } +} diff --git a/db/java/src/com/sleepycat/db/ReplicationStatus.java b/db/java/src/com/sleepycat/db/ReplicationStatus.java new file mode 100644 index 000000000..64e4fd2c1 --- /dev/null +++ b/db/java/src/com/sleepycat/db/ReplicationStatus.java @@ -0,0 +1,121 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: ReplicationStatus.java,v 1.5 2004/08/17 20:04:42 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbEnv; + +public final class ReplicationStatus { + static final ReplicationStatus SUCCESS = + new ReplicationStatus("SUCCESS", 0); + + private int errCode; + private DatabaseEntry cdata; + private int envid; + private LogSequenceNumber lsn; + + /* For toString */ + private String statusName; + + private ReplicationStatus(final String statusName, + final int errCode, + final DatabaseEntry cdata, + final int envid, + final LogSequenceNumber lsn) { + this.statusName = statusName; + this.errCode = errCode; + this.cdata = cdata; + this.envid = envid; + this.lsn = lsn; + } + + private ReplicationStatus(final String statusName, final int errCode) { + this(statusName, errCode, null, 0, null); + } + + public boolean isSuccess() { + return errCode == 0; + } + + public boolean isDupMaster() { + return errCode == DbConstants.DB_REP_DUPMASTER; + } + + public boolean isHoldElection() { + return errCode == DbConstants.DB_REP_HOLDELECTION; + } + + public boolean isPermanent() { + return errCode == DbConstants.DB_REP_ISPERM; + } + + public boolean isNewMaster() { + return errCode == DbConstants.DB_REP_NEWMASTER; + } + + public boolean isNewSite() { + return errCode == DbConstants.DB_REP_NEWSITE; + } + + public boolean isNotPermanent() { + return errCode == DbConstants.DB_REP_NOTPERM; + } + + public boolean isStartupDone() { + return errCode == DbConstants.DB_REP_STARTUPDONE; + } + + public DatabaseEntry getCData() { + return cdata; + } + + public int getEnvID() { + return envid; + } + + public LogSequenceNumber getLSN() { + return lsn; + } + + public String toString() { + return "ReplicationStatus." + statusName; + } + + /* package */ + static ReplicationStatus getStatus(final int errCode, + final DatabaseEntry cdata, + final int envid, + final LogSequenceNumber lsn) { + switch(errCode) { + case 0: + return SUCCESS; + case DbConstants.DB_REP_DUPMASTER: + return DUPMASTER; + case DbConstants.DB_REP_HOLDELECTION: + return HOLDELECTION; + case DbConstants.DB_REP_ISPERM: + return new ReplicationStatus("ISPERM", errCode, cdata, envid, lsn); + case DbConstants.DB_REP_NEWMASTER: + return new ReplicationStatus("NEWMASTER", errCode, cdata, envid, lsn); + case DbConstants.DB_REP_NEWSITE: + return new ReplicationStatus("NEWSITE", errCode, cdata, envid, lsn); + case DbConstants.DB_REP_NOTPERM: + return new ReplicationStatus("NOTPERM", errCode, cdata, envid, lsn); + default: + throw new IllegalArgumentException( + "Unknown error code: " + DbEnv.strerror(errCode)); + } + } + + private static final ReplicationStatus DUPMASTER = + new ReplicationStatus("DUPMASTER", DbConstants.DB_REP_DUPMASTER); + private static final ReplicationStatus HOLDELECTION = + new ReplicationStatus("HOLDELECTION", DbConstants.DB_REP_HOLDELECTION); +} diff --git a/db/java/src/com/sleepycat/db/ReplicationTransport.java b/db/java/src/com/sleepycat/db/ReplicationTransport.java new file mode 100644 index 000000000..72b28a73c --- /dev/null +++ b/db/java/src/com/sleepycat/db/ReplicationTransport.java @@ -0,0 +1,26 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ReplicationTransport.java,v 1.3 2004/07/06 15:06:37 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public interface ReplicationTransport { + int send(Environment dbenv, + DatabaseEntry control, + DatabaseEntry rec, + LogSequenceNumber lsn, + int envid, + boolean noBuffer, + boolean permanent) + throws DatabaseException; + + int EID_BROADCAST = DbConstants.DB_EID_BROADCAST; + int EID_INVALID = DbConstants.DB_EID_INVALID; +} diff --git a/db/java/src/com/sleepycat/db/RunRecoveryException.java b/db/java/src/com/sleepycat/db/RunRecoveryException.java new file mode 100644 index 000000000..640d81af8 --- /dev/null +++ b/db/java/src/com/sleepycat/db/RunRecoveryException.java @@ -0,0 +1,20 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 1997-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: RunRecoveryException.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbEnv; + +public class RunRecoveryException extends DatabaseException { + protected RunRecoveryException(final String s, + final int errno, + final DbEnv dbenv) { + super(s, errno, dbenv); + } +} diff --git a/db/java/src/com/sleepycat/db/SecondaryConfig.java b/db/java/src/com/sleepycat/db/SecondaryConfig.java new file mode 100644 index 000000000..e275e19e5 --- /dev/null +++ b/db/java/src/com/sleepycat/db/SecondaryConfig.java @@ -0,0 +1,91 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: SecondaryConfig.java,v 1.3 2004/08/06 21:56:40 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.Db; +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbEnv; +import com.sleepycat.db.internal.DbTxn; + +public class SecondaryConfig extends DatabaseConfig implements Cloneable { + /* + * For internal use, to allow null as a valid value for + * the config parameter. + */ + public static final SecondaryConfig DEFAULT = new SecondaryConfig(); + + /* package */ + static SecondaryConfig checkNull(SecondaryConfig config) { + return (config == null) ? DEFAULT : config; + } + + private boolean allowPopulate; + private SecondaryKeyCreator keyCreator; + + public SecondaryConfig() { + } + + public void setKeyCreator(final SecondaryKeyCreator keyCreator) { + this.keyCreator = keyCreator; + } + + public SecondaryKeyCreator getKeyCreator() { + return keyCreator; + } + + public void setAllowPopulate(final boolean allowPopulate) { + this.allowPopulate = allowPopulate; + } + + public boolean getAllowPopulate() { + return allowPopulate; + } + + /* package */ + Db openSecondaryDatabase(final DbEnv dbenv, + final DbTxn txn, + final String fileName, + final String databaseName, + final Db primary) + throws DatabaseException, java.io.FileNotFoundException { + + int associateFlags = 0; + associateFlags |= allowPopulate ? DbConstants.DB_CREATE : 0; + if (getTransactional() && txn == null) + associateFlags |= DbConstants.DB_AUTO_COMMIT; + + final Db db = super.openDatabase(dbenv, txn, fileName, databaseName); + boolean succeeded = false; + try { + primary.associate(txn, db, keyCreator, associateFlags); + succeeded = true; + return db; + } finally { + if (!succeeded) + try { + db.close(0); + } catch (Throwable t) { + // Ignore it -- there is already an exception in flight. + } + } + } + + /* package */ + SecondaryConfig(final Db db) + throws DatabaseException { + + super(db); + + // XXX: There is no way to find out whether allowPopulate was set. + allowPopulate = false; + keyCreator = db.get_seckey_create(); + } +} + diff --git a/db/java/src/com/sleepycat/db/SecondaryCursor.java b/db/java/src/com/sleepycat/db/SecondaryCursor.java new file mode 100644 index 000000000..886ea5f92 --- /dev/null +++ b/db/java/src/com/sleepycat/db/SecondaryCursor.java @@ -0,0 +1,250 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: SecondaryCursor.java,v 1.3 2004/04/21 01:09:09 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.Dbc; + +public class SecondaryCursor extends Cursor { + /* package */ + SecondaryCursor(final SecondaryDatabase database, + final Dbc dbc, + final CursorConfig config) + throws DatabaseException { + + super(database, dbc, config); + } + + public SecondaryDatabase getSecondaryDatabase() { + return (SecondaryDatabase)super.getDatabase(); + } + + public Cursor dup(final boolean samePosition) + throws DatabaseException { + + return dupSecondary(samePosition); + } + + public SecondaryCursor dupSecondary(final boolean samePosition) + throws DatabaseException { + + return new SecondaryCursor(getSecondaryDatabase(), + dbc.dup(samePosition ? DbConstants.DB_POSITION : 0), config); + } + + public OperationStatus getCurrent(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_CURRENT | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getFirst(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_FIRST | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getLast(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_LAST | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getNext(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_NEXT | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getNextDup(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_NEXT_DUP | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getNextNoDup(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_NEXT_NODUP | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getPrev(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_PREV | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getPrevDup(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + /* + * "Get the previous duplicate" isn't directly supported by the C API, + * so here's how to get it: dup the cursor and call getPrev, then dup + * the result and call getNextDup. If both succeed then there was a + * previous duplicate and the first dup is sitting on it. Keep that, + * and call getCurrent to fill in the user's buffers. + */ + Dbc dup1 = dbc.dup(DbConstants.DB_POSITION); + try { + int errCode = dup1.get(DatabaseEntry.IGNORE, DatabaseEntry.IGNORE, + DbConstants.DB_PREV | LockMode.getFlag(lockMode)); + if (errCode == 0) { + Dbc dup2 = dup1.dup(DbConstants.DB_POSITION); + try { + errCode = dup2.get(DatabaseEntry.IGNORE, + DatabaseEntry.IGNORE, + DbConstants.DB_NEXT_DUP | LockMode.getFlag(lockMode)); + } finally { + dup2.close(); + } + } + if (errCode == 0) + errCode = dup1.pget(key, pKey, data, + DbConstants.DB_CURRENT | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag())); + if (errCode == 0) { + Dbc tdbc = dbc; + dbc = dup1; + dup1 = tdbc; + } + return OperationStatus.fromInt(errCode); + } finally { + dup1.close(); + } + } + + public OperationStatus getPrevNoDup(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_PREV_NODUP | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getRecordNumber(final DatabaseEntry secondaryRecno, + final DatabaseEntry primaryRecno, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(DatabaseEntry.IGNORE, secondaryRecno, primaryRecno, + DbConstants.DB_GET_RECNO | LockMode.getFlag(lockMode))); + } + + public OperationStatus getSearchKey(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_SET | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchKeyRange(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_SET_RANGE | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchBoth(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_GET_BOTH | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchBothRange(final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(key, pKey, data, + DbConstants.DB_GET_BOTH_RANGE | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchRecordNumber( + final DatabaseEntry secondaryRecno, + final DatabaseEntry pKey, + final DatabaseEntry data, + LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + dbc.pget(secondaryRecno, pKey, data, + DbConstants.DB_SET_RECNO | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } +} diff --git a/db/java/src/com/sleepycat/db/SecondaryDatabase.java b/db/java/src/com/sleepycat/db/SecondaryDatabase.java new file mode 100644 index 000000000..3ea7c1398 --- /dev/null +++ b/db/java/src/com/sleepycat/db/SecondaryDatabase.java @@ -0,0 +1,106 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: SecondaryDatabase.java,v 1.3 2004/04/21 01:09:10 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.Db; +import com.sleepycat.db.internal.DbConstants; + +public class SecondaryDatabase extends Database { + private final Database primaryDatabase; + + /* package */ + SecondaryDatabase(final Db db, final Database primaryDatabase) + throws DatabaseException { + + super(db); + this.primaryDatabase = primaryDatabase; + } + + public SecondaryDatabase(final String fileName, + final String databaseName, + final Database primaryDatabase, + final SecondaryConfig config) + throws DatabaseException, java.io.FileNotFoundException { + + this(SecondaryConfig.checkNull(config).openSecondaryDatabase( + null, null, fileName, databaseName, primaryDatabase.db), + primaryDatabase); + } + + public Cursor openCursor(final Transaction txn, final CursorConfig config) + throws DatabaseException { + + return openSecondaryCursor(txn, config); + } + + public SecondaryCursor openSecondaryCursor(final Transaction txn, + final CursorConfig config) + throws DatabaseException { + + return new SecondaryCursor(this, + CursorConfig.checkNull(config).openCursor(db, + (txn == null) ? null : txn.txn), config); + } + + public Database getPrimaryDatabase() { + return primaryDatabase; + } + + public DatabaseConfig getConfig() + throws DatabaseException { + + return getSecondaryConfig(); + } + + public SecondaryConfig getSecondaryConfig() + throws DatabaseException { + + return new SecondaryConfig(db); + } + + public OperationStatus get(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + final LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + db.pget((txn == null) ? null : txn.txn, key, pKey, data, + LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchBoth(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + final LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + db.pget((txn == null) ? null : txn.txn, key, pKey, data, + DbConstants.DB_GET_BOTH | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } + + public OperationStatus getSearchRecordNumber(final Transaction txn, + final DatabaseEntry key, + final DatabaseEntry pKey, + final DatabaseEntry data, + final LockMode lockMode) + throws DatabaseException { + + return OperationStatus.fromInt( + db.pget((txn == null) ? null : txn.txn, key, pKey, data, + DbConstants.DB_SET_RECNO | LockMode.getFlag(lockMode) | + ((data == null) ? 0 : data.getMultiFlag()))); + } +} diff --git a/db/java/src/com/sleepycat/db/SecondaryKeyCreator.java b/db/java/src/com/sleepycat/db/SecondaryKeyCreator.java new file mode 100644 index 000000000..10d17c56e --- /dev/null +++ b/db/java/src/com/sleepycat/db/SecondaryKeyCreator.java @@ -0,0 +1,18 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: SecondaryKeyCreator.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ +*/ + +package com.sleepycat.db; + +public interface SecondaryKeyCreator { + boolean createSecondaryKey(SecondaryDatabase secondary, + DatabaseEntry key, + DatabaseEntry data, + DatabaseEntry result) + throws DatabaseException; +} diff --git a/db/java/src/com/sleepycat/db/Sequence.java b/db/java/src/com/sleepycat/db/Sequence.java new file mode 100644 index 000000000..14a6d29dd --- /dev/null +++ b/db/java/src/com/sleepycat/db/Sequence.java @@ -0,0 +1,63 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: Sequence.java,v 1.2 2004/09/28 19:30:37 mjc Exp $ + */ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbSequence; + +public class Sequence { + private DbSequence seq; + private int autoCommitFlag; + + /* package */ + Sequence(final DbSequence seq, SequenceConfig config) + throws DatabaseException { + + this.seq = seq; + seq.wrapper = this; + if (seq.get_db().get_transactional()) + this.autoCommitFlag = DbConstants.DB_AUTO_COMMIT | + (SequenceConfig.checkNull(config).getAutoCommitNoSync() ? + DbConstants.DB_TXN_NOSYNC : 0); + } + + public void close() + throws DatabaseException { + + seq.close(0); + } + + public long get(Transaction txn, int delta) + throws DatabaseException { + + return seq.get((txn == null) ? null : txn.txn, delta, + (txn == null) ? autoCommitFlag : 0); + } + + public Database getDatabase() + throws DatabaseException { + + return seq.get_db().wrapper; + } + + public DatabaseEntry getKey() + throws DatabaseException { + + DatabaseEntry key = new DatabaseEntry(); + seq.get_key(key); + return key; + } + + public SequenceStats getStats(StatsConfig config) + throws DatabaseException { + + return seq.stat(config.getFlags()); + } +} diff --git a/db/java/src/com/sleepycat/db/SequenceConfig.java b/db/java/src/com/sleepycat/db/SequenceConfig.java new file mode 100644 index 000000000..2901ac1e6 --- /dev/null +++ b/db/java/src/com/sleepycat/db/SequenceConfig.java @@ -0,0 +1,199 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: SequenceConfig.java,v 1.2 2004/09/23 17:56:39 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.Db; +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbSequence; +import com.sleepycat.db.internal.DbTxn; + +public class SequenceConfig implements Cloneable { + /* + * For internal use, final to allow null as a valid value for + * the config parameter. + */ + public static final SequenceConfig DEFAULT = new SequenceConfig(); + + /* package */ + static SequenceConfig checkNull(SequenceConfig config) { + return (config == null) ? DEFAULT : config; + } + + /* Parameters */ + private int cacheSize = 0; + private long rangeMin = Long.MIN_VALUE; + private long rangeMax = Long.MAX_VALUE; + private long initialValue = 0L; + + /* Flags */ + private boolean allowCreate = false; + private boolean decrement = false; + private boolean exclusiveCreate = false; + private boolean autoCommitNoSync = false; + private boolean wrap = false; + + public SequenceConfig() { + } + + public void setAllowCreate(final boolean allowCreate) { + this.allowCreate = allowCreate; + } + + public boolean getAllowCreate() { + return allowCreate; + } + + public void setCacheSize(final int cacheSize) { + this.cacheSize = cacheSize; + } + + public int getCacheSize() { + return cacheSize; + } + + public void setDecrement(boolean decrement) { + this.decrement = decrement; + } + + public boolean getDecrement() { + return decrement; + } + + public void setExclusiveCreate(final boolean exclusiveCreate) { + this.exclusiveCreate = exclusiveCreate; + } + + public boolean getExclusiveCreate() { + return exclusiveCreate; + } + + public void setInitialValue(long initialValue) { + this.initialValue = initialValue; + } + + public long getInitialValue() { + return initialValue; + } + + public void setAutoCommitNoSync(final boolean autoCommitNoSync) { + this.autoCommitNoSync = autoCommitNoSync; + } + + public boolean getAutoCommitNoSync() { + return autoCommitNoSync; + } + + public void setRange(final long min, final long max) { + this.rangeMin = min; + this.rangeMax = max; + } + + public long getRangeMin() { + return rangeMin; + } + + public long getRangeMax() { + return rangeMax; + } + + public void setWrap(final boolean wrap) { + this.wrap = wrap; + } + + public boolean getWrap() { + return wrap; + } + + /* package */ + DbSequence createSequence(final Db db) + throws DatabaseException { + + int createFlags = 0; + + return new DbSequence(db, createFlags); + } + + /* package */ + DbSequence openSequence(final Db db, + final DbTxn txn, + final DatabaseEntry key) + throws DatabaseException { + + final DbSequence seq = createSequence(db); + // The DB_THREAD flag is inherited from the database + boolean threaded = ((db.get_open_flags() & DbConstants.DB_THREAD) != 0); + + int openFlags = 0; + openFlags |= allowCreate ? DbConstants.DB_CREATE : 0; + openFlags |= exclusiveCreate ? DbConstants.DB_EXCL : 0; + openFlags |= threaded ? DbConstants.DB_THREAD : 0; + + if (db.get_transactional() && txn == null) + openFlags |= DbConstants.DB_AUTO_COMMIT; + + configureSequence(seq, DEFAULT); + boolean succeeded = false; + try { + seq.open(txn, key, openFlags); + succeeded = true; + return seq; + } finally { + if (!succeeded) + try { + seq.close(0); + } catch (Throwable t) { + // Ignore it -- an exception is already in flight. + } + } + } + + /* package */ + void configureSequence(final DbSequence seq, final SequenceConfig oldConfig) + throws DatabaseException { + + int seqFlags = 0; + seqFlags |= decrement ? DbConstants.DB_SEQ_DEC : DbConstants.DB_SEQ_INC; + seqFlags |= wrap ? DbConstants.DB_SEQ_WRAP : 0; + + if (seqFlags != 0) + seq.set_flags(seqFlags); + + if (rangeMin != oldConfig.rangeMin || rangeMax != oldConfig.rangeMax) + seq.set_range(rangeMin, rangeMax); + + if (initialValue != oldConfig.initialValue) + seq.initial_value(initialValue); + + if (cacheSize != oldConfig.cacheSize) + seq.set_cachesize(cacheSize); + } + + /* package */ + SequenceConfig(final DbSequence seq) + throws DatabaseException { + + // XXX: can't get open flags + final int openFlags = 0; + allowCreate = (openFlags & DbConstants.DB_CREATE) != 0; + exclusiveCreate = (openFlags & DbConstants.DB_EXCL) != 0; + + final int seqFlags = seq.get_flags(); + decrement = (seqFlags & DbConstants.DB_SEQ_DEC) != 0; + wrap = (seqFlags & DbConstants.DB_SEQ_WRAP) != 0; + + // XXX: can't get initial value + final long initialValue = 0; + + cacheSize = seq.get_cachesize(); + + rangeMin = seq.get_range_min(); + rangeMax = seq.get_range_max(); + } +} diff --git a/db/java/src/com/sleepycat/db/SequenceStats.java b/db/java/src/com/sleepycat/db/SequenceStats.java new file mode 100644 index 000000000..5c1e3a868 --- /dev/null +++ b/db/java/src/com/sleepycat/db/SequenceStats.java @@ -0,0 +1,74 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +public class SequenceStats { + // no public constructor + protected SequenceStats() {} + + private int st_wait; + public int getWait() { + return st_wait; + } + + private int st_nowait; + public int getNowait() { + return st_nowait; + } + + private long st_current; + public long getCurrent() { + return st_current; + } + + private long st_value; + public long getValue() { + return st_value; + } + + private long st_last_value; + public long getLastValue() { + return st_last_value; + } + + private long st_min; + public long getMin() { + return st_min; + } + + private long st_max; + public long getMax() { + return st_max; + } + + private int st_cache_size; + public int getCacheSize() { + return st_cache_size; + } + + private int st_flags; + public int getFlags() { + return st_flags; + } + + public String toString() { + return "SequenceStats:" + + "\n st_wait=" + st_wait + + "\n st_nowait=" + st_nowait + + "\n st_current=" + st_current + + "\n st_value=" + st_value + + "\n st_last_value=" + st_last_value + + "\n st_min=" + st_min + + "\n st_max=" + st_max + + "\n st_cache_size=" + st_cache_size + + "\n st_flags=" + st_flags + ; + } +} diff --git a/db/java/src/com/sleepycat/db/StatsConfig.java b/db/java/src/com/sleepycat/db/StatsConfig.java new file mode 100644 index 000000000..400407960 --- /dev/null +++ b/db/java/src/com/sleepycat/db/StatsConfig.java @@ -0,0 +1,56 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: StatsConfig.java,v 1.3 2004/04/21 01:09:10 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public class StatsConfig { + /* + * For internal use, to allow null as a valid value for + * the config parameter. + */ + public static final StatsConfig DEFAULT = new StatsConfig(); + + /* package */ + static StatsConfig checkNull(StatsConfig config) { + return (config == null) ? DEFAULT : config; + } + + private boolean clear = false; + private boolean fast = false; + + public StatsConfig() { + } + + public void setClear(boolean clear) { + this.clear = clear; + } + + public boolean getClear() { + return clear; + } + + public void setFast(boolean fast) { + this.fast = fast; + } + + public boolean getFast() { + return fast; + } + + int getFlags() { + int flags = 0; + if (fast) + flags |= DbConstants.DB_FAST_STAT; + if (clear) + flags |= DbConstants.DB_STAT_CLEAR; + return flags; + } +} diff --git a/db/java/src/com/sleepycat/db/Transaction.java b/db/java/src/com/sleepycat/db/Transaction.java new file mode 100644 index 000000000..7aacc86ed --- /dev/null +++ b/db/java/src/com/sleepycat/db/Transaction.java @@ -0,0 +1,75 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: Transaction.java,v 1.2 2004/04/21 01:09:10 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbTxn; + +public class Transaction { + /*package */ final DbTxn txn; + + Transaction(final DbTxn txn) { + this.txn = txn; + } + + public void abort() + throws DatabaseException { + + txn.abort(); + } + + public void commit() + throws DatabaseException { + + txn.commit(0); + } + + public void commitSync() + throws DatabaseException { + + txn.commit(DbConstants.DB_TXN_SYNC); + } + + public void commitNoSync() + throws DatabaseException { + + txn.commit(DbConstants.DB_TXN_NOSYNC); + } + + public void discard() + throws DatabaseException { + + txn.discard(0); + } + + public int getId() + throws DatabaseException { + + return txn.id(); + } + + public void prepare(final byte[] gid) + throws DatabaseException { + + txn.prepare(gid); + } + + public void setTxnTimeout(final long timeOut) + throws DatabaseException { + + txn.set_timeout(timeOut, DbConstants.DB_SET_TXN_TIMEOUT); + } + + public void setLockTimeout(final long timeOut) + throws DatabaseException { + + txn.set_timeout(timeOut, DbConstants.DB_SET_LOCK_TIMEOUT); + } +} diff --git a/db/java/src/com/sleepycat/db/TransactionConfig.java b/db/java/src/com/sleepycat/db/TransactionConfig.java new file mode 100644 index 000000000..6d00225cc --- /dev/null +++ b/db/java/src/com/sleepycat/db/TransactionConfig.java @@ -0,0 +1,89 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: TransactionConfig.java,v 1.3 2004/09/28 19:30:37 mjc Exp $ + */ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; +import com.sleepycat.db.internal.DbEnv; +import com.sleepycat.db.internal.DbTxn; + +public class TransactionConfig implements Cloneable { + /* + * For internal use, to allow null as a valid value for + * the config parameter. + */ + public static final TransactionConfig DEFAULT = new TransactionConfig(); + + /* package */ + static TransactionConfig checkNull(TransactionConfig config) { + return (config == null) ? DEFAULT : config; + } + + private boolean dirtyRead = false; + private boolean degree2 = false; + private boolean noSync = false; + private boolean noWait = false; + private boolean sync = false; + + public TransactionConfig() { + } + + public void setDegree2(final boolean degree2) { + this.degree2 = degree2; + } + + public boolean getDegree2() { + return degree2; + } + + public void setDirtyRead(final boolean dirtyRead) { + this.dirtyRead = dirtyRead; + } + + public boolean getDirtyRead() { + return dirtyRead; + } + + public void setNoSync(final boolean noSync) { + this.noSync = noSync; + } + + public boolean getNoSync() { + return noSync; + } + + public void setNoWait(final boolean noWait) { + this.noWait = noWait; + } + + public boolean getNoWait() { + return noWait; + } + + public void setSync(final boolean sync) { + this.sync = sync; + } + + public boolean getSync() { + return sync; + } + + DbTxn beginTransaction(final DbEnv dbenv, final DbTxn parent) + throws DatabaseException { + + int flags = 0; + flags |= degree2 ? DbConstants.DB_DEGREE_2 : 0; + flags |= dirtyRead ? DbConstants.DB_DIRTY_READ : 0; + flags |= noSync ? DbConstants.DB_TXN_NOSYNC : 0; + flags |= noWait ? DbConstants.DB_TXN_NOWAIT : 0; + flags |= sync ? DbConstants.DB_TXN_SYNC : 0; + + return dbenv.txn_begin(parent, flags); + } +} diff --git a/db/java/src/com/sleepycat/db/TransactionStats.java b/db/java/src/com/sleepycat/db/TransactionStats.java new file mode 100644 index 000000000..a872cde4c --- /dev/null +++ b/db/java/src/com/sleepycat/db/TransactionStats.java @@ -0,0 +1,147 @@ +/*- + * DO NOT EDIT: automatically built by dist/s_java_stat. + * + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2002-2004 + * Sleepycat Software. All rights reserved. + */ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbUtil; + +public class TransactionStats +{ + // no public constructor + protected TransactionStats() {} + + public static class Active { // no public constructor + protected Active() {} + + private int txnid; + public int getTxnId() { + return txnid; + } + + private int parentid; + public int getParentId() { + return parentid; + } + + private LogSequenceNumber lsn; + public LogSequenceNumber getLsn() { + return lsn; + } + + private int xa_status; + public int getXaStatus() { + return xa_status; + } + + private byte[] xid; + public byte[] getXId() { + return xid; + } + + public String toString() { + return "Active:" + + "\n txnid=" + txnid + + "\n parentid=" + parentid + + "\n lsn=" + lsn + + "\n xa_status=" + xa_status + + "\n xid=" + DbUtil.byteArrayToString(xid) + ; + } + }; + + private LogSequenceNumber st_last_ckp; + public LogSequenceNumber getLastCkp() { + return st_last_ckp; + } + + private long st_time_ckp; + public long getTimeCkp() { + return st_time_ckp; + } + + private int st_last_txnid; + public int getLastTxnId() { + return st_last_txnid; + } + + private int st_maxtxns; + public int getMaxTxns() { + return st_maxtxns; + } + + private int st_naborts; + public int getNaborts() { + return st_naborts; + } + + private int st_nbegins; + public int getNumBegins() { + return st_nbegins; + } + + private int st_ncommits; + public int getNumCommits() { + return st_ncommits; + } + + private int st_nactive; + public int getNactive() { + return st_nactive; + } + + private int st_nrestores; + public int getNumRestores() { + return st_nrestores; + } + + private int st_maxnactive; + public int getMaxNactive() { + return st_maxnactive; + } + + private Active[] st_txnarray; + public Active[] getTxnarray() { + return st_txnarray; + } + + private int st_region_wait; + public int getRegionWait() { + return st_region_wait; + } + + private int st_region_nowait; + public int getRegionNowait() { + return st_region_nowait; + } + + private int st_regsize; + public int getRegSize() { + return st_regsize; + } + + public String toString() { + return "TransactionStats:" + + "\n st_last_ckp=" + st_last_ckp + + "\n st_time_ckp=" + st_time_ckp + + "\n st_last_txnid=" + st_last_txnid + + "\n st_maxtxns=" + st_maxtxns + + "\n st_naborts=" + st_naborts + + "\n st_nbegins=" + st_nbegins + + "\n st_ncommits=" + st_ncommits + + "\n st_nactive=" + st_nactive + + "\n st_nrestores=" + st_nrestores + + "\n st_maxnactive=" + st_maxnactive + + "\n st_txnarray=" + DbUtil.objectArrayToString(st_txnarray, "st_txnarray") + + "\n st_region_wait=" + st_region_wait + + "\n st_region_nowait=" + st_region_nowait + + "\n st_regsize=" + st_regsize + ; + } +} +// end of TransactionStats.java diff --git a/db/java/src/com/sleepycat/db/VerifyConfig.java b/db/java/src/com/sleepycat/db/VerifyConfig.java new file mode 100644 index 000000000..0bb4031e0 --- /dev/null +++ b/db/java/src/com/sleepycat/db/VerifyConfig.java @@ -0,0 +1,81 @@ +/*- +* See the file LICENSE for redistribution information. +* +* Copyright (c) 2002-2004 +* Sleepycat Software. All rights reserved. +* +* $Id: VerifyConfig.java,v 1.3 2004/04/21 01:09:10 mjc Exp $ +*/ + +package com.sleepycat.db; + +import com.sleepycat.db.internal.DbConstants; + +public class VerifyConfig { + public static final VerifyConfig DEFAULT = new VerifyConfig(); + + /* package */ + static VerifyConfig checkNull(VerifyConfig config) { + return (config == null) ? DEFAULT : config; + } + + private boolean aggressive = false; + private boolean noOrderCheck = false; + private boolean orderCheckOnly = false; + private boolean salvage = false; + private boolean printable = false; + + public VerifyConfig() { + } + + public void setAggressive(final boolean aggressive) { + this.aggressive = aggressive; + } + + public boolean getAggressive() { + return aggressive; + } + + public void setNoOrderCheck(final boolean noOrderCheck) { + this.noOrderCheck = noOrderCheck; + } + + public boolean getNoOrderCheck() { + return printable; + } + + public void setOrderCheckOnly(final boolean orderCheckOnly) { + this.orderCheckOnly = orderCheckOnly; + } + + public boolean getOrderCheckOnly() { + return orderCheckOnly; + } + + public void setPrintable(final boolean printable) { + this.printable = printable; + } + + public boolean getPrintable() { + return printable; + } + + public void setSalvage(final boolean salvage) { + this.salvage = salvage; + } + + public boolean getSalvage() { + return salvage; + } + + int getFlags() { + int flags = 0; + flags |= aggressive ? DbConstants.DB_AGGRESSIVE : 0; + flags |= noOrderCheck ? DbConstants.DB_NOORDERCHK : 0; + flags |= orderCheckOnly ? DbConstants.DB_ORDERCHKONLY : 0; + flags |= salvage ? DbConstants.DB_SALVAGE : 0; + flags |= printable ? DbConstants.DB_PRINTABLE : 0; + + return flags; + } +} diff --git a/db/java/src/com/sleepycat/db/internal/Db.java b/db/java/src/com/sleepycat/db/internal/Db.java new file mode 100644 index 000000000..0bee8f4c1 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/Db.java @@ -0,0 +1,399 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + +package com.sleepycat.db.internal; + + +import com.sleepycat.db.*; +import java.util.Comparator; + +public class Db { + private long swigCPtr; + protected boolean swigCMemOwn; + + protected Db(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected Db() { + this(0, false); + } + + /* package */ void delete() { + if(swigCPtr != 0 && swigCMemOwn) { + swigCMemOwn = false; + throw new UnsupportedOperationException("C++ destructor does not have public access"); + } + swigCPtr = 0; + } + + protected static long getCPtr(Db obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + + /* package */ static final int GIGABYTE = 1 << 30; + /* + * Internally, the JNI layer creates a global reference to each Db, + * which can potentially be different to this. We keep a copy here so + * we can clean up after destructors. + */ + private long db_ref; + private DbEnv dbenv; + private boolean private_dbenv; + + public Database wrapper; + private RecordNumberAppender append_recno_handler; + private Comparator bt_compare_handler; + private BtreePrefixCalculator bt_prefix_handler; + private Comparator dup_compare_handler; + private FeedbackHandler db_feedback_handler; + private Hasher h_hash_handler; + private SecondaryKeyCreator seckey_create_handler; + + /* Called by the Db constructor */ + private void initialize(DbEnv dbenv) { + if (dbenv == null) { + private_dbenv = true; + dbenv = db_java.getDbEnv0(this); + dbenv.initialize(); + } + this.dbenv = dbenv; + db_ref = db_java.initDbRef0(this, this); + } + + private void cleanup() { + swigCPtr = 0; + db_java.deleteRef0(db_ref); + db_ref = 0L; + if (private_dbenv) + dbenv.cleanup(); + dbenv = null; + } + + public synchronized void close(int flags) throws DatabaseException { + try { + close0(flags); + } finally { + cleanup(); + } + } + + public DbEnv get_env() throws DatabaseException { + return dbenv; + } + + private final void handle_append_recno(DatabaseEntry data, int recno) + throws DatabaseException { + append_recno_handler.appendRecordNumber(wrapper, data, recno); + } + + public RecordNumberAppender get_append_recno() throws com.sleepycat.db.DatabaseException { + return append_recno_handler; + } + + private final int handle_bt_compare(DatabaseEntry dbt1, DatabaseEntry dbt2) { + return bt_compare_handler.compare(dbt1, dbt2); + } + + public Comparator get_bt_compare() throws com.sleepycat.db.DatabaseException { + return bt_compare_handler; + } + + private final int handle_bt_prefix(DatabaseEntry dbt1, DatabaseEntry dbt2) { + return bt_prefix_handler.prefix(wrapper, dbt1, dbt2); + } + + public BtreePrefixCalculator get_bt_prefix() throws com.sleepycat.db.DatabaseException { + return bt_prefix_handler; + } + + private final void handle_db_feedback(int opcode, int percent) { + if (opcode == DbConstants.DB_UPGRADE) + db_feedback_handler.upgradeFeedback(wrapper, percent); + else if (opcode == DbConstants.DB_VERIFY) + db_feedback_handler.upgradeFeedback(wrapper, percent); + /* No other database feedback types known. */ + } + + public FeedbackHandler get_feedback() throws com.sleepycat.db.DatabaseException { + return db_feedback_handler; + } + + private final int handle_dup_compare(DatabaseEntry dbt1, DatabaseEntry dbt2) { + return dup_compare_handler.compare(dbt1, dbt2); + } + + public Comparator get_dup_compare() throws com.sleepycat.db.DatabaseException { + return dup_compare_handler; + } + + private final int handle_h_hash(byte[] data, int len) { + return h_hash_handler.hash(wrapper, data, len); + } + + public Hasher get_h_hash() throws com.sleepycat.db.DatabaseException { + return h_hash_handler; + } + + private final int handle_seckey_create(DatabaseEntry key, DatabaseEntry data, DatabaseEntry result) + throws DatabaseException { + return seckey_create_handler.createSecondaryKey( + (SecondaryDatabase)wrapper, key, data, result) ? + 0 : DbConstants.DB_DONOTINDEX; + } + + public SecondaryKeyCreator get_seckey_create() throws com.sleepycat.db.DatabaseException { + return seckey_create_handler; + } + + public synchronized void remove(String file, String database, int flags) + throws DatabaseException, java.io.FileNotFoundException { + try { + remove0(file, database, flags); + } finally { + cleanup(); + } + } + + public synchronized void rename(String file, String database, + String newname, int flags) + throws DatabaseException, java.io.FileNotFoundException { + try { + rename0(file, database, newname, flags); + } finally { + cleanup(); + } + } + + public synchronized boolean verify(String file, String database, + java.io.OutputStream outfile, int flags) + throws DatabaseException, java.io.FileNotFoundException { + try { + return verify0(file, database, outfile, flags); + } finally { + cleanup(); + } + } + + public ErrorHandler get_errcall() /* no exception */ { + return dbenv.get_errcall(); + } + + public void set_errcall(ErrorHandler db_errcall_fcn) /* no exception */ { + dbenv.set_errcall(db_errcall_fcn); + } + + public MessageHandler get_msgcall() /* no exception */ { + return dbenv.get_msgcall(); + } + + public void set_msgcall(MessageHandler db_msgcall_fcn) /* no exception */ { + dbenv.set_msgcall(db_msgcall_fcn); + } + + public java.io.OutputStream get_error_stream() /* no exception */ { + return dbenv.get_error_stream(); + } + + public void set_error_stream(java.io.OutputStream stream) /* no exception */ { + dbenv.set_error_stream(stream); + } + + public java.io.OutputStream get_message_stream() /* no exception */ { + return dbenv.get_message_stream(); + } + + public void set_message_stream(java.io.OutputStream stream) /* no exception */ { + dbenv.set_message_stream(stream); + } + + public void set_paniccall(PanicHandler db_panic_fcn) + throws DatabaseException { + dbenv.set_paniccall(db_panic_fcn); + } + + public PanicHandler get_paniccall() throws com.sleepycat.db.DatabaseException { + return dbenv.get_paniccall(); + } + + public Db(DbEnv dbenv, int flags) throws com.sleepycat.db.DatabaseException { + this(db_javaJNI.new_Db(DbEnv.getCPtr(dbenv), flags), true); + initialize(dbenv); + } + + public void associate(DbTxn txnid, Db secondary, com.sleepycat.db.SecondaryKeyCreator callback, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_associate(swigCPtr, DbTxn.getCPtr(txnid), Db.getCPtr(secondary), (secondary.seckey_create_handler = callback) , flags); } + + /* package */ int close0(int flags) { + return db_javaJNI.Db_close0(swigCPtr, flags); + } + + public Dbc cursor(DbTxn txnid, int flags) throws com.sleepycat.db.DatabaseException { + long cPtr = db_javaJNI.Db_cursor(swigCPtr, DbTxn.getCPtr(txnid), flags); + return (cPtr == 0) ? null : new Dbc(cPtr, false); + } + + public int del(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_del(swigCPtr, DbTxn.getCPtr(txnid), key, flags); + } + + public void err(int error, String message) /* no exception */ { + db_javaJNI.Db_err(swigCPtr, error, message); + } + + public void errx(String message) /* no exception */ { + db_javaJNI.Db_errx(swigCPtr, message); + } + + public boolean get_transactional() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_transactional(swigCPtr); } + + public int get(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get(swigCPtr, DbTxn.getCPtr(txnid), key, data, flags); + } + + public boolean get_byteswapped() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_byteswapped(swigCPtr); } + + public long get_cachesize() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get_cachesize(swigCPtr); + } + + public int get_cachesize_ncache() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_cachesize_ncache(swigCPtr); } + + public String get_filename() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get_filename(swigCPtr); + } + + public String get_dbname() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get_dbname(swigCPtr); + } + + public int get_encrypt_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_encrypt_flags(swigCPtr); } + + public String get_errpfx() /* no exception */ { + return db_javaJNI.Db_get_errpfx(swigCPtr); + } + + public int get_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_flags(swigCPtr); } + + public int get_lorder() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get_lorder(swigCPtr); + } + + public DbMpoolFile get_mpf() throws com.sleepycat.db.DatabaseException { + long cPtr = db_javaJNI.Db_get_mpf(swigCPtr); + return (cPtr == 0) ? null : new DbMpoolFile(cPtr, false); + } + + public int get_open_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_open_flags(swigCPtr); } + + public int get_pagesize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_pagesize(swigCPtr); } + + public int get_bt_minkey() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_bt_minkey(swigCPtr); } + + public int get_h_ffactor() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_h_ffactor(swigCPtr); } + + public int get_h_nelem() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_h_nelem(swigCPtr); } + + public int get_re_delim() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get_re_delim(swigCPtr); + } + + public int get_re_len() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_re_len(swigCPtr); } + + public int get_re_pad() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get_re_pad(swigCPtr); + } + + public String get_re_source() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get_re_source(swigCPtr); + } + + public int get_q_extentsize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_get_q_extentsize(swigCPtr); } + + public int get_type() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_get_type(swigCPtr); + } + + public Dbc join(Dbc[] curslist, int flags) throws com.sleepycat.db.DatabaseException { + long cPtr = db_javaJNI.Db_join(swigCPtr, curslist, flags); + return (cPtr == 0) ? null : new Dbc(cPtr, true); + } + + public void key_range(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.KeyRange key_range, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_key_range(swigCPtr, DbTxn.getCPtr(txnid), key, key_range, flags); } + + public void open(DbTxn txnid, String file, String database, int type, int flags, int mode) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException { db_javaJNI.Db_open(swigCPtr, DbTxn.getCPtr(txnid), file, database, type, flags, mode); } + + public int pget(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry pkey, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_pget(swigCPtr, DbTxn.getCPtr(txnid), key, pkey, data, flags); + } + + public int put(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_put(swigCPtr, DbTxn.getCPtr(txnid), key, data, flags); + } + + /* package */ void remove0(String file, String database, int flags) { db_javaJNI.Db_remove0(swigCPtr, file, database, flags); } + + /* package */ void rename0(String file, String database, String newname, int flags) { db_javaJNI.Db_rename0(swigCPtr, file, database, newname, flags); } + + public void set_append_recno(com.sleepycat.db.RecordNumberAppender db_append_recno_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_append_recno(swigCPtr, (append_recno_handler = db_append_recno_fcn) ); } + + public void set_bt_compare(java.util.Comparator bt_compare_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_bt_compare(swigCPtr, (bt_compare_handler = bt_compare_fcn) ); } + + public void set_bt_maxkey(int maxkey) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_bt_maxkey(swigCPtr, maxkey); } + + public void set_bt_minkey(int bt_minkey) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_bt_minkey(swigCPtr, bt_minkey); } + + public void set_bt_prefix(com.sleepycat.db.BtreePrefixCalculator bt_prefix_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_bt_prefix(swigCPtr, (bt_prefix_handler = bt_prefix_fcn) ); } + + public void set_cachesize(long bytes, int ncache) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_cachesize(swigCPtr, bytes, ncache); } + + public void set_dup_compare(java.util.Comparator dup_compare_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_dup_compare(swigCPtr, (dup_compare_handler = dup_compare_fcn) ); } + + public void set_encrypt(String passwd, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_encrypt(swigCPtr, passwd, flags); } + + public void set_errpfx(String errpfx) /* no exception */ { + db_javaJNI.Db_set_errpfx(swigCPtr, errpfx); + } + + public void set_feedback(com.sleepycat.db.FeedbackHandler db_feedback_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_feedback(swigCPtr, (db_feedback_handler = db_feedback_fcn) ); } + + public void set_flags(int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_flags(swigCPtr, flags); } + + public void set_h_ffactor(int h_ffactor) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_h_ffactor(swigCPtr, h_ffactor); } + + public void set_h_hash(com.sleepycat.db.Hasher h_hash_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_h_hash(swigCPtr, (h_hash_handler = h_hash_fcn) ); } + + public void set_h_nelem(int h_nelem) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_h_nelem(swigCPtr, h_nelem); } + + public void set_lorder(int lorder) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_lorder(swigCPtr, lorder); } + + public void set_pagesize(long pagesize) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_pagesize(swigCPtr, pagesize); } + + public void set_re_delim(int re_delim) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_re_delim(swigCPtr, re_delim); } + + public void set_re_len(int re_len) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_re_len(swigCPtr, re_len); } + + public void set_re_pad(int re_pad) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_re_pad(swigCPtr, re_pad); } + + public void set_re_source(String source) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_re_source(swigCPtr, source); } + + public void set_q_extentsize(int extentsize) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_set_q_extentsize(swigCPtr, extentsize); } + + public Object stat(DbTxn txnid, int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.Db_stat(swigCPtr, DbTxn.getCPtr(txnid), flags); } + + public void sync(int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_sync(swigCPtr, flags); } + + public int truncate(DbTxn txnid, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Db_truncate(swigCPtr, DbTxn.getCPtr(txnid), flags); + } + + public void upgrade(String file, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.Db_upgrade(swigCPtr, file, flags); } + + /* package */ boolean verify0(String file, String database, java.io.OutputStream outfile, int flags) { return db_javaJNI.Db_verify0(swigCPtr, file, database, outfile, flags); } + +} diff --git a/db/java/src/com/sleepycat/db/internal/DbClient.java b/db/java/src/com/sleepycat/db/internal/DbClient.java new file mode 100644 index 000000000..28d586ecb --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbClient.java @@ -0,0 +1,17 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2003 + * Sleepycat Software. All rights reserved. + * + * $Id: DbClient.java,v 1.1 2004/04/06 20:43:40 mjc Exp $ + */ +package com.sleepycat.db.internal; + +/** + * The DbClient object is used to encapsulate a reference to an RPC + * client.</p> + */ +public interface DbClient { +} diff --git a/db/java/src/com/sleepycat/db/internal/DbConstants.java b/db/java/src/com/sleepycat/db/internal/DbConstants.java new file mode 100644 index 000000000..0ad378cd6 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbConstants.java @@ -0,0 +1,182 @@ +/* DO NOT EDIT: automatically built by dist/s_java_const. */ + +package com.sleepycat.db.internal; + +public interface DbConstants +{ + int DB_AFTER = 1; + int DB_AGGRESSIVE = 0x0000001; + int DB_APPEND = 2; + int DB_ARCH_ABS = 0x001; + int DB_ARCH_DATA = 0x002; + int DB_ARCH_LOG = 0x004; + int DB_ARCH_REMOVE = 0x008; + int DB_AUTO_COMMIT = 0x01000000; + int DB_BEFORE = 3; + int DB_BTREE = 1; + int DB_CDB_ALLDB = 0x00001000; + int DB_CHKSUM = 0x0000001; + int DB_CONSUME = 5; + int DB_CONSUME_WAIT = 6; + int DB_CREATE = 0x0000001; + int DB_CURRENT = 7; + int DB_DBT_MALLOC = 0x004; + int DB_DBT_PARTIAL = 0x008; + int DB_DBT_USERMEM = 0x020; + int DB_DEGREE_2 = 0x02000000; + int DB_DIRECT_DB = 0x00002000; + int DB_DIRECT_LOG = 0x00004000; + int DB_DIRTY_READ = 0x04000000; + int DB_DONOTINDEX = -30998; + int DB_DSYNC_LOG = 0x00008000; + int DB_DUP = 0x0000002; + int DB_DUPSORT = 0x0000004; + int DB_EID_BROADCAST = -1; + int DB_EID_INVALID = -2; + int DB_ENCRYPT = 0x0000008; + int DB_ENCRYPT_AES = 0x0000001; + int DB_EXCL = 0x0001000; + int DB_FAST_STAT = 8; + int DB_FIRST = 9; + int DB_FLUSH = 0x001; + int DB_FORCE = 0x0000004; + int DB_GET_BOTH = 10; + int DB_GET_BOTH_RANGE = 12; + int DB_GET_RECNO = 13; + int DB_HASH = 2; + int DB_INIT_CDB = 0x0001000; + int DB_INIT_LOCK = 0x0002000; + int DB_INIT_LOG = 0x0004000; + int DB_INIT_MPOOL = 0x0008000; + int DB_INIT_REP = 0x0010000; + int DB_INIT_TXN = 0x0020000; + int DB_INORDER = 0x0000010; + int DB_JOINENV = 0x0040000; + int DB_JOIN_ITEM = 14; + int DB_JOIN_NOSORT = 0x0000001; + int DB_KEYEMPTY = -30997; + int DB_KEYEXIST = -30996; + int DB_KEYFIRST = 15; + int DB_KEYLAST = 16; + int DB_LAST = 17; + int DB_LOCKDOWN = 0x0080000; + int DB_LOCK_DEFAULT = 1; + int DB_LOCK_EXPIRE = 2; + int DB_LOCK_GET = 1; + int DB_LOCK_GET_TIMEOUT = 2; + int DB_LOCK_IREAD = 5; + int DB_LOCK_IWR = 6; + int DB_LOCK_IWRITE = 4; + int DB_LOCK_MAXLOCKS = 3; + int DB_LOCK_MAXWRITE = 4; + int DB_LOCK_MINLOCKS = 5; + int DB_LOCK_MINWRITE = 6; + int DB_LOCK_NOTGRANTED = -30994; + int DB_LOCK_NOWAIT = 0x002; + int DB_LOCK_OLDEST = 7; + int DB_LOCK_PUT = 4; + int DB_LOCK_PUT_ALL = 5; + int DB_LOCK_PUT_OBJ = 6; + int DB_LOCK_RANDOM = 8; + int DB_LOCK_READ = 1; + int DB_LOCK_TIMEOUT = 8; + int DB_LOCK_WRITE = 2; + int DB_LOCK_YOUNGEST = 9; + int DB_LOG_AUTOREMOVE = 0x00010000; + int DB_LOG_INMEMORY = 0x00020000; + int DB_MPOOL_NOFILE = 0x001; + int DB_MPOOL_UNLINK = 0x002; + int DB_MULTIPLE = 0x08000000; + int DB_MULTIPLE_KEY = 0x10000000; + int DB_NEXT = 18; + int DB_NEXT_DUP = 19; + int DB_NEXT_NODUP = 20; + int DB_NODUPDATA = 21; + int DB_NOLOCKING = 0x00040000; + int DB_NOMMAP = 0x0000008; + int DB_NOORDERCHK = 0x0000002; + int DB_NOOVERWRITE = 22; + int DB_NOPANIC = 0x00080000; + int DB_NOSERVER_HOME = -30991; + int DB_NOSERVER_ID = -30990; + int DB_NOSYNC = 23; + int DB_NOTFOUND = -30989; + int DB_ORDERCHKONLY = 0x0000004; + int DB_OVERWRITE = 0x00100000; + int DB_PANIC_ENVIRONMENT = 0x00200000; + int DB_POSITION = 24; + int DB_PREV = 25; + int DB_PREV_NODUP = 26; + int DB_PRINTABLE = 0x0000020; + int DB_PRIORITY_DEFAULT = 3; + int DB_PRIORITY_HIGH = 4; + int DB_PRIORITY_LOW = 2; + int DB_PRIORITY_VERY_HIGH = 5; + int DB_PRIORITY_VERY_LOW = 1; + int DB_PRIVATE = 0x0100000; + int DB_QUEUE = 4; + int DB_RDONLY = 0x0000010; + int DB_RECNO = 3; + int DB_RECNUM = 0x0000020; + int DB_RECOVER = 0x0000020; + int DB_RECOVER_FATAL = 0x0200000; + int DB_REGION_INIT = 0x00400000; + int DB_RENUMBER = 0x0000040; + int DB_REP_CLIENT = 0x001; + int DB_REP_DUPMASTER = -30986; + int DB_REP_HOLDELECTION = -30984; + int DB_REP_ISPERM = -30983; + int DB_REP_MASTER = 0x002; + int DB_REP_NEWMASTER = -30982; + int DB_REP_NEWSITE = -30981; + int DB_REP_NOBUFFER = 0x0000001; + int DB_REP_NOTPERM = -30980; + int DB_REP_PERMANENT = 0x0000002; + int DB_REP_STARTUPDONE = -30979; + int DB_REVSPLITOFF = 0x0000080; + int DB_RMW = 0x20000000; + int DB_RPCCLIENT = 0x0000001; + int DB_SALVAGE = 0x0000040; + int DB_SEQ_DEC = 0x00000001; + int DB_SEQ_INC = 0x00000002; + int DB_SEQ_WRAP = 0x00000008; + int DB_SET = 28; + int DB_SET_LOCK_TIMEOUT = 29; + int DB_SET_RANGE = 30; + int DB_SET_RECNO = 31; + int DB_SET_TXN_TIMEOUT = 33; + int DB_SNAPSHOT = 0x0000100; + int DB_STAT_CLEAR = 0x0000002; + int DB_SYSTEM_MEM = 0x0400000; + int DB_THREAD = 0x0000040; + int DB_TIME_NOTGRANTED = 0x00800000; + int DB_TRUNCATE = 0x0000080; + int DB_TXN_ABORT = 0; + int DB_TXN_APPLY = 1; + int DB_TXN_BACKWARD_ROLL = 3; + int DB_TXN_FORWARD_ROLL = 4; + int DB_TXN_NOSYNC = 0x0000100; + int DB_TXN_NOT_DURABLE = 0x0000200; + int DB_TXN_NOWAIT = 0x0001000; + int DB_TXN_PRINT = 7; + int DB_TXN_SYNC = 0x0002000; + int DB_TXN_WRITE_NOSYNC = 0x10000000; + int DB_UNKNOWN = 5; + int DB_UPGRADE = 0x0000001; + int DB_USE_ENVIRON = 0x0000400; + int DB_USE_ENVIRON_ROOT = 0x0000800; + int DB_VERB_DEADLOCK = 0x0001; + int DB_VERB_RECOVERY = 0x0002; + int DB_VERB_REPLICATION = 0x0004; + int DB_VERB_WAITSFOR = 0x0008; + int DB_VERIFY = 0x0000002; + int DB_VERSION_MAJOR = 4; + int DB_VERSION_MINOR = 3; + int DB_VERSION_PATCH = 14; + int DB_WRITECURSOR = 35; + int DB_XA_CREATE = 0x0000002; + int DB_XIDDATASIZE = 128; + int DB_YIELDCPU = 0x20000000; +} + +// end of DbConstants.java diff --git a/db/java/src/com/sleepycat/db/internal/DbEnv.java b/db/java/src/com/sleepycat/db/internal/DbEnv.java new file mode 100644 index 000000000..1a8112943 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbEnv.java @@ -0,0 +1,434 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + +package com.sleepycat.db.internal; + + +import com.sleepycat.db.*; +import java.util.Comparator; + +public class DbEnv { + private long swigCPtr; + protected boolean swigCMemOwn; + + protected DbEnv(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected DbEnv() { + this(0, false); + } + + /* package */ void delete() { + if(swigCPtr != 0 && swigCMemOwn) { + swigCMemOwn = false; + throw new UnsupportedOperationException("C++ destructor does not have public access"); + } + swigCPtr = 0; + } + + protected static long getCPtr(DbEnv obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + + /* + * Internally, the JNI layer creates a global reference to each DbEnv, + * which can potentially be different to this. We keep a copy here so + * we can clean up after destructors. + */ + private long dbenv_ref; + public Environment wrapper; + + private LogRecordHandler app_dispatch_handler; + private FeedbackHandler env_feedback_handler; + private ErrorHandler error_handler; + private MessageHandler message_handler; + private PanicHandler panic_handler; + private ReplicationTransport rep_transport_handler; + private java.io.OutputStream error_stream; + private java.io.OutputStream message_stream; + + public static class RepProcessMessage { + public int envid; + } + + /* + * Called by the public DbEnv constructor and for private environments + * by the Db constructor. + */ + void initialize() { + dbenv_ref = db_java.initDbEnvRef0(this, this); + /* Start with System.err as the default error stream. */ + set_error_stream(System.err); + set_message_stream(System.out); + } + + void cleanup() { + swigCPtr = 0; + db_java.deleteRef0(dbenv_ref); + dbenv_ref = 0L; + } + + public synchronized void close(int flags) throws DatabaseException { + try { + close0(flags); + } finally { + cleanup(); + } + } + + private final int handle_app_dispatch(DatabaseEntry dbt, LogSequenceNumber lsn, int recops) { + return app_dispatch_handler.handleLogRecord(wrapper, dbt, lsn, RecoveryOperation.fromFlag(recops)); + } + + public LogRecordHandler get_app_dispatch() throws com.sleepycat.db.DatabaseException { + return app_dispatch_handler; + } + + private final void handle_env_feedback(int opcode, int percent) { + if (opcode == DbConstants.DB_RECOVER) + env_feedback_handler.recoveryFeedback(wrapper, percent); + /* No other environment feedback type supported. */ + } + + public FeedbackHandler get_feedback() throws com.sleepycat.db.DatabaseException { + return env_feedback_handler; + } + + private final void handle_error(String errpfx, String msg) { + error_handler.error(wrapper, errpfx, msg); + } + + public ErrorHandler get_errcall() /* no exception */ { + return error_handler; + } + + private final void handle_message(String msg) { + message_handler.message(wrapper, msg); + } + + public MessageHandler get_msgcall() /* no exception */ { + return message_handler; + } + + private final void handle_panic(DatabaseException e) { + panic_handler.panic(wrapper, e); + } + + public PanicHandler get_paniccall() throws com.sleepycat.db.DatabaseException { + return panic_handler; + } + + private final int handle_rep_transport(DatabaseEntry control, DatabaseEntry rec, + LogSequenceNumber lsn, int envid, int flags) + throws DatabaseException { + return rep_transport_handler.send(wrapper, control, rec, lsn, envid, + (flags & DbConstants.DB_REP_NOBUFFER) != 0, + (flags & DbConstants.DB_REP_PERMANENT) != 0); + } + + public void lock_vec(/*u_int32_t*/ int locker, int flags, + LockRequest[] list, int offset, int count) throws DatabaseException { + db_javaJNI.DbEnv_lock_vec(swigCPtr, locker, flags, list, + offset, count); + } + + public synchronized void remove(String db_home, int flags) + throws DatabaseException, java.io.FileNotFoundException { + try { + remove0(db_home, flags); + } finally { + cleanup(); + } + } + + public void set_error_stream(java.io.OutputStream stream) /* no exception */ { + error_stream = stream; + final java.io.PrintWriter pw = new java.io.PrintWriter(stream); + set_errcall(new ErrorHandler() { + public void error(Environment env, + String prefix, String buf) /* no exception */ { + if (prefix != null) + pw.print(prefix + ": "); + pw.println(buf); + pw.flush(); + } + }); + } + + public java.io.OutputStream get_error_stream() /* no exception */ { + return error_stream; + } + + + public void set_message_stream(java.io.OutputStream stream) /* no exception */ { + message_stream = stream; + final java.io.PrintWriter pw = new java.io.PrintWriter(stream); + set_msgcall(new MessageHandler() { + public void message(Environment env, String msg) /* no exception */ + /* no exception */ { + pw.println(msg); + pw.flush(); + } + }); + } + + public java.io.OutputStream get_message_stream() /* no exception */ { + return message_stream; + } + + public void set_tx_timestamp(java.util.Date timestamp) throws com.sleepycat.db.DatabaseException { + set_tx_timestamp0(timestamp.getTime()/1000); + } + + public DbEnv(int flags) throws com.sleepycat.db.DatabaseException { + this(db_javaJNI.new_DbEnv(flags), true); + initialize(); + } + + /* package */ void close0(int flags) { db_javaJNI.DbEnv_close0(swigCPtr, flags); } + + public void dbremove(DbTxn txnid, String file, String database, int flags) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException { db_javaJNI.DbEnv_dbremove(swigCPtr, DbTxn.getCPtr(txnid), file, database, flags); } + + public void dbrename(DbTxn txnid, String file, String database, String newname, int flags) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException { db_javaJNI.DbEnv_dbrename(swigCPtr, DbTxn.getCPtr(txnid), file, database, newname, flags); } + + public void err(int error, String message) /* no exception */ { + db_javaJNI.DbEnv_err(swigCPtr, error, message); + } + + public void errx(String message) /* no exception */ { + db_javaJNI.DbEnv_errx(swigCPtr, message); + } + + public String[] get_data_dirs() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_data_dirs(swigCPtr); } + + public int get_encrypt_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_encrypt_flags(swigCPtr); } + + public String get_errpfx() /* no exception */ { + return db_javaJNI.DbEnv_get_errpfx(swigCPtr); + } + + public int get_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_flags(swigCPtr); } + + public String get_home() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_get_home(swigCPtr); + } + + public int get_open_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_open_flags(swigCPtr); } + + public long get_shm_key() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_shm_key(swigCPtr); } + + public int get_tas_spins() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_tas_spins(swigCPtr); } + + public String get_tmp_dir() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_get_tmp_dir(swigCPtr); + } + + public boolean get_verbose(int which) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_verbose(swigCPtr, which); } + + public void open(String db_home, int flags, int mode) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException { db_javaJNI.DbEnv_open(swigCPtr, db_home, flags, mode); } + + /* package */ void remove0(String db_home, int flags) { db_javaJNI.DbEnv_remove0(swigCPtr, db_home, flags); } + + public void set_cachesize(long bytes, int ncache) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_cachesize(swigCPtr, bytes, ncache); } + + public void set_data_dir(String dir) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_data_dir(swigCPtr, dir); } + + public void set_encrypt(String passwd, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_encrypt(swigCPtr, passwd, flags); } + + public void set_errcall(com.sleepycat.db.ErrorHandler db_errcall_fcn) /* no exception */ { + db_javaJNI.DbEnv_set_errcall(swigCPtr, (error_handler = db_errcall_fcn) ); + } + + public void set_errpfx(String errpfx) /* no exception */ { + db_javaJNI.DbEnv_set_errpfx(swigCPtr, errpfx); + } + + public void set_flags(int flags, boolean onoff) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_flags(swigCPtr, flags, onoff); } + + public void set_feedback(com.sleepycat.db.FeedbackHandler env_feedback_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_feedback(swigCPtr, (env_feedback_handler = env_feedback_fcn) ); } + + public void set_mp_mmapsize(long mp_mmapsize) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_mp_mmapsize(swigCPtr, mp_mmapsize); } + + public void set_msgcall(com.sleepycat.db.MessageHandler db_msgcall_fcn) /* no exception */ { + db_javaJNI.DbEnv_set_msgcall(swigCPtr, (message_handler = db_msgcall_fcn) ); + } + + public void set_paniccall(com.sleepycat.db.PanicHandler db_panic_fcn) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_paniccall(swigCPtr, (panic_handler = db_panic_fcn) ); } + + public void set_rpc_server(DbClient client, String host, long cl_timeout, long sv_timeout, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_rpc_server(swigCPtr, client, host, cl_timeout, sv_timeout, flags); } + + public void set_shm_key(long shm_key) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_shm_key(swigCPtr, shm_key); } + + public void set_tas_spins(int tas_spins) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_tas_spins(swigCPtr, tas_spins); } + + public void set_timeout(long timeout, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_timeout(swigCPtr, timeout, flags); } + + public void set_tmp_dir(String dir) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_tmp_dir(swigCPtr, dir); } + + public void set_tx_max(int max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_tx_max(swigCPtr, max); } + + public void set_app_dispatch(com.sleepycat.db.LogRecordHandler tx_recover) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_app_dispatch(swigCPtr, (app_dispatch_handler = tx_recover) ); } + + /* package */ void set_tx_timestamp0(long timestamp) { db_javaJNI.DbEnv_set_tx_timestamp0(swigCPtr, timestamp); } + + public void set_verbose(int which, boolean onoff) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_verbose(swigCPtr, which, onoff); } + + public byte[][] get_lk_conflicts() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_conflicts(swigCPtr); } + + public int get_lk_detect() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_detect(swigCPtr); } + + public int get_lk_max_locks() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_max_locks(swigCPtr); } + + public int get_lk_max_lockers() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_max_lockers(swigCPtr); } + + public int get_lk_max_objects() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lk_max_objects(swigCPtr); } + + public int lock_detect(int flags, int atype) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_lock_detect(swigCPtr, flags, atype); + } + + public DbLock lock_get(int locker, int flags, com.sleepycat.db.DatabaseEntry object, int lock_mode) throws com.sleepycat.db.DatabaseException { + long cPtr = db_javaJNI.DbEnv_lock_get(swigCPtr, locker, flags, object, lock_mode); + return (cPtr == 0) ? null : new DbLock(cPtr, true); + } + + public int lock_id() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_lock_id(swigCPtr); } + + public void lock_id_free(int id) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_lock_id_free(swigCPtr, id); } + + public void lock_put(DbLock lock) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_lock_put(swigCPtr, DbLock.getCPtr(lock)); } + + public com.sleepycat.db.LockStats lock_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_lock_stat(swigCPtr, flags); } + + public void set_lk_conflicts(byte[][] conflicts) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_conflicts(swigCPtr, conflicts); } + + public void set_lk_detect(int detect) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_detect(swigCPtr, detect); } + + public void set_lk_max_lockers(int max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_max_lockers(swigCPtr, max); } + + public void set_lk_max_locks(int max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_max_locks(swigCPtr, max); } + + public void set_lk_max_objects(int max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lk_max_objects(swigCPtr, max); } + + public int get_lg_bsize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lg_bsize(swigCPtr); } + + public String get_lg_dir() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_get_lg_dir(swigCPtr); + } + + public int get_lg_max() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lg_max(swigCPtr); } + + public int get_lg_regionmax() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_lg_regionmax(swigCPtr); } + + public String[] log_archive(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_log_archive(swigCPtr, flags); } + + public static int log_compare(com.sleepycat.db.LogSequenceNumber lsn0, com.sleepycat.db.LogSequenceNumber lsn1) /* no exception */ { + return db_javaJNI.DbEnv_log_compare(lsn0, lsn1); + } + + public DbLogc log_cursor(int flags) throws com.sleepycat.db.DatabaseException { + long cPtr = db_javaJNI.DbEnv_log_cursor(swigCPtr, flags); + return (cPtr == 0) ? null : new DbLogc(cPtr, true); + } + + public String log_file(com.sleepycat.db.LogSequenceNumber lsn) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_log_file(swigCPtr, lsn); + } + + public void log_flush(com.sleepycat.db.LogSequenceNumber lsn) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_log_flush(swigCPtr, lsn); } + + public void log_put(com.sleepycat.db.LogSequenceNumber lsn, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_log_put(swigCPtr, lsn, data, flags); } + + public com.sleepycat.db.LogStats log_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_log_stat(swigCPtr, flags); } + + public void set_lg_bsize(int lg_bsize) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lg_bsize(swigCPtr, lg_bsize); } + + public void set_lg_dir(String dir) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lg_dir(swigCPtr, dir); } + + public void set_lg_max(int lg_max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lg_max(swigCPtr, lg_max); } + + public void set_lg_regionmax(int lg_regionmax) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_lg_regionmax(swigCPtr, lg_regionmax); } + + public long get_cachesize() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_get_cachesize(swigCPtr); + } + + public int get_cachesize_ncache() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_get_cachesize_ncache(swigCPtr); + } + + public long get_mp_mmapsize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_mp_mmapsize(swigCPtr); } + + public com.sleepycat.db.CacheStats memp_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_memp_stat(swigCPtr, flags); } + + public com.sleepycat.db.CacheFileStats[] memp_fstat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_memp_fstat(swigCPtr, flags); } + + public int memp_trickle(int percent) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_memp_trickle(swigCPtr, percent); + } + + public int get_tx_max() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_tx_max(swigCPtr); } + + public long get_tx_timestamp() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_tx_timestamp(swigCPtr); } + + public long get_timeout(int flag) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_get_timeout(swigCPtr, flag); } + + public DbTxn txn_begin(DbTxn parent, int flags) throws com.sleepycat.db.DatabaseException { + long cPtr = db_javaJNI.DbEnv_txn_begin(swigCPtr, DbTxn.getCPtr(parent), flags); + return (cPtr == 0) ? null : new DbTxn(cPtr, false); + } + + public void txn_checkpoint(int kbyte, int min, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_txn_checkpoint(swigCPtr, kbyte, min, flags); } + + public com.sleepycat.db.PreparedTransaction[] txn_recover(int count, int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_txn_recover(swigCPtr, count, flags); } + + public com.sleepycat.db.TransactionStats txn_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_txn_stat(swigCPtr, flags); } + + public long get_rep_limit() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_get_rep_limit(swigCPtr); + } + + public int rep_elect(int nsites, int nvotes, int priority, int timeout, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbEnv_rep_elect(swigCPtr, nsites, nvotes, priority, timeout, flags); + } + + public int rep_process_message(com.sleepycat.db.DatabaseEntry control, com.sleepycat.db.DatabaseEntry rec, DbEnv.RepProcessMessage envid, com.sleepycat.db.LogSequenceNumber ret_lsn) /* no exception */ { + return db_javaJNI.DbEnv_rep_process_message(swigCPtr, control, rec, envid, ret_lsn); + } + + public void rep_start(com.sleepycat.db.DatabaseEntry cdata, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_rep_start(swigCPtr, cdata, flags); } + + public com.sleepycat.db.ReplicationStats rep_stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbEnv_rep_stat(swigCPtr, flags); } + + public void set_rep_limit(long bytes) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_rep_limit(swigCPtr, bytes); } + + public void set_rep_transport(int envid, com.sleepycat.db.ReplicationTransport send) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbEnv_set_rep_transport(swigCPtr, envid, (rep_transport_handler = send) ); } + + public static String strerror(int error) /* no exception */ { + return db_javaJNI.DbEnv_strerror(error); + } + + public static int get_version_major() /* no exception */ { + return db_javaJNI.DbEnv_get_version_major(); + } + + public static int get_version_minor() /* no exception */ { + return db_javaJNI.DbEnv_get_version_minor(); + } + + public static int get_version_patch() /* no exception */ { + return db_javaJNI.DbEnv_get_version_patch(); + } + + public static String get_version_string() /* no exception */ { + return db_javaJNI.DbEnv_get_version_string(); + } + +} diff --git a/db/java/src/com/sleepycat/db/internal/DbLock.java b/db/java/src/com/sleepycat/db/internal/DbLock.java new file mode 100644 index 000000000..723629908 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbLock.java @@ -0,0 +1,51 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + +package com.sleepycat.db.internal; + + +import com.sleepycat.db.*; +import java.util.Comparator; + +public class DbLock { + private long swigCPtr; + protected boolean swigCMemOwn; + + protected DbLock(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected DbLock() { + this(0, false); + } + + protected void finalize() { + try { + delete(); + } catch(Exception e) { + System.err.println("Exception during finalization: " + e); + e.printStackTrace(System.err); + } + } + + /* package */ void delete() { + if(swigCPtr != 0 && swigCMemOwn) { + swigCMemOwn = false; + db_javaJNI.delete_DbLock(swigCPtr); + } + swigCPtr = 0; + } + + protected static long getCPtr(DbLock obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + + public Lock wrapper; + +} diff --git a/db/java/src/com/sleepycat/db/internal/DbLogc.java b/db/java/src/com/sleepycat/db/internal/DbLogc.java new file mode 100644 index 000000000..d26d95f60 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbLogc.java @@ -0,0 +1,54 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + +package com.sleepycat.db.internal; + + +import com.sleepycat.db.*; +import java.util.Comparator; + +public class DbLogc { + private long swigCPtr; + protected boolean swigCMemOwn; + + protected DbLogc(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected DbLogc() { + this(0, false); + } + + /* package */ void delete() { + if(swigCPtr != 0 && swigCMemOwn) { + swigCMemOwn = false; + throw new UnsupportedOperationException("C++ destructor does not have public access"); + } + swigCPtr = 0; + } + + protected static long getCPtr(DbLogc obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + + public synchronized void close(int flags) throws DatabaseException { + try { + close0(flags); + } finally { + swigCPtr = 0; + } + } + + /* package */ void close0(int flags) { db_javaJNI.DbLogc_close0(swigCPtr, flags); } + + public int get(com.sleepycat.db.LogSequenceNumber lsn, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbLogc_get(swigCPtr, lsn, data, flags); + } + +} diff --git a/db/java/src/com/sleepycat/db/internal/DbMpoolFile.java b/db/java/src/com/sleepycat/db/internal/DbMpoolFile.java new file mode 100644 index 000000000..e72c1c70d --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbMpoolFile.java @@ -0,0 +1,56 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + +package com.sleepycat.db.internal; + + +import com.sleepycat.db.*; +import java.util.Comparator; + +public class DbMpoolFile { + private long swigCPtr; + protected boolean swigCMemOwn; + + protected DbMpoolFile(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected DbMpoolFile() { + this(0, false); + } + + /* package */ void delete() { + if(swigCPtr != 0 && swigCMemOwn) { + swigCMemOwn = false; + throw new UnsupportedOperationException("C++ destructor does not have public access"); + } + swigCPtr = 0; + } + + protected static long getCPtr(DbMpoolFile obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + + public int get_priority() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbMpoolFile_get_priority(swigCPtr); + } + + public void set_priority(int priority) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbMpoolFile_set_priority(swigCPtr, priority); } + + public int get_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbMpoolFile_get_flags(swigCPtr); } + + public void set_flags(int flags, boolean onoff) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbMpoolFile_set_flags(swigCPtr, flags, onoff); } + + public long get_maxsize() throws com.sleepycat.db.DatabaseException { + return db_javaJNI.DbMpoolFile_get_maxsize(swigCPtr); + } + + public void set_maxsize(long bytes) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbMpoolFile_set_maxsize(swigCPtr, bytes); } + +} diff --git a/db/java/src/com/sleepycat/db/internal/DbSequence.java b/db/java/src/com/sleepycat/db/internal/DbSequence.java new file mode 100644 index 000000000..ac8da65b5 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbSequence.java @@ -0,0 +1,96 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + +package com.sleepycat.db.internal; + + +import com.sleepycat.db.*; +import java.util.Comparator; + +public class DbSequence { + private long swigCPtr; + protected boolean swigCMemOwn; + + protected DbSequence(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected DbSequence() { + this(0, false); + } + + /* package */ void delete() { + if(swigCPtr != 0 && swigCMemOwn) { + swigCMemOwn = false; + throw new UnsupportedOperationException("C++ destructor does not have public access"); + } + swigCPtr = 0; + } + + protected static long getCPtr(DbSequence obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + + public Sequence wrapper; + + public synchronized void close(int flags) throws DatabaseException { + try { + close0(flags); + } finally { + swigCPtr = 0; + } + } + + public synchronized void remove(DbTxn txn, int flags) + throws DatabaseException { + try { + remove0(txn, flags); + } finally { + swigCPtr = 0; + } + } + + public DbSequence(Db db, int flags) throws com.sleepycat.db.DatabaseException { + this(db_javaJNI.new_DbSequence(Db.getCPtr(db), flags), true); + } + + /* package */ void close0(int flags) { db_javaJNI.DbSequence_close0(swigCPtr, flags); } + + public long get(DbTxn txnid, int delta, int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get(swigCPtr, DbTxn.getCPtr(txnid), delta, flags); } + + public int get_cachesize() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get_cachesize(swigCPtr); } + + public Db get_db() throws com.sleepycat.db.DatabaseException { + long cPtr = db_javaJNI.DbSequence_get_db(swigCPtr); + return (cPtr == 0) ? null : new Db(cPtr, false); + } + + public int get_flags() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get_flags(swigCPtr); } + + public void get_key(com.sleepycat.db.DatabaseEntry key) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_get_key(swigCPtr, key); } + + public long get_range_min() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get_range_min(swigCPtr); } + + public long get_range_max() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_get_range_max(swigCPtr); } + + public void initial_value(long val) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_initial_value(swigCPtr, val); } + + public void open(DbTxn txnid, com.sleepycat.db.DatabaseEntry key, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_open(swigCPtr, DbTxn.getCPtr(txnid), key, flags); } + + /* package */ void remove0(DbTxn txnid, int flags) { db_javaJNI.DbSequence_remove0(swigCPtr, DbTxn.getCPtr(txnid), flags); } + + public void set_cachesize(int size) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_set_cachesize(swigCPtr, size); } + + public void set_flags(int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_set_flags(swigCPtr, flags); } + + public void set_range(long min, long max) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbSequence_set_range(swigCPtr, min, max); } + + public com.sleepycat.db.SequenceStats stat(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbSequence_stat(swigCPtr, flags); } + +} diff --git a/db/java/src/com/sleepycat/db/internal/DbTxn.java b/db/java/src/com/sleepycat/db/internal/DbTxn.java new file mode 100644 index 000000000..d277f1982 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbTxn.java @@ -0,0 +1,103 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + +package com.sleepycat.db.internal; + + +import com.sleepycat.db.*; +import java.util.Comparator; + +public class DbTxn { + private long swigCPtr; + protected boolean swigCMemOwn; + + protected DbTxn(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected DbTxn() { + this(0, false); + } + + /* package */ void delete() { + if(swigCPtr != 0 && swigCMemOwn) { + swigCMemOwn = false; + throw new UnsupportedOperationException("C++ destructor does not have public access"); + } + swigCPtr = 0; + } + + protected static long getCPtr(DbTxn obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + + public void abort() throws DatabaseException { + try { + abort0(); + } finally { + swigCPtr = 0; + } + } + + public void commit(int flags) throws DatabaseException { + try { + commit0(flags); + } finally { + swigCPtr = 0; + } + } + + public void discard(int flags) throws DatabaseException { + try { + discard0(flags); + } finally { + swigCPtr = 0; + } + } + + /* + * We override Object.equals because it is possible for the Java API to + * create multiple DbTxns that reference the same underlying object. + * This can happen for example during DbEnv.txn_recover(). + */ + public boolean equals(Object obj) + { + if (this == obj) + return true; + + if (obj != null && (obj instanceof DbTxn)) { + DbTxn that = (DbTxn)obj; + return (this.swigCPtr == that.swigCPtr); + } + return false; + } + + /* + * We must override Object.hashCode whenever we override + * Object.equals() to enforce the maxim that equal objects have the + * same hashcode. + */ + public int hashCode() + { + return ((int)swigCPtr ^ (int)(swigCPtr >> 32)); + } + + /* package */ void abort0() { db_javaJNI.DbTxn_abort0(swigCPtr); } + + /* package */ void commit0(int flags) { db_javaJNI.DbTxn_commit0(swigCPtr, flags); } + + /* package */ void discard0(int flags) { db_javaJNI.DbTxn_discard0(swigCPtr, flags); } + + public int id() throws com.sleepycat.db.DatabaseException { return db_javaJNI.DbTxn_id(swigCPtr); } + + public void prepare(byte[] gid) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbTxn_prepare(swigCPtr, gid); } + + public void set_timeout(long timeout, int flags) throws com.sleepycat.db.DatabaseException { db_javaJNI.DbTxn_set_timeout(swigCPtr, timeout, flags); } + +} diff --git a/db/java/src/com/sleepycat/db/internal/DbUtil.java b/db/java/src/com/sleepycat/db/internal/DbUtil.java new file mode 100644 index 000000000..54a2730f9 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/DbUtil.java @@ -0,0 +1,179 @@ +/* + * - + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2001-2003 + * Sleepycat Software. All rights reserved. + * + * $Id: DbUtil.java,v 1.2 2004/09/22 18:01:04 bostic Exp $ + */ +package com.sleepycat.db.internal; + +/** + * DbUtil is a simple class that holds a few static utility functions other + * parts of the package share and that don't have a good home elsewhere. (For + * now, that's limited to byte-array-to-int conversion and back.) + */ + +public class DbUtil { + /** + * Get the u_int32_t stored beginning at offset "offset" into + * array "arr". We have to do the conversion manually since it's + * a C-native int, and we're not really supposed to make this + * kind of cast in Java. + * + * @return Description of the Return Value + */ + public static int array2int(byte[] arr, int offset) { + int b1; + int b2; + int b3; + int b4; + int pos = offset; + + // Get the component bytes; b4 is most significant, b1 least. + if (big_endian) { + b4 = arr[pos++]; + b3 = arr[pos++]; + b2 = arr[pos++]; + b1 = arr[pos]; + } else { + b1 = arr[pos++]; + b2 = arr[pos++]; + b3 = arr[pos++]; + b4 = arr[pos]; + } + + // Bytes are signed. Convert [-128, -1] to [128, 255]. + if (b1 < 0) { + b1 += 256; + } + if (b2 < 0) { + b2 += 256; + } + if (b3 < 0) { + b3 += 256; + } + if (b4 < 0) { + b4 += 256; + } + + // Put the bytes in their proper places in an int. + b2 <<= 8; + b3 <<= 16; + b4 <<= 24; + + // Return their sum. + return (b1 + b2 + b3 + b4); + } + + + /** + * Store the specified u_int32_t, with endianness appropriate to + * the platform we're running on, into four consecutive bytes of + * the specified byte array, starting from the specified offset. + */ + public static void int2array(int n, byte[] arr, int offset) { + int b1; + int b2; + int b3; + int b4; + int pos = offset; + + b1 = n & 0xff; + b2 = (n >> 8) & 0xff; + b3 = (n >> 16) & 0xff; + b4 = (n >> 24) & 0xff; + + // Bytes are signed. Convert [128, 255] to [-128, -1]. + if (b1 >= 128) { + b1 -= 256; + } + if (b2 >= 128) { + b2 -= 256; + } + if (b3 >= 128) { + b3 -= 256; + } + if (b4 >= 128) { + b4 -= 256; + } + + // Put the bytes in the appropriate place in the array. + if (big_endian) { + arr[pos++] = (byte) b4; + arr[pos++] = (byte) b3; + arr[pos++] = (byte) b2; + arr[pos] = (byte) b1; + } else { + arr[pos++] = (byte) b1; + arr[pos++] = (byte) b2; + arr[pos++] = (byte) b3; + arr[pos] = (byte) b4; + } + } + + + /** + * Convert a byte array to a concise, readable string suitable + * for use in toString methods of the *Stat classes. + * + * @return Description of the Return Value + */ + public static String byteArrayToString(byte[] barr) { + if (barr == null) { + return "null"; + } + + StringBuffer sb = new StringBuffer(); + int len = barr.length; + for (int i = 0; i < len; i++) { + sb.append('x'); + int val = (barr[i] >> 4) & 0xf; + if (val < 10) { + sb.append((char) ('0' + val)); + } else { + sb.append((char) ('a' + val - 10)); + } + val = barr[i] & 0xf; + if (val < 10) { + sb.append((char) ('0' + val)); + } else { + sb.append((char) ('a' + val - 10)); + } + } + return sb.toString(); + } + + + /** + * Convert an object array to a string, suitable for use in + * toString methods of the *Stat classes. + * + * @return Description of the Return Value + */ + public static String objectArrayToString(Object[] arr, String name) { + if (arr == null) { + return "null"; + } + + StringBuffer sb = new StringBuffer(); + int len = arr.length; + for (int i = 0; i < len; i++) { + sb.append("\n " + name + "[" + i + "]:\n"); + sb.append(" " + arr[i].toString()); + } + return sb.toString(); + } + + public static int default_lorder() { + return big_endian ? 4321 : 1234; + } + + private final static boolean big_endian = is_big_endian(); + + /** + * @return Description of the Return Value + */ + private native static boolean is_big_endian(); +} diff --git a/db/java/src/com/sleepycat/db/internal/Dbc.java b/db/java/src/com/sleepycat/db/internal/Dbc.java new file mode 100644 index 000000000..9d9bced88 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/Dbc.java @@ -0,0 +1,73 @@ +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + +package com.sleepycat.db.internal; + + +import com.sleepycat.db.*; +import java.util.Comparator; + +public class Dbc { + private long swigCPtr; + protected boolean swigCMemOwn; + + protected Dbc(long cPtr, boolean cMemoryOwn) { + swigCMemOwn = cMemoryOwn; + swigCPtr = cPtr; + } + + protected Dbc() { + this(0, false); + } + + /* package */ void delete() { + if(swigCPtr != 0 && swigCMemOwn) { + swigCMemOwn = false; + throw new UnsupportedOperationException("C++ destructor does not have public access"); + } + swigCPtr = 0; + } + + protected static long getCPtr(Dbc obj) { + return (obj == null) ? 0 : obj.swigCPtr; + } + + public synchronized void close() throws DatabaseException { + try { + close0(); + } finally { + swigCPtr = 0; + } + } + + /* package */ void close0() { db_javaJNI.Dbc_close0(swigCPtr); } + + public int count(int flags) throws com.sleepycat.db.DatabaseException { return db_javaJNI.Dbc_count(swigCPtr, flags); } + + public int del(int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Dbc_del(swigCPtr, flags); + } + + public Dbc dup(int flags) throws com.sleepycat.db.DatabaseException { + long cPtr = db_javaJNI.Dbc_dup(swigCPtr, flags); + return (cPtr == 0) ? null : new Dbc(cPtr, false); + } + + public int get(com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Dbc_get(swigCPtr, key, data, flags); + } + + public int pget(com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry pkey, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Dbc_pget(swigCPtr, key, pkey, data, flags); + } + + public int put(com.sleepycat.db.DatabaseEntry key, com.sleepycat.db.DatabaseEntry data, int flags) throws com.sleepycat.db.DatabaseException { + return db_javaJNI.Dbc_put(swigCPtr, key, data, flags); + } + +} diff --git a/db/java/src/com/sleepycat/db/internal/db_java.java b/db/java/src/com/sleepycat/db/internal/db_java.java new file mode 100644 index 000000000..6bfbf64c8 --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/db_java.java @@ -0,0 +1,34 @@ +package com.sleepycat.db.internal; + +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + + +/* package */ class db_java { + public static void DbEnv_lock_vec(DbEnv dbenv, int locker, int flags, com.sleepycat.db.LockRequest[] list, int offset, int nlist) throws com.sleepycat.db.DatabaseException { + db_javaJNI.DbEnv_lock_vec(DbEnv.getCPtr(dbenv), locker, flags, list, offset, nlist); + } + + /* package */ static long initDbEnvRef0(DbEnv self, Object handle) { + return db_javaJNI.initDbEnvRef0(DbEnv.getCPtr(self), handle); + } + + /* package */ static long initDbRef0(Db self, Object handle) { + return db_javaJNI.initDbRef0(Db.getCPtr(self), handle); + } + + /* package */ static void deleteRef0(long ref) { + db_javaJNI.deleteRef0(ref); + } + + /* package */ static DbEnv getDbEnv0(Db self) { + long cPtr = db_javaJNI.getDbEnv0(Db.getCPtr(self)); + return (cPtr == 0) ? null : new DbEnv(cPtr, false); + } + +} diff --git a/db/java/src/com/sleepycat/db/internal/db_javaJNI.java b/db/java/src/com/sleepycat/db/internal/db_javaJNI.java new file mode 100644 index 000000000..4b47b3bec --- /dev/null +++ b/db/java/src/com/sleepycat/db/internal/db_javaJNI.java @@ -0,0 +1,263 @@ +package com.sleepycat.db.internal; + +/* ---------------------------------------------------------------------------- + * This file was automatically generated by SWIG (http://www.swig.org). + * Version: 1.3.21 + * + * Do not make changes to this file unless you know what you are doing--modify + * the SWIG interface file instead. + * ----------------------------------------------------------------------------- */ + + +class db_javaJNI { + + static { + /* An alternate library name can be specified via a property. */ + String libname; + + if ((libname = System.getProperty("sleepycat.db.libfile")) != null) + System.load(libname); + else if ((libname = System.getProperty("sleepycat.db.libname")) != null) + System.loadLibrary(libname); + else { + String os = System.getProperty("os.name"); + if (os != null && os.startsWith("Windows")) { + /* + * On Windows, library name is something like + * "libdb_java42.dll" or "libdb_java42d.dll". + */ + libname = "libdb_java" + + DbConstants.DB_VERSION_MAJOR + + DbConstants.DB_VERSION_MINOR; + + try { + System.loadLibrary(libname); + } catch (UnsatisfiedLinkError e) { + try { + libname += "d"; + System.loadLibrary(libname); + } catch (UnsatisfiedLinkError e2) { + throw e; + } + } + } else { + /* + * On UNIX, library name is something like + * "libdb_java-3.0.so". + */ + System.loadLibrary("db_java-" + + DbConstants.DB_VERSION_MAJOR + "." + + DbConstants.DB_VERSION_MINOR); + } + } + + initialize(); + + if (DbEnv_get_version_major() != DbConstants.DB_VERSION_MAJOR || + DbEnv_get_version_minor() != DbConstants.DB_VERSION_MINOR || + DbEnv_get_version_patch() != DbConstants.DB_VERSION_PATCH) + throw new RuntimeException("Berkeley DB library version doesn't match Java classes"); + } + + static native final void initialize(); + + public final static native void DbEnv_lock_vec(long jarg1, int jarg2, int jarg3, com.sleepycat.db.LockRequest[] jarg4, int jarg5, int jarg6) throws com.sleepycat.db.DatabaseException; + /* package */ final static native long initDbEnvRef0(long jarg1, Object jarg2); + /* package */ final static native long initDbRef0(long jarg1, Object jarg2); + /* package */ final static native void deleteRef0(long jarg1); + /* package */ final static native long getDbEnv0(long jarg1); + public final static native long new_Db(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_associate(long jarg1, long jarg2, long jarg3, com.sleepycat.db.SecondaryKeyCreator jarg4, int jarg5) throws com.sleepycat.db.DatabaseException; + /* package */ final static native int Db_close0(long jarg1, int jarg2); + public final static native long Db_cursor(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native int Db_del(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException; + public final static native void Db_err(long jarg1, int jarg2, String jarg3) /* no exception */; + public final static native void Db_errx(long jarg1, String jarg2) /* no exception */; + public final static native boolean Db_get_transactional(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.DatabaseEntry jarg4, int jarg5) throws com.sleepycat.db.DatabaseException; + public final static native boolean Db_get_byteswapped(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long Db_get_cachesize(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_cachesize_ncache(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String Db_get_filename(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String Db_get_dbname(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_encrypt_flags(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String Db_get_errpfx(long jarg1) /* no exception */; + public final static native int Db_get_flags(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_lorder(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long Db_get_mpf(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_open_flags(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_pagesize(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_bt_minkey(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_h_ffactor(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_h_nelem(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_re_delim(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_re_len(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_re_pad(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String Db_get_re_source(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_q_extentsize(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int Db_get_type(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long Db_join(long jarg1, Dbc[] jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void Db_key_range(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.KeyRange jarg4, int jarg5) throws com.sleepycat.db.DatabaseException; + public final static native void Db_open(long jarg1, long jarg2, String jarg3, String jarg4, int jarg5, int jarg6, int jarg7) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException; + public final static native int Db_pget(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.DatabaseEntry jarg4, com.sleepycat.db.DatabaseEntry jarg5, int jarg6) throws com.sleepycat.db.DatabaseException; + public final static native int Db_put(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.DatabaseEntry jarg4, int jarg5) throws com.sleepycat.db.DatabaseException; + /* package */ final static native void Db_remove0(long jarg1, String jarg2, String jarg3, int jarg4); + /* package */ final static native void Db_rename0(long jarg1, String jarg2, String jarg3, String jarg4, int jarg5); + public final static native void Db_set_append_recno(long jarg1, com.sleepycat.db.RecordNumberAppender jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_bt_compare(long jarg1, java.util.Comparator jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_bt_maxkey(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_bt_minkey(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_bt_prefix(long jarg1, com.sleepycat.db.BtreePrefixCalculator jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_cachesize(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_dup_compare(long jarg1, java.util.Comparator jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_encrypt(long jarg1, String jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_errpfx(long jarg1, String jarg2) /* no exception */; + public final static native void Db_set_feedback(long jarg1, com.sleepycat.db.FeedbackHandler jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_flags(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_h_ffactor(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_h_hash(long jarg1, com.sleepycat.db.Hasher jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_h_nelem(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_lorder(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_pagesize(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_re_delim(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_re_len(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_re_pad(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_re_source(long jarg1, String jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void Db_set_q_extentsize(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native Object Db_stat(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void Db_sync(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native int Db_truncate(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void Db_upgrade(long jarg1, String jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + /* package */ final static native boolean Db_verify0(long jarg1, String jarg2, String jarg3, java.io.OutputStream jarg4, int jarg5); + /* package */ final static native void Dbc_close0(long jarg1); + public final static native int Dbc_count(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native int Dbc_del(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native long Dbc_dup(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native int Dbc_get(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException; + public final static native int Dbc_pget(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, com.sleepycat.db.DatabaseEntry jarg3, com.sleepycat.db.DatabaseEntry jarg4, int jarg5) throws com.sleepycat.db.DatabaseException; + public final static native int Dbc_put(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException; + public final static native long new_DbEnv(int jarg1) throws com.sleepycat.db.DatabaseException; + /* package */ final static native void DbEnv_close0(long jarg1, int jarg2); + public final static native void DbEnv_dbremove(long jarg1, long jarg2, String jarg3, String jarg4, int jarg5) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException; + public final static native void DbEnv_dbrename(long jarg1, long jarg2, String jarg3, String jarg4, String jarg5, int jarg6) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException; + public final static native void DbEnv_err(long jarg1, int jarg2, String jarg3) /* no exception */; + public final static native void DbEnv_errx(long jarg1, String jarg2) /* no exception */; + public final static native String[] DbEnv_get_data_dirs(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_encrypt_flags(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String DbEnv_get_errpfx(long jarg1) /* no exception */; + public final static native int DbEnv_get_flags(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String DbEnv_get_home(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_open_flags(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long DbEnv_get_shm_key(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_tas_spins(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String DbEnv_get_tmp_dir(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native boolean DbEnv_get_verbose(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_open(long jarg1, String jarg2, int jarg3, int jarg4) throws com.sleepycat.db.DatabaseException, java.io.FileNotFoundException; + /* package */ final static native void DbEnv_remove0(long jarg1, String jarg2, int jarg3); + public final static native void DbEnv_set_cachesize(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_data_dir(long jarg1, String jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_encrypt(long jarg1, String jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_errcall(long jarg1, com.sleepycat.db.ErrorHandler jarg2) /* no exception */; + public final static native void DbEnv_set_errpfx(long jarg1, String jarg2) /* no exception */; + public final static native void DbEnv_set_flags(long jarg1, int jarg2, boolean jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_feedback(long jarg1, com.sleepycat.db.FeedbackHandler jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_mp_mmapsize(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_msgcall(long jarg1, com.sleepycat.db.MessageHandler jarg2) /* no exception */; + public final static native void DbEnv_set_paniccall(long jarg1, com.sleepycat.db.PanicHandler jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_rpc_server(long jarg1, DbClient jarg2, String jarg3, long jarg4, long jarg5, int jarg6) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_shm_key(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_tas_spins(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_timeout(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_tmp_dir(long jarg1, String jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_tx_max(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_app_dispatch(long jarg1, com.sleepycat.db.LogRecordHandler jarg2) throws com.sleepycat.db.DatabaseException; + /* package */ final static native void DbEnv_set_tx_timestamp0(long jarg1, long jarg2); + public final static native void DbEnv_set_verbose(long jarg1, int jarg2, boolean jarg3) throws com.sleepycat.db.DatabaseException; + public final static native byte[][] DbEnv_get_lk_conflicts(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_lk_detect(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_lk_max_locks(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_lk_max_lockers(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_lk_max_objects(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_lock_detect(long jarg1, int jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native long DbEnv_lock_get(long jarg1, int jarg2, int jarg3, com.sleepycat.db.DatabaseEntry jarg4, int jarg5) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_lock_id(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_lock_id_free(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_lock_put(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException; + public final static native com.sleepycat.db.LockStats DbEnv_lock_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lk_conflicts(long jarg1, byte[][] jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lk_detect(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lk_max_lockers(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lk_max_locks(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lk_max_objects(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_lg_bsize(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String DbEnv_get_lg_dir(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_lg_max(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_lg_regionmax(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native String[] DbEnv_log_archive(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_log_compare(com.sleepycat.db.LogSequenceNumber jarg1, com.sleepycat.db.LogSequenceNumber jarg2) /* no exception */; + public final static native long DbEnv_log_cursor(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native String DbEnv_log_file(long jarg1, com.sleepycat.db.LogSequenceNumber jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_log_flush(long jarg1, com.sleepycat.db.LogSequenceNumber jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_log_put(long jarg1, com.sleepycat.db.LogSequenceNumber jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException; + public final static native com.sleepycat.db.LogStats DbEnv_log_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lg_bsize(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lg_dir(long jarg1, String jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lg_max(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_lg_regionmax(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native long DbEnv_get_cachesize(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_cachesize_ncache(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long DbEnv_get_mp_mmapsize(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native com.sleepycat.db.CacheStats DbEnv_memp_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native com.sleepycat.db.CacheFileStats[] DbEnv_memp_fstat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_memp_trickle(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_get_tx_max(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long DbEnv_get_tx_timestamp(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long DbEnv_get_timeout(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native long DbEnv_txn_begin(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_txn_checkpoint(long jarg1, int jarg2, int jarg3, int jarg4) throws com.sleepycat.db.DatabaseException; + public final static native com.sleepycat.db.PreparedTransaction[] DbEnv_txn_recover(long jarg1, int jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native com.sleepycat.db.TransactionStats DbEnv_txn_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native long DbEnv_get_rep_limit(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_rep_elect(long jarg1, int jarg2, int jarg3, int jarg4, int jarg5, int jarg6) throws com.sleepycat.db.DatabaseException; + public final static native int DbEnv_rep_process_message(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, com.sleepycat.db.DatabaseEntry jarg3, DbEnv.RepProcessMessage jarg4, com.sleepycat.db.LogSequenceNumber jarg5) /* no exception */; + public final static native void DbEnv_rep_start(long jarg1, com.sleepycat.db.DatabaseEntry jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; + public final static native com.sleepycat.db.ReplicationStats DbEnv_rep_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_rep_limit(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbEnv_set_rep_transport(long jarg1, int jarg2, com.sleepycat.db.ReplicationTransport jarg3) throws com.sleepycat.db.DatabaseException; + public final static native String DbEnv_strerror(int jarg1) /* no exception */; + public final static native int DbEnv_get_version_major() /* no exception */; + public final static native int DbEnv_get_version_minor() /* no exception */; + public final static native int DbEnv_get_version_patch() /* no exception */; + public final static native String DbEnv_get_version_string() /* no exception */; + /* package */ final static native void delete_DbLock(long jarg1); + /* package */ final static native void DbLogc_close0(long jarg1, int jarg2); + public final static native int DbLogc_get(long jarg1, com.sleepycat.db.LogSequenceNumber jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException; + public final static native int DbMpoolFile_get_priority(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native void DbMpoolFile_set_priority(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native int DbMpoolFile_get_flags(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native void DbMpoolFile_set_flags(long jarg1, int jarg2, boolean jarg3) throws com.sleepycat.db.DatabaseException; + public final static native long DbMpoolFile_get_maxsize(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native void DbMpoolFile_set_maxsize(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException; + public final static native long new_DbSequence(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + /* package */ final static native void DbSequence_close0(long jarg1, int jarg2); + public final static native long DbSequence_get(long jarg1, long jarg2, int jarg3, int jarg4) throws com.sleepycat.db.DatabaseException; + public final static native int DbSequence_get_cachesize(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long DbSequence_get_db(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native int DbSequence_get_flags(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native void DbSequence_get_key(long jarg1, com.sleepycat.db.DatabaseEntry jarg2) throws com.sleepycat.db.DatabaseException; + public final static native long DbSequence_get_range_min(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native long DbSequence_get_range_max(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native void DbSequence_initial_value(long jarg1, long jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbSequence_open(long jarg1, long jarg2, com.sleepycat.db.DatabaseEntry jarg3, int jarg4) throws com.sleepycat.db.DatabaseException; + /* package */ final static native void DbSequence_remove0(long jarg1, long jarg2, int jarg3); + public final static native void DbSequence_set_cachesize(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbSequence_set_flags(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbSequence_set_range(long jarg1, long jarg2, long jarg3) throws com.sleepycat.db.DatabaseException; + public final static native com.sleepycat.db.SequenceStats DbSequence_stat(long jarg1, int jarg2) throws com.sleepycat.db.DatabaseException; + /* package */ final static native void DbTxn_abort0(long jarg1); + /* package */ final static native void DbTxn_commit0(long jarg1, int jarg2); + /* package */ final static native void DbTxn_discard0(long jarg1, int jarg2); + public final static native int DbTxn_id(long jarg1) throws com.sleepycat.db.DatabaseException; + public final static native void DbTxn_prepare(long jarg1, byte[] jarg2) throws com.sleepycat.db.DatabaseException; + public final static native void DbTxn_set_timeout(long jarg1, long jarg2, int jarg3) throws com.sleepycat.db.DatabaseException; +} diff --git a/db/java/src/com/sleepycat/db/package.html b/db/java/src/com/sleepycat/db/package.html index 73f58df1d..fe77f9223 100644 --- a/db/java/src/com/sleepycat/db/package.html +++ b/db/java/src/com/sleepycat/db/package.html @@ -1,7 +1,29 @@ -<!-- $Id: package.html,v 1.4 2003/10/28 12:49:47 gburd Exp $ --> +<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> <html> +<head> +<!-- + + See the file LICENSE for redistribution information. + + Copyright (c) 2002-2004 + Sleepycat Software. All rights reserved. + + $Id: package.html,v 1.6 2004/08/02 18:52:03 mjc Exp $ + +--> +</head> <body> -<p>Java API programming notes<br> -<a href="{@docRoot}../ref/java/program.html" target="_top">[reference guide]</a> +Berkeley DB Java API<br> +<a href="{@docRoot}/%2e%2e/ref/toc.html" target="_top">[reference guide]</a> <a href="{@docRoot}/%2e%2e/ref/java/program%2ehtml" target="_top">[Java programming notes]</a>. +<p> +This package is a wrapper around the Berkeley DB library. It uses JNI +to provide access to Berkeley DB, which is implemented in C. That means +that a shared library or DLL must be available when applications use +this package. +<p> +There are also several utilities provided with Berkeley DB that make +administrative tasks possible from the command line. For more +information, see the page <a href="{@docRoot}/../utility/index.html" +target="_top">Berkeley DB Supporting Utilities</a>. </body> </html> diff --git a/db/java/src/com/sleepycat/util/ExceptionUnwrapper.java b/db/java/src/com/sleepycat/util/ExceptionUnwrapper.java new file mode 100644 index 000000000..ee7e8ceb4 --- /dev/null +++ b/db/java/src/com/sleepycat/util/ExceptionUnwrapper.java @@ -0,0 +1,69 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ExceptionUnwrapper.java,v 1.1 2004/04/09 16:34:10 mark Exp $ + */ + +package com.sleepycat.util; + +/** + * Unwraps nested exceptions by calling the {@link + * ExceptionWrapper#getDetail()} method for exceptions that implement the + * {@link ExceptionWrapper} interface. Does not currently support the Java 1.4 + * <code>Throwable.getDetail()</code> method. + * + * @author Mark Hayes + */ +public class ExceptionUnwrapper { + + /** + * Unwraps an Exception and returns the underlying Exception, or throws an + * Error if the underlying Throwable is an Error. + * + * @param e is the Exception to unwrap. + * + * @return the underlying Exception. + * + * @throws Error if the underlying Throwable is an Error. + * + * @throws IllegalArgumentException if the underlying Throwable is not an + * Exception or an Error. + */ + public static Exception unwrap(Exception e) { + + Throwable t = unwrapAny(e); + if (t instanceof Exception) { + return (Exception) t; + } else if (t instanceof Error) { + throw (Error) t; + } else { + throw new IllegalArgumentException("Not Exception or Error: " + t); + } + } + + /** + * Unwraps an Exception and returns the underlying Throwable. + * + * @param e is the Exception to unwrap. + * + * @return the underlying Throwable. + */ + public static Throwable unwrapAny(Throwable e) { + + while (true) { + if (e instanceof ExceptionWrapper) { + Throwable e2 = ((ExceptionWrapper) e).getDetail(); + if (e2 == null) { + return e; + } else { + e = e2; + } + } else { + return e; + } + } + } +} diff --git a/db/java/src/com/sleepycat/util/ExceptionWrapper.java b/db/java/src/com/sleepycat/util/ExceptionWrapper.java new file mode 100644 index 000000000..4917fdc2b --- /dev/null +++ b/db/java/src/com/sleepycat/util/ExceptionWrapper.java @@ -0,0 +1,25 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: ExceptionWrapper.java,v 1.1 2004/04/09 16:34:10 mark Exp $ + */ + +package com.sleepycat.util; + +/** + * Interface implemented by exceptions that can contain nested exceptions. + * + * @author Mark Hayes + */ +public interface ExceptionWrapper { + + /** + * Returns the nested exception or null if none is present. + * + * @return the nested exception or null if none is present. + */ + Throwable getDetail(); +} diff --git a/db/java/src/com/sleepycat/util/FastInputStream.java b/db/java/src/com/sleepycat/util/FastInputStream.java new file mode 100644 index 000000000..6bad7d79a --- /dev/null +++ b/db/java/src/com/sleepycat/util/FastInputStream.java @@ -0,0 +1,179 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: FastInputStream.java,v 1.2 2004/06/04 18:24:50 mark Exp $ + */ + +package com.sleepycat.util; + +import java.io.IOException; +import java.io.InputStream; + +/** + * A replacement for ByteArrayInputStream that does not synchronize every + * byte read. + * + * <p>This class extends {@link InputStream} and its <code>read()</code> + * methods allow it to be used as a standard input stream. In addition, it + * provides <code>readFast()</code> methods that are not declared to throw + * <code>IOException</code>. <code>IOException</code> is never thrown by this + * class.</p> + * + * @author Mark Hayes + */ +public class FastInputStream extends InputStream { + + protected int len; + protected int off; + protected int mark; + protected byte[] buf; + + /** + * Creates an input stream. + * + * @param buffer the data to read. + */ + public FastInputStream(byte[] buffer) { + + buf = buffer; + len = buffer.length; + } + + /** + * Creates an input stream. + * + * @param buffer the data to read. + * + * @param offset the byte offset at which to begin reading. + * + * @param length the number of bytes to read. + */ + public FastInputStream(byte[] buffer, int offset, int length) { + + buf = buffer; + off = offset; + len = length; + } + + // --- begin ByteArrayInputStream compatible methods --- + + public int available() { + + return len - off; + } + + public boolean markSupported() { + + return true; + } + + public void mark(int pos) { + + mark = pos; + } + + public void reset() { + + off = mark; + } + + public long skip(long count) { + + int myCount = (int) count; + if (myCount + off > len) { + myCount = len - off; + } + off += myCount; + return myCount; + } + + public int read() throws IOException { + + return readFast(); + } + + public int read(byte[] toBuf) throws IOException { + + return readFast(toBuf, 0, toBuf.length); + } + + public int read(byte[] toBuf, int offset, int length) throws IOException { + + return readFast(toBuf, offset, length); + } + + // --- end ByteArrayInputStream compatible methods --- + + /** + * Equivalent to <code>read()<code> but does not throw + * <code>IOException</code>. + * @see #read() + */ + public final int readFast() { + + return (off < len) ? (buf[off++] & 0xff) : (-1); + } + + /** + * Equivalent to <code>read(byte[])<code> but does not throw + * <code>IOException</code>. + * @see #read(byte[]) + */ + public final int readFast(byte[] toBuf) { + + return readFast(toBuf, 0, toBuf.length); + } + + /** + * Equivalent to <code>read(byte[],int,int)<code> but does not throw + * <code>IOException</code>. + * @see #read(byte[],int,int) + */ + public final int readFast(byte[] toBuf, int offset, int length) { + + int avail = len - off; + if (avail <= 0) { + return -1; + } + if (length > avail) { + length = avail; + } + for (int i = 0; i < length; i++) { + toBuf[offset++] = buf[off++]; + } + return length; + } + + /** + * Returns the underlying data being read. + * + * @return the underlying data. + */ + public final byte[] getBufferBytes() { + + return buf; + } + + /** + * Returns the offset at which data is being read from the buffer. + * + * @return the offset at which data is being read. + */ + public final int getBufferOffset() { + + return off; + } + + /** + * Returns the end of the buffer being read. + * + * @return the end of the buffer. + */ + public final int getBufferLength() { + + return len; + } +} diff --git a/db/java/src/com/sleepycat/util/FastOutputStream.java b/db/java/src/com/sleepycat/util/FastOutputStream.java new file mode 100644 index 000000000..a0984bfe4 --- /dev/null +++ b/db/java/src/com/sleepycat/util/FastOutputStream.java @@ -0,0 +1,278 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: FastOutputStream.java,v 1.3 2004/07/03 16:15:36 mark Exp $ + */ + +package com.sleepycat.util; + +import java.io.IOException; +import java.io.OutputStream; +import java.io.UnsupportedEncodingException; + +/** + * A replacement for ByteArrayOutputStream that does not synchronize every + * byte read. + * + * <p>This class extends {@link OutputStream} and its <code>write()</code> + * methods allow it to be used as a standard output stream. In addition, it + * provides <code>writeFast()</code> methods that are not declared to throw + * <code>IOException</code>. <code>IOException</code> is never thrown by this + * class.</p> + * + * @author Mark Hayes + */ +public class FastOutputStream extends OutputStream { + + public static final int DEFAULT_INIT_SIZE = 100; + public static final int DEFAULT_BUMP_SIZE = 100; + + private int len; + private int bumpLen; + private byte[] buf; + + /** + * Creates an output stream with default sizes. + */ + public FastOutputStream() { + + initBuffer(DEFAULT_INIT_SIZE, DEFAULT_BUMP_SIZE); + } + + /** + * Creates an output stream with a default bump size and a given initial + * size. + * + * @param initialSize the initial size of the buffer. + */ + public FastOutputStream(int initialSize) { + + initBuffer(initialSize, DEFAULT_BUMP_SIZE); + } + + /** + * Creates an output stream with a given bump size and initial size. + * + * @param initialSize the initial size of the buffer. + * + * @param bumpSize the amount to increment the buffer. + */ + public FastOutputStream(int initialSize, int bumpSize) { + + initBuffer(initialSize, bumpSize); + } + + /** + * Creates an output stream with a given initial buffer and a default + * bump size. + * + * @param buffer the initial buffer; will be owned by this object. + */ + public FastOutputStream(byte[] buffer) { + + buf = buffer; + bumpLen = DEFAULT_BUMP_SIZE; + } + + /** + * Creates an output stream with a given initial buffer and a given + * bump size. + * + * @param buffer the initial buffer; will be owned by this object. + * + * @param bumpSize the amount to increment the buffer. + */ + public FastOutputStream(byte[] buffer, int bumpSize) { + + buf = buffer; + bumpLen = bumpSize; + } + + private void initBuffer(int bufferSize, int bumpLen) { + buf = new byte[bufferSize]; + this.bumpLen = bumpLen; + } + + // --- begin ByteArrayOutputStream compatible methods --- + + public int size() { + + return len; + } + + public void reset() { + + len = 0; + } + + public void write(int b) throws IOException { + + writeFast(b); + } + + public void write(byte[] fromBuf) throws IOException { + + writeFast(fromBuf); + } + + public void write(byte[] fromBuf, int offset, int length) + throws IOException { + + writeFast(fromBuf, offset, length); + } + + public void writeTo(OutputStream out) throws IOException { + + out.write(buf, 0, len); + } + + public String toString() { + + return new String(buf, 0, len); + } + + public String toString(String encoding) + throws UnsupportedEncodingException { + + return new String(buf, 0, len, encoding); + } + + public byte[] toByteArray() { + + byte[] toBuf = new byte[len]; + + for (int i = 0; i < len; i++) + toBuf[i] = buf[i]; + + return toBuf; + } + + // --- end ByteArrayOutputStream compatible methods --- + + /** + * Equivalent to <code>write(int)<code> but does not throw + * <code>IOException</code>. + * @see #write(int) + */ + public final void writeFast(int b) { + + if (len + 1 > buf.length) + bump(1); + + buf[len++] = (byte) b; + } + + /** + * Equivalent to <code>write(byte[])<code> but does not throw + * <code>IOException</code>. + * @see #write(byte[]) + */ + public final void writeFast(byte[] fromBuf) { + + int needed = len + fromBuf.length - buf.length; + if (needed > 0) + bump(needed); + + for (int i = 0; i < fromBuf.length; i++) + buf[len++] = fromBuf[i]; + } + + /** + * Equivalent to <code>write(byte[],int,int)<code> but does not throw + * <code>IOException</code>. + * @see #write(byte[],int,int) + */ + public final void writeFast(byte[] fromBuf, int offset, int length) { + + int needed = len + length - buf.length; + if (needed > 0) + bump(needed); + + int fromLen = offset + length; + + for (int i = offset; i < fromLen; i++) + buf[len++] = fromBuf[i]; + } + + /** + * Copy the buffered data to the given array. + * + * @param toBuf the buffer to hold a copy of the data. + * + * @param offset the offset at which to start copying. + */ + public void toByteArray(byte[] toBuf, int offset) { + + int toLen = (toBuf.length > len) ? len : toBuf.length; + + for (int i = offset; i < toLen; i++) + toBuf[i] = buf[i]; + } + + /** + * Returns the buffer owned by this object. + * + * @return the buffer. + */ + public byte[] getBufferBytes() { + + return buf; + } + + /** + * Returns the offset of the internal buffer. + * + * @return always zero currently. + */ + public int getBufferOffset() { + + return 0; + } + + /** + * Returns the length used in the internal buffer, i.e., the offset at + * which data will be written next. + * + * @return the buffer length. + */ + public int getBufferLength() { + + return len; + } + + /** + * Ensure that at least the given number of bytes are available in the + * internal buffer. + * + * @param sizeNeeded the number of bytes desired. + */ + public void makeSpace(int sizeNeeded) { + + int needed = len + sizeNeeded - buf.length; + if (needed > 0) + bump(needed); + } + + /** + * Skip the given number of bytes in the buffer. + * + * @param sizeAdded number of bytes to skip. + */ + public void addSize(int sizeAdded) { + + len += sizeAdded; + } + + private void bump(int needed) { + + byte[] toBuf = new byte[buf.length + needed + bumpLen]; + + for (int i = 0; i < len; i++) + toBuf[i] = buf[i]; + + buf = toBuf; + } +} diff --git a/db/java/src/com/sleepycat/util/IOExceptionWrapper.java b/db/java/src/com/sleepycat/util/IOExceptionWrapper.java new file mode 100644 index 000000000..76b409829 --- /dev/null +++ b/db/java/src/com/sleepycat/util/IOExceptionWrapper.java @@ -0,0 +1,34 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: IOExceptionWrapper.java,v 1.1 2004/04/09 16:34:10 mark Exp $ + */ + +package com.sleepycat.util; + +import java.io.IOException; + +/** + * An IOException that can contain nested exceptions. + * + * @author Mark Hayes + */ +public class IOExceptionWrapper + extends IOException implements ExceptionWrapper { + + private Throwable e; + + public IOExceptionWrapper(Throwable e) { + + super(e.getMessage()); + this.e = e; + } + + public Throwable getDetail() { + + return e; + } +} diff --git a/db/java/src/com/sleepycat/util/RuntimeExceptionWrapper.java b/db/java/src/com/sleepycat/util/RuntimeExceptionWrapper.java new file mode 100644 index 000000000..f40f1ff7a --- /dev/null +++ b/db/java/src/com/sleepycat/util/RuntimeExceptionWrapper.java @@ -0,0 +1,32 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: RuntimeExceptionWrapper.java,v 1.1 2004/04/09 16:34:10 mark Exp $ + */ + +package com.sleepycat.util; + +/** + * A RuntimeException that can contain nested exceptions. + * + * @author Mark Hayes + */ +public class RuntimeExceptionWrapper extends RuntimeException + implements ExceptionWrapper { + + private Throwable e; + + public RuntimeExceptionWrapper(Throwable e) { + + super(e.getMessage()); + this.e = e; + } + + public Throwable getDetail() { + + return e; + } +} diff --git a/db/java/src/com/sleepycat/util/UtfOps.java b/db/java/src/com/sleepycat/util/UtfOps.java new file mode 100644 index 000000000..19c8a9ee3 --- /dev/null +++ b/db/java/src/com/sleepycat/util/UtfOps.java @@ -0,0 +1,281 @@ +/*- + * See the file LICENSE for redistribution information. + * + * Copyright (c) 2000-2004 + * Sleepycat Software. All rights reserved. + * + * $Id: UtfOps.java,v 1.2 2004/06/04 18:24:51 mark Exp $ + */ + +package com.sleepycat.util; + +/** + * UTF operations with more flexibility than is provided by DataInput and + * DataOutput. + * + * @author Mark Hayes + */ +public class UtfOps { + + private static byte[] EMPTY_BYTES = {}; + private static String EMPTY_STRING = ""; + + /** + * Returns the byte length of a null terminated UTF string, not including + * the terminator. + * + * @param bytes the data containing the UTF string. + * + * @param offset the beginning of the string the measure. + * + * @throws IndexOutOfBoundsException if no zero terminator is found. + * + * @return the number of bytes. + */ + public static int getZeroTerminatedByteLength(byte[] bytes, int offset) + throws IndexOutOfBoundsException { + + int len = 0; + while (bytes[offset++] != 0) { + len++; + } + return len; + } + + /** + * Returns the byte length of the UTF string that would be created by + * converting the given characters to UTF. + * + * @param chars the characters that would be converted. + * + * @return the byte length of the equivalent UTF data. + */ + public static int getByteLength(char[] chars) { + + return getByteLength(chars, 0, chars.length); + } + + /** + * Returns the byte length of the UTF string that would be created by + * converting the given characters to UTF. + * + * @param chars the characters that would be converted. + * + * @param offset the first character to be converted. + * + * @param length the number of characters to be converted. + * + * @return the byte length of the equivalent UTF data. + */ + public static int getByteLength(char[] chars, int offset, int length) { + + int len = 0; + length += offset; + for (int i = offset; i < length; i++) { + int c = chars[i]; + if ((c >= 0x0001) && (c <= 0x007F)) { + len++; + } else if (c > 0x07FF) { + len += 3; + } else { + len += 2; + } + } + return len; + } + + /** + * Returns the number of characters represented by the given UTF string. + * + * @param bytes the UTF string. + * + * @return the number of characters. + * + * @throws IndexOutOfBoundsException if a UTF character sequence at the end + * of the data is not complete. + * + * @throws IllegalArgumentException if an illegal UTF sequence is + * encountered. + */ + public static int getCharLength(byte[] bytes) + throws IllegalArgumentException, IndexOutOfBoundsException { + + return getCharLength(bytes, 0, bytes.length); + } + + /** + * Returns the number of characters represented by the given UTF string. + * + * @param bytes the data containing the UTF string. + * + * @param offset the first byte to be converted. + * + * @param length the number of byte to be converted. + * + * @throws IndexOutOfBoundsException if a UTF character sequence at the end + * of the data is not complete. + * + * @throws IllegalArgumentException if an illegal UTF sequence is + * encountered. + */ + public static int getCharLength(byte[] bytes, int offset, int length) + throws IllegalArgumentException, IndexOutOfBoundsException { + + int charCount = 0; + length += offset; + while (offset < length) { + switch ((bytes[offset] & 0xff) >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + offset++; + break; + case 12: case 13: + offset += 2; + break; + case 14: + offset += 3; + break; + default: + throw new IllegalArgumentException(); + } + charCount++; + } + return charCount; + } + + /** + * Converts byte arrays into character arrays. + * + * @param bytes the source byte data to convert + * + * @param byteOffset the offset into the byte array at which + * to start the conversion + * + * @param chars the destination array + * + * @param charOffset the offset into chars at which to begin the copy + * + * @param len the amount of information to copy into chars + * + * @param isByteLen if true then len is a measure of bytes, otherwise + * len is a measure of characters + * + * @throws IndexOutOfBoundsException if a UTF character sequence at the end + * of the data is not complete. + * + * @throws IllegalArgumentException if an illegal UTF sequence is + * encountered. + */ + public static int bytesToChars(byte[] bytes, int byteOffset, + char[] chars, int charOffset, + int len, boolean isByteLen) + throws IllegalArgumentException, IndexOutOfBoundsException { + + int char1, char2, char3; + len += isByteLen ? byteOffset : charOffset; + while ((isByteLen ? byteOffset : charOffset) < len) { + char1 = bytes[byteOffset++] & 0xff; + switch ((char1 & 0xff) >> 4) { + case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: + chars[charOffset++] = (char) char1; + break; + case 12: case 13: + char2 = bytes[byteOffset++]; + if ((char2 & 0xC0) != 0x80) { + throw new IllegalArgumentException(); + } + chars[charOffset++] = (char)(((char1 & 0x1F) << 6) | + (char2 & 0x3F)); + break; + case 14: + char2 = bytes[byteOffset++]; + char3 = bytes[byteOffset++]; + if (((char2 & 0xC0) != 0x80) || ((char3 & 0xC0) != 0x80)) + throw new IllegalArgumentException(); + chars[charOffset++] = (char)(((char1 & 0x0F) << 12) | + ((char2 & 0x3F) << 6) | + ((char3 & 0x3F) << 0)); + break; + default: + throw new IllegalArgumentException(); + } + } + return byteOffset; + } + + /** + * Converts character arrays into byte arrays. + * + * @param chars the source character data to convert + * + * @param charOffset the offset into the character array at which + * to start the conversion + * + * @param bytes the destination array + * + * @param byteOffset the offset into bytes at which to begin the copy + * + * @param charLength the length of characters to copy into bytes + */ + public static void charsToBytes(char[] chars, int charOffset, + byte[] bytes, int byteOffset, + int charLength) { + charLength += charOffset; + for (int i = charOffset; i < charLength; i++) { + int c = chars[i]; + if ((c >= 0x0001) && (c <= 0x007F)) { + bytes[byteOffset++] = (byte) c; + } else if (c > 0x07FF) { + bytes[byteOffset++] = (byte) (0xE0 | ((c >> 12) & 0x0F)); + bytes[byteOffset++] = (byte) (0x80 | ((c >> 6) & 0x3F)); + bytes[byteOffset++] = (byte) (0x80 | ((c >> 0) & 0x3F)); + } else { + bytes[byteOffset++] = (byte) (0xC0 | ((c >> 6) & 0x1F)); + bytes[byteOffset++] = (byte) (0x80 | ((c >> 0) & 0x3F)); + } + } + } + + /** + * Converts byte arrays into strings. + * + * @param bytes the source byte data to convert + * + * @param offset the offset into the byte array at which + * to start the conversion + * + * @param length the number of bytes to be converted. + * + * @return the string. + * + * @throws IndexOutOfBoundsException if a UTF character sequence at the end + * of the data is not complete. + * + * @throws IllegalArgumentException if an illegal UTF sequence is + * encountered. + */ + public static String bytesToString(byte[] bytes, int offset, int length) + throws IllegalArgumentException, IndexOutOfBoundsException { + + if (length == 0) return EMPTY_STRING; + int charLen = UtfOps.getCharLength(bytes, offset, length); + char[] chars = new char[charLen]; + UtfOps.bytesToChars(bytes, offset, chars, 0, length, true); + return new String(chars, 0, charLen); + } + + /** + * Converts strings to byte arrays. + * + * @param string the string to convert. + * + * @return the UTF byte array. + */ + public static byte[] stringToBytes(String string) { + + if (string.length() == 0) return EMPTY_BYTES; + char[] chars = string.toCharArray(); + byte[] bytes = new byte[UtfOps.getByteLength(chars)]; + UtfOps.charsToBytes(chars, 0, bytes, 0, chars.length); + return bytes; + } +} diff --git a/db/java/src/com/sleepycat/util/package.html b/db/java/src/com/sleepycat/util/package.html new file mode 100644 index 000000000..83fae5ca9 --- /dev/null +++ b/db/java/src/com/sleepycat/util/package.html @@ -0,0 +1,6 @@ +<!-- $Id: package.html,v 1.1 2004/08/02 18:52:06 mjc Exp $ --> +<html> +<body> +General utilities used throughout Berkeley DB. +</body> +</html> |