summaryrefslogtreecommitdiff
path: root/examples_java
diff options
context:
space:
mode:
Diffstat (limited to 'examples_java')
-rw-r--r--examples_java/src/collections/access/AccessExample.java288
-rw-r--r--examples_java/src/collections/hello/HelloDatabaseWorld.java156
-rw-r--r--examples_java/src/collections/ship/basic/PartData.java64
-rw-r--r--examples_java/src/collections/ship/basic/PartKey.java40
-rw-r--r--examples_java/src/collections/ship/basic/Sample.java254
-rw-r--r--examples_java/src/collections/ship/basic/SampleDatabase.java134
-rw-r--r--examples_java/src/collections/ship/basic/SampleViews.java122
-rw-r--r--examples_java/src/collections/ship/basic/ShipmentData.java41
-rw-r--r--examples_java/src/collections/ship/basic/ShipmentKey.java48
-rw-r--r--examples_java/src/collections/ship/basic/SupplierData.java57
-rw-r--r--examples_java/src/collections/ship/basic/SupplierKey.java40
-rw-r--r--examples_java/src/collections/ship/basic/Weight.java49
-rw-r--r--examples_java/src/collections/ship/entity/Part.java72
-rw-r--r--examples_java/src/collections/ship/entity/PartData.java65
-rw-r--r--examples_java/src/collections/ship/entity/PartKey.java40
-rw-r--r--examples_java/src/collections/ship/entity/Sample.java236
-rw-r--r--examples_java/src/collections/ship/entity/SampleDatabase.java331
-rw-r--r--examples_java/src/collections/ship/entity/SampleViews.java306
-rw-r--r--examples_java/src/collections/ship/entity/Shipment.java55
-rw-r--r--examples_java/src/collections/ship/entity/ShipmentData.java42
-rw-r--r--examples_java/src/collections/ship/entity/ShipmentKey.java48
-rw-r--r--examples_java/src/collections/ship/entity/Supplier.java63
-rw-r--r--examples_java/src/collections/ship/entity/SupplierData.java58
-rw-r--r--examples_java/src/collections/ship/entity/SupplierKey.java40
-rw-r--r--examples_java/src/collections/ship/entity/Weight.java49
-rw-r--r--examples_java/src/collections/ship/factory/Part.java106
-rw-r--r--examples_java/src/collections/ship/factory/PartKey.java60
-rw-r--r--examples_java/src/collections/ship/factory/Sample.java234
-rw-r--r--examples_java/src/collections/ship/factory/SampleDatabase.java226
-rw-r--r--examples_java/src/collections/ship/factory/SampleViews.java142
-rw-r--r--examples_java/src/collections/ship/factory/Shipment.java102
-rw-r--r--examples_java/src/collections/ship/factory/ShipmentKey.java70
-rw-r--r--examples_java/src/collections/ship/factory/Supplier.java108
-rw-r--r--examples_java/src/collections/ship/factory/SupplierKey.java60
-rw-r--r--examples_java/src/collections/ship/factory/Weight.java49
-rw-r--r--examples_java/src/collections/ship/index/PartData.java64
-rw-r--r--examples_java/src/collections/ship/index/PartKey.java40
-rw-r--r--examples_java/src/collections/ship/index/Sample.java278
-rw-r--r--examples_java/src/collections/ship/index/SampleDatabase.java331
-rw-r--r--examples_java/src/collections/ship/index/SampleViews.java161
-rw-r--r--examples_java/src/collections/ship/index/ShipmentData.java41
-rw-r--r--examples_java/src/collections/ship/index/ShipmentKey.java48
-rw-r--r--examples_java/src/collections/ship/index/SupplierData.java57
-rw-r--r--examples_java/src/collections/ship/index/SupplierKey.java40
-rw-r--r--examples_java/src/collections/ship/index/Weight.java49
-rw-r--r--examples_java/src/collections/ship/marshal/MarshalledEnt.java42
-rw-r--r--examples_java/src/collections/ship/marshal/MarshalledKey.java36
-rw-r--r--examples_java/src/collections/ship/marshal/Part.java116
-rw-r--r--examples_java/src/collections/ship/marshal/PartKey.java59
-rw-r--r--examples_java/src/collections/ship/marshal/Sample.java236
-rw-r--r--examples_java/src/collections/ship/marshal/SampleDatabase.java260
-rw-r--r--examples_java/src/collections/ship/marshal/SampleViews.java276
-rw-r--r--examples_java/src/collections/ship/marshal/Shipment.java113
-rw-r--r--examples_java/src/collections/ship/marshal/ShipmentKey.java69
-rw-r--r--examples_java/src/collections/ship/marshal/Supplier.java118
-rw-r--r--examples_java/src/collections/ship/marshal/SupplierKey.java59
-rw-r--r--examples_java/src/collections/ship/marshal/Weight.java49
-rw-r--r--examples_java/src/collections/ship/sentity/Part.java90
-rw-r--r--examples_java/src/collections/ship/sentity/PartKey.java38
-rw-r--r--examples_java/src/collections/ship/sentity/Sample.java249
-rw-r--r--examples_java/src/collections/ship/sentity/SampleDatabase.java323
-rw-r--r--examples_java/src/collections/ship/sentity/SampleViews.java419
-rw-r--r--examples_java/src/collections/ship/sentity/Shipment.java75
-rw-r--r--examples_java/src/collections/ship/sentity/ShipmentKey.java46
-rw-r--r--examples_java/src/collections/ship/sentity/Supplier.java82
-rw-r--r--examples_java/src/collections/ship/sentity/SupplierKey.java38
-rw-r--r--examples_java/src/collections/ship/sentity/Weight.java49
-rw-r--r--examples_java/src/collections/ship/tuple/Part.java72
-rw-r--r--examples_java/src/collections/ship/tuple/PartData.java65
-rw-r--r--examples_java/src/collections/ship/tuple/PartKey.java38
-rw-r--r--examples_java/src/collections/ship/tuple/Sample.java235
-rw-r--r--examples_java/src/collections/ship/tuple/SampleDatabase.java323
-rw-r--r--examples_java/src/collections/ship/tuple/SampleViews.java396
-rw-r--r--examples_java/src/collections/ship/tuple/Shipment.java55
-rw-r--r--examples_java/src/collections/ship/tuple/ShipmentData.java42
-rw-r--r--examples_java/src/collections/ship/tuple/ShipmentKey.java46
-rw-r--r--examples_java/src/collections/ship/tuple/Supplier.java63
-rw-r--r--examples_java/src/collections/ship/tuple/SupplierData.java58
-rw-r--r--examples_java/src/collections/ship/tuple/SupplierKey.java38
-rw-r--r--examples_java/src/collections/ship/tuple/Weight.java49
-rw-r--r--examples_java/src/db/AccessExample.java182
-rw-r--r--examples_java/src/db/BtRecExample.java287
-rw-r--r--examples_java/src/db/BulkAccessExample.java161
-rw-r--r--examples_java/src/db/BulkAccessNIOExample.java180
-rw-r--r--examples_java/src/db/EnvExample.java144
-rw-r--r--examples_java/src/db/GettingStarted/ExampleDatabaseLoad.java231
-rw-r--r--examples_java/src/db/GettingStarted/ExampleDatabaseRead.java205
-rw-r--r--examples_java/src/db/GettingStarted/Inventory.java70
-rw-r--r--examples_java/src/db/GettingStarted/InventoryBinding.java54
-rw-r--r--examples_java/src/db/GettingStarted/ItemNameKeyCreator.java45
-rw-r--r--examples_java/src/db/GettingStarted/MyDbs.java165
-rw-r--r--examples_java/src/db/GettingStarted/Vendor.java90
-rw-r--r--examples_java/src/db/GettingStarted/inventory.txt800
-rw-r--r--examples_java/src/db/GettingStarted/vendors.txt6
-rw-r--r--examples_java/src/db/LockExample.java226
-rw-r--r--examples_java/src/db/SequenceExample.java92
-rw-r--r--examples_java/src/db/TpcbExample.java789
-rw-r--r--examples_java/src/db/repquote/RepConfig.java111
-rw-r--r--examples_java/src/db/repquote/RepQuoteEnvironment.java59
-rw-r--r--examples_java/src/db/repquote/RepQuoteExample.java669
-rw-r--r--examples_java/src/db/repquote/RepRemoteHost.java29
-rw-r--r--examples_java/src/db/repquote_gsg/RepConfig.java105
-rw-r--r--examples_java/src/db/repquote_gsg/RepQuoteEnvironment.java40
-rw-r--r--examples_java/src/db/repquote_gsg/RepQuoteExampleGSG.java383
-rw-r--r--examples_java/src/db/repquote_gsg/SimpleConfig.java32
-rw-r--r--examples_java/src/db/repquote_gsg/SimpleTxn.java229
-rw-r--r--examples_java/src/db/txn/DBWriter.java213
-rw-r--r--examples_java/src/db/txn/PayloadData.java27
-rw-r--r--examples_java/src/db/txn/TxnGuide.java181
-rw-r--r--examples_java/src/db/txn/TxnGuideInMemory.java172
-rw-r--r--examples_java/src/persist/CustomKeyOrderExample.java126
-rw-r--r--examples_java/src/persist/DplDump.java148
-rw-r--r--examples_java/src/persist/EventExample.java424
-rw-r--r--examples_java/src/persist/EventExampleDPL.java273
-rw-r--r--examples_java/src/persist/PersonExample.java256
-rw-r--r--examples_java/src/persist/gettingStarted/SimpleDA.java51
-rw-r--r--examples_java/src/persist/gettingStarted/SimpleEntityClass.java42
-rw-r--r--examples_java/src/persist/gettingStarted/SimpleStoreGet.java112
-rw-r--r--examples_java/src/persist/gettingStarted/SimpleStorePut.java116
-rw-r--r--examples_java/src/persist/txn/PayloadDataEntity.java33
-rw-r--r--examples_java/src/persist/txn/StoreWriter.java176
-rw-r--r--examples_java/src/persist/txn/TxnGuideDPL.java162
122 files changed, 17182 insertions, 0 deletions
diff --git a/examples_java/src/collections/access/AccessExample.java b/examples_java/src/collections/access/AccessExample.java
new file mode 100644
index 0000000..26aa401
--- /dev/null
+++ b/examples_java/src/collections/access/AccessExample.java
@@ -0,0 +1,288 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.access;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.PrintStream;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedMap;
+
+import com.sleepycat.bind.ByteArrayBinding;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+/**
+ * AccesssExample mirrors the functionality of a class by the same name
+ * used to demonstrate the com.sleepycat.je Java API. This version makes
+ * use of the new com.sleepycat.collections.* collections style classes to make
+ * life easier.
+ *
+ *@author Gregory Burd
+ *@created October 22, 2002
+ */
+public class AccessExample
+ implements Runnable {
+
+ // Class Variables of AccessExample class
+ private static boolean create = true;
+ private static final int EXIT_SUCCESS = 0;
+ private static final int EXIT_FAILURE = 1;
+
+ public static void usage() {
+
+ System.out.println("usage: java " + AccessExample.class.getName() +
+ " [-r] [database]\n");
+ System.exit(EXIT_FAILURE);
+ }
+
+ /**
+ * The main program for the AccessExample class
+ *
+ *@param argv The command line arguments
+ */
+ public static void main(String[] argv) {
+
+ boolean removeExistingDatabase = false;
+ String databaseName = "access.db";
+
+ for (int i = 0; i < argv.length; i++) {
+ if (argv[i].equals("-r")) {
+ removeExistingDatabase = true;
+ } else if (argv[i].equals("-?")) {
+ usage();
+ } else if (argv[i].startsWith("-")) {
+ usage();
+ } else {
+ if ((argv.length - i) != 1)
+ usage();
+ databaseName = argv[i];
+ break;
+ }
+ }
+
+ try {
+
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ if (create) {
+ envConfig.setAllowCreate(true);
+ }
+ Environment env = new Environment(new File("."), envConfig);
+ // Remove the previous database.
+ if (removeExistingDatabase) {
+ env.removeDatabase(null, databaseName, null);
+ }
+
+ // create the app and run it
+ AccessExample app = new AccessExample(env, databaseName);
+ app.run();
+ app.close();
+ } catch (DatabaseException e) {
+ e.printStackTrace();
+ System.exit(1);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ System.exit(1);
+ } catch (Exception e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+
+
+ private Database db;
+ private SortedMap map;
+ private Environment env;
+
+
+ /**
+ * Constructor for the AccessExample object
+ *
+ *@param env Description of the Parameter
+ *@exception Exception Description of the Exception
+ */
+ public AccessExample(Environment env, String databaseName)
+ throws Exception {
+
+ this.env = env;
+
+ //
+ // Lets mimic the db.AccessExample 100%
+ // and use plain old byte arrays to store the key and data strings.
+ //
+ ByteArrayBinding keyBinding = new ByteArrayBinding();
+ ByteArrayBinding dataBinding = new ByteArrayBinding();
+
+ //
+ // Open a data store.
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ if (create) {
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+ }
+ this.db = env.openDatabase(null, databaseName, null, dbConfig);
+
+ //
+ // Now create a collection style map view of the data store
+ // so that it is easy to work with the data in the database.
+ //
+ this.map = new StoredSortedMap(db, keyBinding, dataBinding, true);
+ }
+
+ /**
+ * Close the database and environment.
+ */
+ void close()
+ throws DatabaseException {
+
+ db.close();
+ env.close();
+ }
+
+ /**
+ * Main processing method for the AccessExample object
+ */
+ public void run() {
+ //
+ // Insert records into a Stored Sorted Map DatabaseImpl, where
+ // the key is the user input and the data is the user input
+ // in reverse order.
+ //
+ final InputStreamReader reader = new InputStreamReader(System.in);
+
+ for (; ; ) {
+ final String line = askForLine(reader, System.out, "input> ");
+ if (line == null) {
+ break;
+ }
+
+ final String reversed =
+ (new StringBuffer(line)).reverse().toString();
+
+ log("adding: \"" +
+ line + "\" : \"" +
+ reversed + "\"");
+
+ // Do the work to add the key/data to the HashMap here.
+ TransactionRunner tr = new TransactionRunner(env);
+ try {
+ tr.run(
+ new TransactionWorker() {
+ public void doWork() {
+ if (!map.containsKey(line.getBytes()))
+ map.put(line.getBytes(),
+ reversed.getBytes());
+ else
+ System.out.println("Key " + line +
+ " already exists.");
+ }
+ });
+ } catch (com.sleepycat.db.DatabaseException e) {
+ System.err.println("AccessExample: " + e.toString());
+ System.exit(1);
+ } catch (java.lang.Exception e) {
+ System.err.println("AccessExample: " + e.toString());
+ System.exit(1);
+ }
+ }
+ System.out.println("");
+
+ // Do the work to traverse and print the HashMap key/data
+ // pairs here get iterator over map entries.
+ Iterator iter = map.entrySet().iterator();
+ System.out.println("Reading data");
+ while (iter.hasNext()) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ log("found \"" +
+ new String((byte[]) entry.getKey()) +
+ "\" key with data \"" +
+ new String((byte[]) entry.getValue()) + "\"");
+ }
+ }
+
+
+ /**
+ * Prompts for a line, and keeps prompting until a non blank line is
+ * returned. Returns null on error.
+ *
+ *@param reader stream from which to read user input
+ *@param out stream on which to prompt for user input
+ *@param prompt prompt to use to solicit input
+ *@return the string supplied by the user
+ */
+ String askForLine(InputStreamReader reader, PrintStream out,
+ String prompt) {
+
+ String result = "";
+ while (result != null && result.length() == 0) {
+ out.print(prompt);
+ out.flush();
+ result = getLine(reader);
+ }
+ return result;
+ }
+
+
+ /**
+ * Read a single line. Gets the line attribute of the AccessExample object
+ * Not terribly efficient, but does the job. Works for reading a line from
+ * stdin or a file.
+ *
+ *@param reader stream from which to read the line
+ *@return either a String or null on EOF, if EOF appears in the
+ * middle of a line, returns that line, then null on next call.
+ */
+ String getLine(InputStreamReader reader) {
+
+ StringBuffer b = new StringBuffer();
+ int c;
+ try {
+ while ((c = reader.read()) != -1 && c != '\n') {
+ if (c != '\r') {
+ b.append((char) c);
+ }
+ }
+ } catch (IOException ioe) {
+ c = -1;
+ }
+
+ if (c == -1 && b.length() == 0) {
+ return null;
+ } else {
+ return b.toString();
+ }
+ }
+
+
+ /**
+ * A simple log method.
+ *
+ *@param s The string to be logged.
+ */
+ private void log(String s) {
+
+ System.out.println(s);
+ System.out.flush();
+ }
+}
diff --git a/examples_java/src/collections/hello/HelloDatabaseWorld.java b/examples_java/src/collections/hello/HelloDatabaseWorld.java
new file mode 100644
index 0000000..6791dff
--- /dev/null
+++ b/examples_java/src/collections/hello/HelloDatabaseWorld.java
@@ -0,0 +1,156 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.hello;
+
+import java.io.File;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.SortedMap;
+
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+/**
+ * @author Mark Hayes
+ */
+public class HelloDatabaseWorld implements TransactionWorker {
+
+ private static final String[] INT_NAMES = {
+ "Hello", "Database", "World",
+ };
+ private static boolean create = true;
+
+ private Environment env;
+ private ClassCatalog catalog;
+ private Database db;
+ private SortedMap map;
+
+ /** Creates the environment and runs a transaction */
+ public static void main(String[] argv)
+ throws Exception {
+
+ String dir = "./tmp";
+
+ // environment is transactional
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ if (create) {
+ envConfig.setAllowCreate(true);
+ }
+ Environment env = new Environment(new File(dir), envConfig);
+
+ // create the application and run a transaction
+ HelloDatabaseWorld worker = new HelloDatabaseWorld(env);
+ TransactionRunner runner = new TransactionRunner(env);
+ try {
+ // open and access the database within a transaction
+ runner.run(worker);
+ } finally {
+ // close the database outside the transaction
+ worker.close();
+ }
+ }
+
+ /** Creates the database for this application */
+ private HelloDatabaseWorld(Environment env)
+ throws Exception {
+
+ this.env = env;
+ open();
+ }
+
+ /** Performs work within a transaction. */
+ public void doWork()
+ throws Exception {
+
+ writeAndRead();
+ }
+
+ /** Opens the database and creates the Map. */
+ private void open()
+ throws Exception {
+
+ // use a generic database configuration
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ if (create) {
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+ }
+
+ // catalog is needed for serial bindings (java serialization)
+ Database catalogDb = env.openDatabase(null, "catalog", null, dbConfig);
+ catalog = new StoredClassCatalog(catalogDb);
+
+ // use Integer tuple binding for key entries
+ TupleBinding keyBinding =
+ TupleBinding.getPrimitiveBinding(Integer.class);
+
+ // use String serial binding for data entries
+ SerialBinding dataBinding = new SerialBinding(catalog, String.class);
+
+ this.db = env.openDatabase(null, "helloworld", null, dbConfig);
+
+ // create a map view of the database
+ this.map = new StoredSortedMap(db, keyBinding, dataBinding, true);
+ }
+
+ /** Closes the database. */
+ private void close()
+ throws Exception {
+
+ if (catalog != null) {
+ catalog.close();
+ catalog = null;
+ }
+ if (db != null) {
+ db.close();
+ db = null;
+ }
+ if (env != null) {
+ env.close();
+ env = null;
+ }
+ }
+
+ /** Writes and reads the database via the Map. */
+ private void writeAndRead() {
+
+ // check for existing data
+ Integer key = new Integer(0);
+ String val = (String) map.get(key);
+ if (val == null) {
+ System.out.println("Writing data");
+ // write in reverse order to show that keys are sorted
+ for (int i = INT_NAMES.length - 1; i >= 0; i -= 1) {
+ map.put(new Integer(i), INT_NAMES[i]);
+ }
+ }
+ // get iterator over map entries
+ Iterator iter = map.entrySet().iterator();
+ System.out.println("Reading data");
+ while (iter.hasNext()) {
+ Map.Entry entry = (Map.Entry) iter.next();
+ System.out.println(entry.getKey().toString() + ' ' +
+ entry.getValue());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/PartData.java b/examples_java/src/collections/ship/basic/PartData.java
new file mode 100644
index 0000000..316a56d
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/PartData.java
@@ -0,0 +1,64 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.Serializable;
+
+/**
+ * A PartData serves as the data in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartData is used both as the storage entry for the
+ * data as well as the object binding to the data. Because it is used
+ * directly as storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartData implements Serializable {
+
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public PartData(String name, String color, Weight weight, String city) {
+
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[PartData: name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/PartKey.java b/examples_java/src/collections/ship/basic/PartKey.java
new file mode 100644
index 0000000..851362f
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/PartKey.java
@@ -0,0 +1,40 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.Serializable;
+
+/**
+ * A PartKey serves as the key in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartKey is used both as the storage entry for the key as
+ * well as the object binding to the key. Because it is used directly as
+ * storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartKey implements Serializable {
+
+ private String number;
+
+ public PartKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[PartKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/Sample.java b/examples_java/src/collections/ship/basic/Sample.java
new file mode 100644
index 0000000..7413cf8
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/Sample.java
@@ -0,0 +1,254 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * Sample is the main entry point for the sample program and may be run as
+ * follows:
+ *
+ * <pre>
+ * java collections.ship.basic.Sample
+ * [-h <home-directory> ]
+ * </pre>
+ *
+ * <p> The default for the home directory is ./tmp -- the tmp subdirectory of
+ * the current directory where the sample is run. The home directory must exist
+ * before running the sample. To recreate the sample database from scratch,
+ * delete all files in the home directory before running the sample. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Sample {
+
+ private SampleDatabase db;
+ private SampleViews views;
+
+ /**
+ * Run the sample program.
+ */
+ public static void main(String[] args) {
+
+ System.out.println("\nRunning sample: " + Sample.class);
+
+ // Parse the command line arguments.
+ //
+ String homeDir = "./tmp";
+ for (int i = 0; i < args.length; i += 1) {
+ if (args[i].equals("-h") && i < args.length - 1) {
+ i += 1;
+ homeDir = args[i];
+ } else {
+ System.err.println("Usage:\n java " + Sample.class.getName() +
+ "\n [-h <home-directory>]");
+ System.exit(2);
+ }
+ }
+
+ // Run the sample.
+ //
+ Sample sample = null;
+ try {
+ sample = new Sample(homeDir);
+ sample.run();
+ } catch (Exception e) {
+ // If an exception reaches this point, the last transaction did not
+ // complete. If the exception is RunRecoveryException, follow
+ // the Berkeley DB recovery procedures before running again.
+ e.printStackTrace();
+ } finally {
+ if (sample != null) {
+ try {
+ // Always attempt to close the database cleanly.
+ sample.close();
+ } catch (Exception e) {
+ System.err.println("Exception during database close:");
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Open the database and views.
+ */
+ private Sample(String homeDir)
+ throws DatabaseException, FileNotFoundException {
+
+ db = new SampleDatabase(homeDir);
+ views = new SampleViews(db);
+ }
+
+ /**
+ * Close the database cleanly.
+ */
+ private void close()
+ throws DatabaseException {
+
+ db.close();
+ }
+
+ /**
+ * Run two transactions to populate and print the database. A
+ * TransactionRunner is used to ensure consistent handling of transactions,
+ * including deadlock retries. But the best transaction handling mechanism
+ * to use depends on the application.
+ */
+ private void run()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(db.getEnvironment());
+ runner.run(new PopulateDatabase());
+ runner.run(new PrintDatabase());
+ }
+
+ /**
+ * Populate the database in a single transaction.
+ */
+ private class PopulateDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ addSuppliers();
+ addParts();
+ addShipments();
+ }
+ }
+
+ /**
+ * Print the database in a single transaction. All entities are printed.
+ */
+ private class PrintDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ printEntries("Parts",
+ views.getPartEntrySet().iterator());
+ printEntries("Suppliers",
+ views.getSupplierEntrySet().iterator());
+ printEntries("Shipments",
+ views.getShipmentEntrySet().iterator());
+ }
+ }
+
+ /**
+ * Populate the part entities in the database. If the part map is not
+ * empty, assume that this has already been done.
+ */
+ private void addParts() {
+
+ Map parts = views.getPartMap();
+ if (parts.isEmpty()) {
+ System.out.println("Adding Parts");
+ parts.put(new PartKey("P1"),
+ new PartData("Nut", "Red",
+ new Weight(12.0, Weight.GRAMS),
+ "London"));
+ parts.put(new PartKey("P2"),
+ new PartData("Bolt", "Green",
+ new Weight(17.0, Weight.GRAMS),
+ "Paris"));
+ parts.put(new PartKey("P3"),
+ new PartData("Screw", "Blue",
+ new Weight(17.0, Weight.GRAMS),
+ "Rome"));
+ parts.put(new PartKey("P4"),
+ new PartData("Screw", "Red",
+ new Weight(14.0, Weight.GRAMS),
+ "London"));
+ parts.put(new PartKey("P5"),
+ new PartData("Cam", "Blue",
+ new Weight(12.0, Weight.GRAMS),
+ "Paris"));
+ parts.put(new PartKey("P6"),
+ new PartData("Cog", "Red",
+ new Weight(19.0, Weight.GRAMS),
+ "London"));
+ }
+ }
+
+ /**
+ * Populate the supplier entities in the database. If the supplier map is
+ * not empty, assume that this has already been done.
+ */
+ private void addSuppliers() {
+
+ Map suppliers = views.getSupplierMap();
+ if (suppliers.isEmpty()) {
+ System.out.println("Adding Suppliers");
+ suppliers.put(new SupplierKey("S1"),
+ new SupplierData("Smith", 20, "London"));
+ suppliers.put(new SupplierKey("S2"),
+ new SupplierData("Jones", 10, "Paris"));
+ suppliers.put(new SupplierKey("S3"),
+ new SupplierData("Blake", 30, "Paris"));
+ suppliers.put(new SupplierKey("S4"),
+ new SupplierData("Clark", 20, "London"));
+ suppliers.put(new SupplierKey("S5"),
+ new SupplierData("Adams", 30, "Athens"));
+ }
+ }
+
+ /**
+ * Populate the shipment entities in the database. If the shipment map
+ * is not empty, assume that this has already been done.
+ */
+ private void addShipments() {
+
+ Map shipments = views.getShipmentMap();
+ if (shipments.isEmpty()) {
+ System.out.println("Adding Shipments");
+ shipments.put(new ShipmentKey("P1", "S1"),
+ new ShipmentData(300));
+ shipments.put(new ShipmentKey("P2", "S1"),
+ new ShipmentData(200));
+ shipments.put(new ShipmentKey("P3", "S1"),
+ new ShipmentData(400));
+ shipments.put(new ShipmentKey("P4", "S1"),
+ new ShipmentData(200));
+ shipments.put(new ShipmentKey("P5", "S1"),
+ new ShipmentData(100));
+ shipments.put(new ShipmentKey("P6", "S1"),
+ new ShipmentData(100));
+ shipments.put(new ShipmentKey("P1", "S2"),
+ new ShipmentData(300));
+ shipments.put(new ShipmentKey("P2", "S2"),
+ new ShipmentData(400));
+ shipments.put(new ShipmentKey("P2", "S3"),
+ new ShipmentData(200));
+ shipments.put(new ShipmentKey("P2", "S4"),
+ new ShipmentData(200));
+ shipments.put(new ShipmentKey("P4", "S4"),
+ new ShipmentData(300));
+ shipments.put(new ShipmentKey("P5", "S4"),
+ new ShipmentData(400));
+ }
+ }
+
+ /**
+ * Print the key/value objects returned by an iterator of Map.Entry
+ * objects.
+ */
+ private void printEntries(String label, Iterator iterator) {
+
+ System.out.println("\n--- " + label + " ---");
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ System.out.println(entry.getKey().toString());
+ System.out.println(entry.getValue().toString());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/SampleDatabase.java b/examples_java/src/collections/ship/basic/SampleDatabase.java
new file mode 100644
index 0000000..47fff2c
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/SampleDatabase.java
@@ -0,0 +1,134 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+/**
+ * SampleDatabase defines the storage containers, indices and foreign keys
+ * for the sample database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleDatabase {
+
+ private static final String CLASS_CATALOG = "java_class_catalog";
+ private static final String SUPPLIER_STORE = "supplier_store";
+ private static final String PART_STORE = "part_store";
+ private static final String SHIPMENT_STORE = "shipment_store";
+
+ private Environment env;
+ private Database partDb;
+ private Database supplierDb;
+ private Database shipmentDb;
+ private StoredClassCatalog javaCatalog;
+
+ /**
+ * Open all storage containers, indices, and catalogs.
+ */
+ public SampleDatabase(String homeDirectory)
+ throws DatabaseException, FileNotFoundException {
+
+ // Open the Berkeley DB environment in transactional mode.
+ //
+ System.out.println("Opening environment in: " + homeDirectory);
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setAllowCreate(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(new File(homeDirectory), envConfig);
+
+ // Set the Berkeley DB config for opening all stores.
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+
+ // Create the Serial class catalog. This holds the serialized class
+ // format for all database records of serial format.
+ //
+ Database catalogDb = env.openDatabase(null, CLASS_CATALOG, null,
+ dbConfig);
+ javaCatalog = new StoredClassCatalog(catalogDb);
+
+ // Open the Berkeley DB database for the part, supplier and shipment
+ // stores. The stores are opened with no duplicate keys allowed.
+ //
+ partDb = env.openDatabase(null, PART_STORE, null, dbConfig);
+
+ supplierDb = env.openDatabase(null, SUPPLIER_STORE, null, dbConfig);
+
+ shipmentDb = env.openDatabase(null, SHIPMENT_STORE, null, dbConfig);
+ }
+
+ /**
+ * Return the storage environment for the database.
+ */
+ public final Environment getEnvironment() {
+
+ return env;
+ }
+
+ /**
+ * Return the class catalog.
+ */
+ public final StoredClassCatalog getClassCatalog() {
+
+ return javaCatalog;
+ }
+
+ /**
+ * Return the part storage container.
+ */
+ public final Database getPartDatabase() {
+
+ return partDb;
+ }
+
+ /**
+ * Return the supplier storage container.
+ */
+ public final Database getSupplierDatabase() {
+
+ return supplierDb;
+ }
+
+ /**
+ * Return the shipment storage container.
+ */
+ public final Database getShipmentDatabase() {
+
+ return shipmentDb;
+ }
+
+ /**
+ * Close all databases and the environment.
+ */
+ public void close()
+ throws DatabaseException {
+
+ partDb.close();
+ supplierDb.close();
+ shipmentDb.close();
+ // And don't forget to close the catalog and the environment.
+ javaCatalog.close();
+ env.close();
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/SampleViews.java b/examples_java/src/collections/ship/basic/SampleViews.java
new file mode 100644
index 0000000..97c1192
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/SampleViews.java
@@ -0,0 +1,122 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.collections.StoredEntrySet;
+import com.sleepycat.collections.StoredMap;
+
+/**
+ * SampleViews defines the data bindings and collection views for the sample
+ * database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleViews {
+
+ private StoredMap partMap;
+ private StoredMap supplierMap;
+ private StoredMap shipmentMap;
+
+ /**
+ * Create the data bindings and collection views.
+ */
+ public SampleViews(SampleDatabase db) {
+
+ // In this sample, the stored key and data entries are used directly
+ // rather than mapping them to separate objects. Therefore, no binding
+ // classes are defined here and the SerialBinding class is used.
+ //
+ ClassCatalog catalog = db.getClassCatalog();
+ EntryBinding partKeyBinding =
+ new SerialBinding(catalog, PartKey.class);
+ EntryBinding partDataBinding =
+ new SerialBinding(catalog, PartData.class);
+ EntryBinding supplierKeyBinding =
+ new SerialBinding(catalog, SupplierKey.class);
+ EntryBinding supplierDataBinding =
+ new SerialBinding(catalog, SupplierData.class);
+ EntryBinding shipmentKeyBinding =
+ new SerialBinding(catalog, ShipmentKey.class);
+ EntryBinding shipmentDataBinding =
+ new SerialBinding(catalog, ShipmentData.class);
+
+ // Create map views for all stores and indices.
+ // StoredSortedMap is not used since the stores and indices are
+ // ordered by serialized key objects, which do not provide a very
+ // useful ordering.
+ //
+ partMap =
+ new StoredMap(db.getPartDatabase(),
+ partKeyBinding, partDataBinding, true);
+ supplierMap =
+ new StoredMap(db.getSupplierDatabase(),
+ supplierKeyBinding, supplierDataBinding, true);
+ shipmentMap =
+ new StoredMap(db.getShipmentDatabase(),
+ shipmentKeyBinding, shipmentDataBinding, true);
+ }
+
+ // The views returned below can be accessed using the java.util.Map or
+ // java.util.Set interfaces, or using the StoredMap and StoredEntrySet
+ // classes, which provide additional methods. The entry sets could be
+ // obtained directly from the Map.entrySet() method, but convenience
+ // methods are provided here to return them in order to avoid down-casting
+ // elsewhere.
+
+ /**
+ * Return a map view of the part storage container.
+ */
+ public final StoredMap getPartMap() {
+
+ return partMap;
+ }
+
+ /**
+ * Return a map view of the supplier storage container.
+ */
+ public final StoredMap getSupplierMap() {
+
+ return supplierMap;
+ }
+
+ /**
+ * Return a map view of the shipment storage container.
+ */
+ public final StoredMap getShipmentMap() {
+
+ return shipmentMap;
+ }
+
+ /**
+ * Return an entry set view of the part storage container.
+ */
+ public final StoredEntrySet getPartEntrySet() {
+
+ return (StoredEntrySet) partMap.entrySet();
+ }
+
+ /**
+ * Return an entry set view of the supplier storage container.
+ */
+ public final StoredEntrySet getSupplierEntrySet() {
+
+ return (StoredEntrySet) supplierMap.entrySet();
+ }
+
+ /**
+ * Return an entry set view of the shipment storage container.
+ */
+ public final StoredEntrySet getShipmentEntrySet() {
+
+ return (StoredEntrySet) shipmentMap.entrySet();
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/ShipmentData.java b/examples_java/src/collections/ship/basic/ShipmentData.java
new file mode 100644
index 0000000..96f3336
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/ShipmentData.java
@@ -0,0 +1,41 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.Serializable;
+
+/**
+ * A ShipmentData serves as the data in the key/data pair for a shipment
+ * entity.
+ *
+ * <p> In this sample, ShipmentData is used both as the storage entry for the
+ * data as well as the object binding to the data. Because it is used
+ * directly as storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentData implements Serializable {
+
+ private int quantity;
+
+ public ShipmentData(int quantity) {
+
+ this.quantity = quantity;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[ShipmentData: quantity=" + quantity + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/ShipmentKey.java b/examples_java/src/collections/ship/basic/ShipmentKey.java
new file mode 100644
index 0000000..2dab3dc
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/ShipmentKey.java
@@ -0,0 +1,48 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.Serializable;
+
+/**
+ * A ShipmentKey serves as the key in the key/data pair for a shipment entity.
+ *
+ * <p> In this sample, ShipmentKey is used both as the storage entry for the
+ * key as well as the object binding to the key. Because it is used directly
+ * as storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentKey implements Serializable {
+
+ private String partNumber;
+ private String supplierNumber;
+
+ public ShipmentKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public String toString() {
+
+ return "[ShipmentKey: supplier=" + supplierNumber +
+ " part=" + partNumber + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/SupplierData.java b/examples_java/src/collections/ship/basic/SupplierData.java
new file mode 100644
index 0000000..0853f23
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/SupplierData.java
@@ -0,0 +1,57 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.Serializable;
+
+/**
+ * A SupplierData serves as the data in the key/data pair for a supplier
+ * entity.
+ *
+ * <p> In this sample, SupplierData is used both as the storage entry for the
+ * data as well as the object binding to the data. Because it is used
+ * directly as storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierData implements Serializable {
+
+ private String name;
+ private int status;
+ private String city;
+
+ public SupplierData(String name, int status, String city) {
+
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[SupplierData: name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/SupplierKey.java b/examples_java/src/collections/ship/basic/SupplierKey.java
new file mode 100644
index 0000000..6f16a1b
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/SupplierKey.java
@@ -0,0 +1,40 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.Serializable;
+
+/**
+ * A SupplierKey serves as the key in the key/data pair for a supplier entity.
+ *
+ * <p>In this sample, SupplierKey is used both as the storage entry for the key
+ * as well as the object binding to the key. Because it is used directly as
+ * storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierKey implements Serializable {
+
+ private String number;
+
+ public SupplierKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[SupplierKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/basic/Weight.java b/examples_java/src/collections/ship/basic/Weight.java
new file mode 100644
index 0000000..f5303fa
--- /dev/null
+++ b/examples_java/src/collections/ship/basic/Weight.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.basic;
+
+import java.io.Serializable;
+
+/**
+ * Weight represents a weight amount and unit of measure.
+ *
+ * <p> In this sample, Weight is embedded in part data values which are stored
+ * as Serial serialized objects; therefore Weight must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Weight implements Serializable {
+
+ public final static String GRAMS = "grams";
+ public final static String OUNCES = "ounces";
+
+ private double amount;
+ private String units;
+
+ public Weight(double amount, String units) {
+
+ this.amount = amount;
+ this.units = units;
+ }
+
+ public final double getAmount() {
+
+ return amount;
+ }
+
+ public final String getUnits() {
+
+ return units;
+ }
+
+ public String toString() {
+
+ return "[" + amount + ' ' + units + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/Part.java b/examples_java/src/collections/ship/entity/Part.java
new file mode 100644
index 0000000..68051fa
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/Part.java
@@ -0,0 +1,72 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+/**
+ * A Part represents the combined key/data pair for a part entity.
+ *
+ * <p>In this sample, Part is created from the stored key/data entry using a
+ * SerialSerialBinding. See {@link SampleViews.PartBinding} for details.
+ * Since this class is not used directly for data storage, it does not need to
+ * be Serializable.</p>
+ *
+ * @author Mark Hayes
+ */
+public class Part {
+
+ private String number;
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public Part(String number, String name, String color, Weight weight,
+ String city) {
+
+ this.number = number;
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Part: number=" + number +
+ " name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/PartData.java b/examples_java/src/collections/ship/entity/PartData.java
new file mode 100644
index 0000000..b61bb0a
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/PartData.java
@@ -0,0 +1,65 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.Serializable;
+
+/**
+ * A PartData serves as the value in the key/value pair for a part entity.
+ *
+ * <p> In this sample, PartData is used only as the storage data for the
+ * value, while the Part object is used as the value's object representation.
+ * Because it is used directly as storage data using serial format, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartData implements Serializable {
+
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public PartData(String name, String color, Weight weight, String city) {
+
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[PartData: name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/PartKey.java b/examples_java/src/collections/ship/entity/PartKey.java
new file mode 100644
index 0000000..b37fc40
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/PartKey.java
@@ -0,0 +1,40 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.Serializable;
+
+/**
+ * A PartKey serves as the key in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartKey is used both as the storage entry for the key as
+ * well as the object binding to the key. Because it is used directly as
+ * storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartKey implements Serializable {
+
+ private String number;
+
+ public PartKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[PartKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/Sample.java b/examples_java/src/collections/ship/entity/Sample.java
new file mode 100644
index 0000000..207a5e1
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/Sample.java
@@ -0,0 +1,236 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * Sample is the main entry point for the sample program and may be run as
+ * follows:
+ *
+ * <pre>
+ * java collections.ship.entity.Sample
+ * [-h <home-directory> ]
+ * </pre>
+ *
+ * <p> The default for the home directory is ./tmp -- the tmp subdirectory of
+ * the current directory where the sample is run. The home directory must exist
+ * before running the sample. To recreate the sample database from scratch,
+ * delete all files in the home directory before running the sample. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Sample {
+
+ private SampleDatabase db;
+ private SampleViews views;
+
+ /**
+ * Run the sample program.
+ */
+ public static void main(String[] args) {
+
+ System.out.println("\nRunning sample: " + Sample.class);
+
+ // Parse the command line arguments.
+ //
+ String homeDir = "./tmp";
+ for (int i = 0; i < args.length; i += 1) {
+ if (args[i].equals("-h") && i < args.length - 1) {
+ i += 1;
+ homeDir = args[i];
+ } else {
+ System.err.println("Usage:\n java " + Sample.class.getName() +
+ "\n [-h <home-directory>]");
+ System.exit(2);
+ }
+ }
+
+ // Run the sample.
+ //
+ Sample sample = null;
+ try {
+ sample = new Sample(homeDir);
+ sample.run();
+ } catch (Exception e) {
+ // If an exception reaches this point, the last transaction did not
+ // complete. If the exception is RunRecoveryException, follow
+ // the Berkeley DB recovery procedures before running again.
+ e.printStackTrace();
+ } finally {
+ if (sample != null) {
+ try {
+ // Always attempt to close the database cleanly.
+ sample.close();
+ } catch (Exception e) {
+ System.err.println("Exception during database close:");
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Open the database and views.
+ */
+ private Sample(String homeDir)
+ throws DatabaseException, FileNotFoundException {
+
+ db = new SampleDatabase(homeDir);
+ views = new SampleViews(db);
+ }
+
+ /**
+ * Close the database cleanly.
+ */
+ private void close()
+ throws DatabaseException {
+
+ db.close();
+ }
+
+ /**
+ * Run two transactions to populate and print the database. A
+ * TransactionRunner is used to ensure consistent handling of transactions,
+ * including deadlock retries. But the best transaction handling mechanism
+ * to use depends on the application.
+ */
+ private void run()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(db.getEnvironment());
+ runner.run(new PopulateDatabase());
+ runner.run(new PrintDatabase());
+ }
+
+ /**
+ * Populate the database in a single transaction.
+ */
+ private class PopulateDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ addSuppliers();
+ addParts();
+ addShipments();
+ }
+ }
+
+ /**
+ * Print the database in a single transaction. All entities are printed
+ * and the indices are used to print the entities for certain keys.
+ *
+ * <p> Note the use of special iterator() methods. These are used here
+ * with indices to find the shipments for certain keys.</p>
+ */
+ private class PrintDatabase implements TransactionWorker {
+
+
+ public void doWork()
+ throws Exception {
+ printValues("Parts",
+ views.getPartSet().iterator());
+ printValues("Suppliers",
+ views.getSupplierSet().iterator());
+ printValues("Suppliers for City Paris",
+ views.getSupplierByCityMap().duplicates(
+ "Paris").iterator());
+ printValues("Shipments",
+ views.getShipmentSet().iterator());
+ printValues("Shipments for Part P1",
+ views.getShipmentByPartMap().duplicates(
+ new PartKey("P1")).iterator());
+ printValues("Shipments for Supplier S1",
+ views.getShipmentBySupplierMap().duplicates(
+ new SupplierKey("S1")).iterator());
+ }
+ }
+
+ /**
+ * Populate the part entities in the database. If the part set is not
+ * empty, assume that this has already been done.
+ */
+ private void addParts() {
+
+ Set parts = views.getPartSet();
+ if (parts.isEmpty()) {
+ System.out.println("Adding Parts");
+ parts.add(new Part("P1", "Nut", "Red",
+ new Weight(12.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P2", "Bolt", "Green",
+ new Weight(17.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P3", "Screw", "Blue",
+ new Weight(17.0, Weight.GRAMS), "Rome"));
+ parts.add(new Part("P4", "Screw", "Red",
+ new Weight(14.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P5", "Cam", "Blue",
+ new Weight(12.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P6", "Cog", "Red",
+ new Weight(19.0, Weight.GRAMS), "London"));
+ }
+ }
+
+ /**
+ * Populate the supplier entities in the database. If the supplier set is
+ * not empty, assume that this has already been done.
+ */
+ private void addSuppliers() {
+
+ Set suppliers = views.getSupplierSet();
+ if (suppliers.isEmpty()) {
+ System.out.println("Adding Suppliers");
+ suppliers.add(new Supplier("S1", "Smith", 20, "London"));
+ suppliers.add(new Supplier("S2", "Jones", 10, "Paris"));
+ suppliers.add(new Supplier("S3", "Blake", 30, "Paris"));
+ suppliers.add(new Supplier("S4", "Clark", 20, "London"));
+ suppliers.add(new Supplier("S5", "Adams", 30, "Athens"));
+ }
+ }
+
+ /**
+ * Populate the shipment entities in the database. If the shipment set
+ * is not empty, assume that this has already been done.
+ */
+ private void addShipments() {
+
+ Set shipments = views.getShipmentSet();
+ if (shipments.isEmpty()) {
+ System.out.println("Adding Shipments");
+ shipments.add(new Shipment("P1", "S1", 300));
+ shipments.add(new Shipment("P2", "S1", 200));
+ shipments.add(new Shipment("P3", "S1", 400));
+ shipments.add(new Shipment("P4", "S1", 200));
+ shipments.add(new Shipment("P5", "S1", 100));
+ shipments.add(new Shipment("P6", "S1", 100));
+ shipments.add(new Shipment("P1", "S2", 300));
+ shipments.add(new Shipment("P2", "S2", 400));
+ shipments.add(new Shipment("P2", "S3", 200));
+ shipments.add(new Shipment("P2", "S4", 200));
+ shipments.add(new Shipment("P4", "S4", 300));
+ shipments.add(new Shipment("P5", "S4", 400));
+ }
+ }
+
+ /**
+ * Print the objects returned by an iterator of entity value objects.
+ */
+ private void printValues(String label, Iterator iterator) {
+
+ System.out.println("\n--- " + label + " ---");
+ while (iterator.hasNext()) {
+ System.out.println(iterator.next().toString());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/SampleDatabase.java b/examples_java/src/collections/ship/entity/SampleDatabase.java
new file mode 100644
index 0000000..870b16a
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/SampleDatabase.java
@@ -0,0 +1,331 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.SerialSerialKeyCreator;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+
+/**
+ * SampleDatabase defines the storage containers, indices and foreign keys
+ * for the sample database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleDatabase {
+
+ private static final String CLASS_CATALOG = "java_class_catalog";
+ private static final String SUPPLIER_STORE = "supplier_store";
+ private static final String PART_STORE = "part_store";
+ private static final String SHIPMENT_STORE = "shipment_store";
+ private static final String SHIPMENT_PART_INDEX = "shipment_part_index";
+ private static final String SHIPMENT_SUPPLIER_INDEX =
+ "shipment_supplier_index";
+ private static final String SUPPLIER_CITY_INDEX = "supplier_city_index";
+
+ private Environment env;
+ private Database partDb;
+ private Database supplierDb;
+ private Database shipmentDb;
+ private SecondaryDatabase supplierByCityDb;
+ private SecondaryDatabase shipmentByPartDb;
+ private SecondaryDatabase shipmentBySupplierDb;
+ private StoredClassCatalog javaCatalog;
+
+ /**
+ * Open all storage containers, indices, and catalogs.
+ */
+ public SampleDatabase(String homeDirectory)
+ throws DatabaseException, FileNotFoundException {
+
+ // Open the Berkeley DB environment in transactional mode.
+ //
+ System.out.println("Opening environment in: " + homeDirectory);
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setAllowCreate(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(new File(homeDirectory), envConfig);
+
+ // Set the Berkeley DB config for opening all stores.
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+
+ // Create the Serial class catalog. This holds the serialized class
+ // format for all database records of serial format.
+ //
+ Database catalogDb = env.openDatabase(null, CLASS_CATALOG, null,
+ dbConfig);
+ javaCatalog = new StoredClassCatalog(catalogDb);
+
+ // Open the Berkeley DB database for the part, supplier and shipment
+ // stores. The stores are opened with no duplicate keys allowed.
+ //
+ partDb = env.openDatabase(null, PART_STORE, null, dbConfig);
+
+ supplierDb = env.openDatabase(null, SUPPLIER_STORE, null, dbConfig);
+
+ shipmentDb = env.openDatabase(null, SHIPMENT_STORE, null, dbConfig);
+
+ // Open the SecondaryDatabase for the city index of the supplier store,
+ // and for the part and supplier indices of the shipment store.
+ // Duplicate keys are allowed since more than one supplier may be in
+ // the same city, and more than one shipment may exist for the same
+ // supplier or part. A foreign key constraint is defined for the
+ // supplier and part indices to ensure that a shipment only refers to
+ // existing part and supplier keys. The CASCADE delete action means
+ // that shipments will be deleted if their associated part or supplier
+ // is deleted.
+ //
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ secConfig.setType(DatabaseType.BTREE);
+ secConfig.setSortedDuplicates(true);
+
+ secConfig.setKeyCreator(
+ new SupplierByCityKeyCreator(javaCatalog,
+ SupplierKey.class,
+ SupplierData.class,
+ String.class));
+ supplierByCityDb = env.openSecondaryDatabase(null, SUPPLIER_CITY_INDEX,
+ null, supplierDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(partDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(
+ new ShipmentByPartKeyCreator(javaCatalog,
+ ShipmentKey.class,
+ ShipmentData.class,
+ PartKey.class));
+ shipmentByPartDb = env.openSecondaryDatabase(null, SHIPMENT_PART_INDEX,
+ null, shipmentDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(supplierDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(
+ new ShipmentBySupplierKeyCreator(javaCatalog,
+ ShipmentKey.class,
+ ShipmentData.class,
+ SupplierKey.class));
+ shipmentBySupplierDb = env.openSecondaryDatabase(null,
+ SHIPMENT_SUPPLIER_INDEX,
+ null, shipmentDb,
+ secConfig);
+ }
+
+ /**
+ * Return the storage environment for the database.
+ */
+ public final Environment getEnvironment() {
+
+ return env;
+ }
+
+ /**
+ * Return the class catalog.
+ */
+ public final StoredClassCatalog getClassCatalog() {
+
+ return javaCatalog;
+ }
+
+ /**
+ * Return the part storage container.
+ */
+ public final Database getPartDatabase() {
+
+ return partDb;
+ }
+
+ /**
+ * Return the supplier storage container.
+ */
+ public final Database getSupplierDatabase() {
+
+ return supplierDb;
+ }
+
+ /**
+ * Return the shipment storage container.
+ */
+ public final Database getShipmentDatabase() {
+
+ return shipmentDb;
+ }
+
+ /**
+ * Return the shipment-by-part index.
+ */
+ public final SecondaryDatabase getShipmentByPartDatabase() {
+
+ return shipmentByPartDb;
+ }
+
+ /**
+ * Return the shipment-by-supplier index.
+ */
+ public final SecondaryDatabase getShipmentBySupplierDatabase() {
+
+ return shipmentBySupplierDb;
+ }
+
+ /**
+ * Return the supplier-by-city index.
+ */
+ public final SecondaryDatabase getSupplierByCityDatabase() {
+
+ return supplierByCityDb;
+ }
+
+ /**
+ * Close all stores (closing a store automatically closes its indices).
+ */
+ public void close()
+ throws DatabaseException {
+
+ // Close secondary databases, then primary databases.
+ supplierByCityDb.close();
+ shipmentByPartDb.close();
+ shipmentBySupplierDb.close();
+ partDb.close();
+ supplierDb.close();
+ shipmentDb.close();
+ // And don't forget to close the catalog and the environment.
+ javaCatalog.close();
+ env.close();
+ }
+
+ /**
+ * The SecondaryKeyCreator for the SupplierByCity index. This is an
+ * extension of the abstract class SerialSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys and value are all
+ * of the serial format.
+ */
+ private static class SupplierByCityKeyCreator
+ extends SerialSerialKeyCreator {
+
+ /**
+ * Construct the city key extractor.
+ * @param catalog is the class catalog.
+ * @param primaryKeyClass is the supplier key class.
+ * @param valueClass is the supplier value class.
+ * @param indexKeyClass is the city key class.
+ */
+ private SupplierByCityKeyCreator(ClassCatalog catalog,
+ Class primaryKeyClass,
+ Class valueClass,
+ Class indexKeyClass) {
+
+ super(catalog, primaryKeyClass, valueClass, indexKeyClass);
+ }
+
+ /**
+ * Extract the city key from a supplier key/value pair. The city key
+ * is stored in the supplier value, so the supplier key is not used.
+ */
+ public Object createSecondaryKey(Object primaryKeyInput,
+ Object valueInput) {
+
+ SupplierData supplierData = (SupplierData) valueInput;
+ return supplierData.getCity();
+ }
+ }
+
+ /**
+ * The SecondaryKeyCreator for the ShipmentByPart index. This is an
+ * extension of the abstract class SerialSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys and value are all
+ * of the serial format.
+ */
+ private static class ShipmentByPartKeyCreator
+ extends SerialSerialKeyCreator {
+
+ /**
+ * Construct the part key extractor.
+ * @param catalog is the class catalog.
+ * @param primaryKeyClass is the shipment key class.
+ * @param valueClass is the shipment value class.
+ * @param indexKeyClass is the part key class.
+ */
+ private ShipmentByPartKeyCreator(ClassCatalog catalog,
+ Class primaryKeyClass,
+ Class valueClass,
+ Class indexKeyClass) {
+
+ super(catalog, primaryKeyClass, valueClass, indexKeyClass);
+ }
+
+ /**
+ * Extract the part key from a shipment key/value pair. The part key
+ * is stored in the shipment key, so the shipment value is not used.
+ */
+ public Object createSecondaryKey(Object primaryKeyInput,
+ Object valueInput) {
+
+ ShipmentKey shipmentKey = (ShipmentKey) primaryKeyInput;
+ return new PartKey(shipmentKey.getPartNumber());
+ }
+ }
+
+ /**
+ * The SecondaryKeyCreator for the ShipmentBySupplier index. This is an
+ * extension of the abstract class SerialSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys and value are all
+ * of the serial format.
+ */
+ private static class ShipmentBySupplierKeyCreator
+ extends SerialSerialKeyCreator {
+
+ /**
+ * Construct the supplier key extractor.
+ * @param catalog is the class catalog.
+ * @param primaryKeyClass is the shipment key class.
+ * @param valueClass is the shipment value class.
+ * @param indexKeyClass is the supplier key class.
+ */
+ private ShipmentBySupplierKeyCreator(ClassCatalog catalog,
+ Class primaryKeyClass,
+ Class valueClass,
+ Class indexKeyClass) {
+
+ super(catalog, primaryKeyClass, valueClass, indexKeyClass);
+ }
+
+ /**
+ * Extract the supplier key from a shipment key/value pair. The part
+ * key is stored in the shipment key, so the shipment value is not
+ * used.
+ */
+ public Object createSecondaryKey(Object primaryKeyInput,
+ Object valueInput) {
+
+ ShipmentKey shipmentKey = (ShipmentKey) primaryKeyInput;
+ return new SupplierKey(shipmentKey.getSupplierNumber());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/SampleViews.java b/examples_java/src/collections/ship/entity/SampleViews.java
new file mode 100644
index 0000000..4ccd1c6
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/SampleViews.java
@@ -0,0 +1,306 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.serial.SerialSerialBinding;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.StoredValueSet;
+
+/**
+ * SampleViews defines the data bindings and collection views for the sample
+ * database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleViews {
+
+ private StoredSortedMap partMap;
+ private StoredSortedMap supplierMap;
+ private StoredSortedMap shipmentMap;
+ private StoredSortedMap shipmentByPartMap;
+ private StoredSortedMap shipmentBySupplierMap;
+ private StoredSortedMap supplierByCityMap;
+
+ /**
+ * Create the data bindings and collection views.
+ */
+ public SampleViews(SampleDatabase db) {
+
+ // Create the data bindings.
+ // In this sample, EntityBinding classes are used to bind the stored
+ // key/data entry pair to a combined data object. For keys, however,
+ // the stored entry is used directly via a SerialBinding and no
+ // special binding class is needed.
+ //
+ ClassCatalog catalog = db.getClassCatalog();
+ SerialBinding partKeyBinding =
+ new SerialBinding(catalog, PartKey.class);
+ EntityBinding partDataBinding =
+ new PartBinding(catalog, PartKey.class, PartData.class);
+ SerialBinding supplierKeyBinding =
+ new SerialBinding(catalog, SupplierKey.class);
+ EntityBinding supplierDataBinding =
+ new SupplierBinding(catalog, SupplierKey.class,
+ SupplierData.class);
+ SerialBinding shipmentKeyBinding =
+ new SerialBinding(catalog, ShipmentKey.class);
+ EntityBinding shipmentDataBinding =
+ new ShipmentBinding(catalog, ShipmentKey.class,
+ ShipmentData.class);
+ SerialBinding cityKeyBinding =
+ new SerialBinding(catalog, String.class);
+
+ // Create map views for all stores and indices.
+ // StoredSortedMap is not used since the stores and indices are
+ // ordered by serialized key objects, which do not provide a very
+ // useful ordering.
+ //
+ partMap =
+ new StoredSortedMap(db.getPartDatabase(),
+ partKeyBinding, partDataBinding, true);
+ supplierMap =
+ new StoredSortedMap(db.getSupplierDatabase(),
+ supplierKeyBinding, supplierDataBinding, true);
+ shipmentMap =
+ new StoredSortedMap(db.getShipmentDatabase(),
+ shipmentKeyBinding, shipmentDataBinding, true);
+ shipmentByPartMap =
+ new StoredSortedMap(db.getShipmentByPartDatabase(),
+ partKeyBinding, shipmentDataBinding, true);
+ shipmentBySupplierMap =
+ new StoredSortedMap(db.getShipmentBySupplierDatabase(),
+ supplierKeyBinding, shipmentDataBinding, true);
+ supplierByCityMap =
+ new StoredSortedMap(db.getSupplierByCityDatabase(),
+ cityKeyBinding, supplierDataBinding, true);
+ }
+
+ // The views returned below can be accessed using the java.util.Map or
+ // java.util.Set interfaces, or using the StoredSortedMap and
+ // StoredValueSet classes, which provide additional methods. The entity
+ // sets could be obtained directly from the Map.values() method but
+ // convenience methods are provided here to return them in order to avoid
+ // down-casting elsewhere.
+
+ /**
+ * Return a map view of the part storage container.
+ */
+ public StoredSortedMap getPartMap() {
+
+ return partMap;
+ }
+
+ /**
+ * Return a map view of the supplier storage container.
+ */
+ public StoredSortedMap getSupplierMap() {
+
+ return supplierMap;
+ }
+
+ /**
+ * Return a map view of the shipment storage container.
+ */
+ public StoredSortedMap getShipmentMap() {
+
+ return shipmentMap;
+ }
+
+ /**
+ * Return an entity set view of the part storage container.
+ */
+ public StoredValueSet getPartSet() {
+
+ return (StoredValueSet) partMap.values();
+ }
+
+ /**
+ * Return an entity set view of the supplier storage container.
+ */
+ public StoredValueSet getSupplierSet() {
+
+ return (StoredValueSet) supplierMap.values();
+ }
+
+ /**
+ * Return an entity set view of the shipment storage container.
+ */
+ public StoredValueSet getShipmentSet() {
+
+ return (StoredValueSet) shipmentMap.values();
+ }
+
+ /**
+ * Return a map view of the shipment-by-part index.
+ */
+ public StoredSortedMap getShipmentByPartMap() {
+
+ return shipmentByPartMap;
+ }
+
+ /**
+ * Return a map view of the shipment-by-supplier index.
+ */
+ public StoredSortedMap getShipmentBySupplierMap() {
+
+ return shipmentBySupplierMap;
+ }
+
+ /**
+ * Return a map view of the supplier-by-city index.
+ */
+ public final StoredSortedMap getSupplierByCityMap() {
+
+ return supplierByCityMap;
+ }
+
+ /**
+ * PartBinding is used to bind the stored key/data entry pair for a part
+ * to a combined data object (entity).
+ */
+ private static class PartBinding extends SerialSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private PartBinding(ClassCatalog classCatalog,
+ Class keyClass,
+ Class dataClass) {
+
+ super(classCatalog, keyClass, dataClass);
+ }
+
+ /**
+ * Create the entity by combining the stored key and data.
+ */
+ public Object entryToObject(Object keyInput, Object dataInput) {
+
+ PartKey key = (PartKey) keyInput;
+ PartData data = (PartData) dataInput;
+ return new Part(key.getNumber(), data.getName(), data.getColor(),
+ data.getWeight(), data.getCity());
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public Object objectToKey(Object object) {
+
+ Part part = (Part) object;
+ return new PartKey(part.getNumber());
+ }
+
+ /**
+ * Create the stored data from the entity.
+ */
+ public Object objectToData(Object object) {
+
+ Part part = (Part) object;
+ return new PartData(part.getName(), part.getColor(),
+ part.getWeight(), part.getCity());
+ }
+ }
+
+ /**
+ * SupplierBinding is used to bind the stored key/data entry pair for a
+ * supplier to a combined data object (entity).
+ */
+ private static class SupplierBinding extends SerialSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private SupplierBinding(ClassCatalog classCatalog,
+ Class keyClass,
+ Class dataClass) {
+
+ super(classCatalog, keyClass, dataClass);
+ }
+
+ /**
+ * Create the entity by combining the stored key and data.
+ */
+ public Object entryToObject(Object keyInput, Object dataInput) {
+
+ SupplierKey key = (SupplierKey) keyInput;
+ SupplierData data = (SupplierData) dataInput;
+ return new Supplier(key.getNumber(), data.getName(),
+ data.getStatus(), data.getCity());
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public Object objectToKey(Object object) {
+
+ Supplier supplier = (Supplier) object;
+ return new SupplierKey(supplier.getNumber());
+ }
+
+ /**
+ * Create the stored data from the entity.
+ */
+ public Object objectToData(Object object) {
+
+ Supplier supplier = (Supplier) object;
+ return new SupplierData(supplier.getName(), supplier.getStatus(),
+ supplier.getCity());
+ }
+ }
+
+ /**
+ * ShipmentBinding is used to bind the stored key/data entry pair for a
+ * shipment to a combined data object (entity).
+ */
+ private static class ShipmentBinding extends SerialSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private ShipmentBinding(ClassCatalog classCatalog,
+ Class keyClass,
+ Class dataClass) {
+
+ super(classCatalog, keyClass, dataClass);
+ }
+
+ /**
+ * Create the entity by combining the stored key and data.
+ */
+ public Object entryToObject(Object keyInput, Object dataInput) {
+
+ ShipmentKey key = (ShipmentKey) keyInput;
+ ShipmentData data = (ShipmentData) dataInput;
+ return new Shipment(key.getPartNumber(), key.getSupplierNumber(),
+ data.getQuantity());
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public Object objectToKey(Object object) {
+
+ Shipment shipment = (Shipment) object;
+ return new ShipmentKey(shipment.getPartNumber(),
+ shipment.getSupplierNumber());
+ }
+
+ /**
+ * Create the stored data from the entity.
+ */
+ public Object objectToData(Object object) {
+
+ Shipment shipment = (Shipment) object;
+ return new ShipmentData(shipment.getQuantity());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/Shipment.java b/examples_java/src/collections/ship/entity/Shipment.java
new file mode 100644
index 0000000..ad5df02
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/Shipment.java
@@ -0,0 +1,55 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+/**
+ * A Shipment represents the combined key/data pair for a shipment entity.
+ *
+ * <p> In this sample, Shipment is created from the stored key/data entry
+ * using a SerialSerialBinding. See {@link SampleViews.ShipmentBinding} for
+ * details. Since this class is not used directly for data storage, it does
+ * not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Shipment {
+
+ private String partNumber;
+ private String supplierNumber;
+ private int quantity;
+
+ public Shipment(String partNumber, String supplierNumber, int quantity) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ this.quantity = quantity;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[Shipment: part=" + partNumber +
+ " supplier=" + supplierNumber +
+ " quantity=" + quantity + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/ShipmentData.java b/examples_java/src/collections/ship/entity/ShipmentData.java
new file mode 100644
index 0000000..002acdc
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/ShipmentData.java
@@ -0,0 +1,42 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.Serializable;
+
+/**
+ * A ShipmentData serves as the value in the key/value pair for a shipment
+ * entity.
+ *
+ * <p> In this sample, ShipmentData is used only as the storage data for the
+ * value, while the Shipment object is used as the value's object
+ * representation. Because it is used directly as storage data using
+ * serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentData implements Serializable {
+
+ private int quantity;
+
+ public ShipmentData(int quantity) {
+
+ this.quantity = quantity;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[ShipmentData: quantity=" + quantity + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/ShipmentKey.java b/examples_java/src/collections/ship/entity/ShipmentKey.java
new file mode 100644
index 0000000..8618e95
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/ShipmentKey.java
@@ -0,0 +1,48 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.Serializable;
+
+/**
+ * A ShipmentKey serves as the key in the key/data pair for a shipment entity.
+ *
+ * <p> In this sample, ShipmentKey is used both as the storage entry for the
+ * key as well as the object binding to the key. Because it is used directly
+ * as storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentKey implements Serializable {
+
+ private String partNumber;
+ private String supplierNumber;
+
+ public ShipmentKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public String toString() {
+
+ return "[ShipmentKey: supplier=" + supplierNumber +
+ " part=" + partNumber + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/Supplier.java b/examples_java/src/collections/ship/entity/Supplier.java
new file mode 100644
index 0000000..9019eb1
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/Supplier.java
@@ -0,0 +1,63 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+/**
+ * A Supplier represents the combined key/data pair for a supplier entity.
+ *
+ * <p> In this sample, Supplier is created from the stored key/data entry
+ * using a SerialSerialBinding. See {@link SampleViews.SupplierBinding} for
+ * details. Since this class is not used directly for data storage, it does
+ * not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Supplier {
+
+ private String number;
+ private String name;
+ private int status;
+ private String city;
+
+ public Supplier(String number, String name, int status, String city) {
+
+ this.number = number;
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Supplier: number=" + number +
+ " name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/SupplierData.java b/examples_java/src/collections/ship/entity/SupplierData.java
new file mode 100644
index 0000000..b158b4f
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/SupplierData.java
@@ -0,0 +1,58 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.Serializable;
+
+/**
+ * A SupplierData serves as the value in the key/value pair for a supplier
+ * entity.
+ *
+ * <p> In this sample, SupplierData is used only as the storage data for the
+ * value, while the Supplier object is used as the value's object
+ * representation. Because it is used directly as storage data using
+ * serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierData implements Serializable {
+
+ private String name;
+ private int status;
+ private String city;
+
+ public SupplierData(String name, int status, String city) {
+
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[SupplierData: name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/SupplierKey.java b/examples_java/src/collections/ship/entity/SupplierKey.java
new file mode 100644
index 0000000..ccfccf1
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/SupplierKey.java
@@ -0,0 +1,40 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.Serializable;
+
+/**
+ * A SupplierKey serves as the key in the key/data pair for a supplier entity.
+ *
+ * <p> In this sample, SupplierKey is used both as the storage entry for the
+ * key as well as the object binding to the key. Because it is used directly
+ * as storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierKey implements Serializable {
+
+ private String number;
+
+ public SupplierKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[SupplierKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/entity/Weight.java b/examples_java/src/collections/ship/entity/Weight.java
new file mode 100644
index 0000000..cded81c
--- /dev/null
+++ b/examples_java/src/collections/ship/entity/Weight.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.entity;
+
+import java.io.Serializable;
+
+/**
+ * Weight represents a weight amount and unit of measure.
+ *
+ * <p> In this sample, Weight is embedded in part data values which are stored
+ * as Serial serialized objects; therefore Weight must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Weight implements Serializable {
+
+ public final static String GRAMS = "grams";
+ public final static String OUNCES = "ounces";
+
+ private double amount;
+ private String units;
+
+ public Weight(double amount, String units) {
+
+ this.amount = amount;
+ this.units = units;
+ }
+
+ public final double getAmount() {
+
+ return amount;
+ }
+
+ public final String getUnits() {
+
+ return units;
+ }
+
+ public String toString() {
+
+ return "[" + amount + ' ' + units + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/Part.java b/examples_java/src/collections/ship/factory/Part.java
new file mode 100644
index 0000000..af28e5a
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/Part.java
@@ -0,0 +1,106 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import java.io.Serializable;
+
+import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A Part represents the combined key/data pair for a part entity.
+ *
+ * <p> In this sample, Part is bound to the stored key/data entry by
+ * implementing the MarshalledTupleKeyEntity interface. </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s)
+ * are transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a PartData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Part implements Serializable, MarshalledTupleKeyEntity {
+
+ private transient String number;
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public Part(String number, String name, String color, Weight weight,
+ String city) {
+
+ this.number = number;
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Part: number=" + number +
+ " name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+
+ // --- MarshalledTupleKeyEntity implementation ---
+
+ public void marshalPrimaryKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.number);
+ }
+
+ public void unmarshalPrimaryKey(TupleInput keyInput) {
+
+ this.number = keyInput.readString();
+ }
+
+ public boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput) {
+
+ throw new UnsupportedOperationException(keyName);
+ }
+
+ public boolean nullifyForeignKey(String keyName) {
+
+ throw new UnsupportedOperationException(keyName);
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/PartKey.java b/examples_java/src/collections/ship/factory/PartKey.java
new file mode 100644
index 0000000..ad7f448
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/PartKey.java
@@ -0,0 +1,60 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import com.sleepycat.bind.tuple.MarshalledTupleEntry;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A PartKey serves as the key in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartKey is bound to the stored key tuple entry by
+ * implementing the MarshalledTupleEntry interface, which is called by {@link
+ * SampleViews.MarshalledKeyBinding}. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartKey implements MarshalledTupleEntry {
+
+ private String number;
+
+ public PartKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[PartKey: number=" + number + ']';
+ }
+
+ // --- MarshalledTupleEntry implementation ---
+
+ public PartKey() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void marshalEntry(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.number);
+ }
+
+ public void unmarshalEntry(TupleInput keyInput) {
+
+ this.number = keyInput.readString();
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/Sample.java b/examples_java/src/collections/ship/factory/Sample.java
new file mode 100644
index 0000000..4fbb357
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/Sample.java
@@ -0,0 +1,234 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import java.util.Iterator;
+import java.util.Set;
+
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+
+/**
+ * Sample is the main entry point for the sample program and may be run as
+ * follows:
+ *
+ * <pre>
+ * java collections.ship.factory.Sample
+ * [-h <home-directory> ]
+ * </pre>
+ *
+ * <p> The default for the home directory is ./tmp -- the tmp subdirectory of
+ * the current directory where the sample is run. To specify a different home
+ * directory, use the -home option. The home directory must exist before
+ * running the sample. To recreate the sample database from scratch, delete
+ * all files in the home directory before running the sample. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Sample {
+
+ private SampleDatabase db;
+ private SampleViews views;
+
+ /**
+ * Run the sample program.
+ */
+ public static void main(String[] args) {
+
+ System.out.println("\nRunning sample: " + Sample.class);
+
+ // Parse the command line arguments.
+ //
+ String homeDir = "./tmp";
+ for (int i = 0; i < args.length; i += 1) {
+ if (args[i].equals("-h") && i < args.length - 1) {
+ i += 1;
+ homeDir = args[i];
+ } else {
+ System.err.println("Usage:\n java " + Sample.class.getName() +
+ "\n [-h <home-directory>]");
+ System.exit(2);
+ }
+ }
+
+ // Run the sample.
+ //
+ Sample sample = null;
+ try {
+ sample = new Sample(homeDir);
+ sample.run();
+ } catch (Exception e) {
+ // If an exception reaches this point, the last transaction did not
+ // complete. If the exception is RunRecoveryException, follow
+ // the Berkeley DB recovery procedures before running again.
+ e.printStackTrace();
+ } finally {
+ if (sample != null) {
+ try {
+ // Always attempt to close the database cleanly.
+ sample.close();
+ } catch (Exception e) {
+ System.err.println("Exception during database close:");
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Open the database and views.
+ */
+ private Sample(String homeDir)
+ throws Exception {
+
+ db = new SampleDatabase(homeDir);
+ views = new SampleViews(db);
+ }
+
+ /**
+ * Close the database cleanly.
+ */
+ private void close()
+ throws Exception {
+
+ db.close();
+ }
+
+ /**
+ * Run two transactions to populate and print the database. A
+ * TransactionRunner is used to ensure consistent handling of transactions,
+ * including deadlock retries. But the best transaction handling mechanism
+ * to use depends on the application.
+ */
+ private void run()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(db.getEnvironment());
+ runner.run(new PopulateDatabase());
+ runner.run(new PrintDatabase());
+ }
+
+ /**
+ * Populate the database in a single transaction.
+ */
+ private class PopulateDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ addSuppliers();
+ addParts();
+ addShipments();
+ }
+ }
+
+ /**
+ * Print the database in a single transaction. All entities are printed
+ * and the indices are used to print the entities for certain keys.
+ *
+ * <p> Note the use of special iterator() methods. These are used here
+ * with indices to find the shipments for certain keys.</p>
+ */
+ private class PrintDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ printValues("Parts",
+ views.getPartSet().iterator());
+ printValues("Suppliers",
+ views.getSupplierSet().iterator());
+ printValues("Suppliers for City Paris",
+ views.getSupplierByCityMap().duplicates(
+ "Paris").iterator());
+ printValues("Shipments",
+ views.getShipmentSet().iterator());
+ printValues("Shipments for Part P1",
+ views.getShipmentByPartMap().duplicates(
+ new PartKey("P1")).iterator());
+ printValues("Shipments for Supplier S1",
+ views.getShipmentBySupplierMap().duplicates(
+ new SupplierKey("S1")).iterator());
+ }
+ }
+
+ /**
+ * Populate the part entities in the database. If the part set is not
+ * empty, assume that this has already been done.
+ */
+ private void addParts() {
+
+ Set parts = views.getPartSet();
+ if (parts.isEmpty()) {
+ System.out.println("Adding Parts");
+ parts.add(new Part("P1", "Nut", "Red",
+ new Weight(12.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P2", "Bolt", "Green",
+ new Weight(17.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P3", "Screw", "Blue",
+ new Weight(17.0, Weight.GRAMS), "Rome"));
+ parts.add(new Part("P4", "Screw", "Red",
+ new Weight(14.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P5", "Cam", "Blue",
+ new Weight(12.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P6", "Cog", "Red",
+ new Weight(19.0, Weight.GRAMS), "London"));
+ }
+ }
+
+ /**
+ * Populate the supplier entities in the database. If the supplier set is
+ * not empty, assume that this has already been done.
+ */
+ private void addSuppliers() {
+
+ Set suppliers = views.getSupplierSet();
+ if (suppliers.isEmpty()) {
+ System.out.println("Adding Suppliers");
+ suppliers.add(new Supplier("S1", "Smith", 20, "London"));
+ suppliers.add(new Supplier("S2", "Jones", 10, "Paris"));
+ suppliers.add(new Supplier("S3", "Blake", 30, "Paris"));
+ suppliers.add(new Supplier("S4", "Clark", 20, "London"));
+ suppliers.add(new Supplier("S5", "Adams", 30, "Athens"));
+ }
+ }
+
+ /**
+ * Populate the shipment entities in the database. If the shipment set
+ * is not empty, assume that this has already been done.
+ */
+ private void addShipments() {
+
+ Set shipments = views.getShipmentSet();
+ if (shipments.isEmpty()) {
+ System.out.println("Adding Shipments");
+ shipments.add(new Shipment("P1", "S1", 300));
+ shipments.add(new Shipment("P2", "S1", 200));
+ shipments.add(new Shipment("P3", "S1", 400));
+ shipments.add(new Shipment("P4", "S1", 200));
+ shipments.add(new Shipment("P5", "S1", 100));
+ shipments.add(new Shipment("P6", "S1", 100));
+ shipments.add(new Shipment("P1", "S2", 300));
+ shipments.add(new Shipment("P2", "S2", 400));
+ shipments.add(new Shipment("P2", "S3", 200));
+ shipments.add(new Shipment("P2", "S4", 200));
+ shipments.add(new Shipment("P4", "S4", 300));
+ shipments.add(new Shipment("P5", "S4", 400));
+ }
+ }
+
+ /**
+ * Print the objects returned by an iterator of entity value objects.
+ */
+ private void printValues(String label, Iterator iterator) {
+
+ System.out.println("\n--- " + label + " ---");
+ while (iterator.hasNext()) {
+ System.out.println(iterator.next().toString());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/SampleDatabase.java b/examples_java/src/collections/ship/factory/SampleDatabase.java
new file mode 100644
index 0000000..bb00840
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/SampleDatabase.java
@@ -0,0 +1,226 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.collections.TupleSerialFactory;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+
+/**
+ * SampleDatabase defines the storage containers, indices and foreign keys
+ * for the sample database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleDatabase {
+
+ private static final String CLASS_CATALOG = "java_class_catalog";
+ private static final String SUPPLIER_STORE = "supplier_store";
+ private static final String PART_STORE = "part_store";
+ private static final String SHIPMENT_STORE = "shipment_store";
+ private static final String SHIPMENT_PART_INDEX = "shipment_part_index";
+ private static final String SHIPMENT_SUPPLIER_INDEX =
+ "shipment_supplier_index";
+ private static final String SUPPLIER_CITY_INDEX = "supplier_city_index";
+
+ private Environment env;
+ private Database partDb;
+ private Database supplierDb;
+ private Database shipmentDb;
+ private SecondaryDatabase supplierByCityDb;
+ private SecondaryDatabase shipmentByPartDb;
+ private SecondaryDatabase shipmentBySupplierDb;
+ private StoredClassCatalog javaCatalog;
+ private TupleSerialFactory factory;
+
+ /**
+ * Open all storage containers, indices, and catalogs.
+ */
+ public SampleDatabase(String homeDirectory)
+ throws DatabaseException, FileNotFoundException {
+
+ // Open the Berkeley DB environment in transactional mode.
+ //
+ System.out.println("Opening environment in: " + homeDirectory);
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setAllowCreate(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(new File(homeDirectory), envConfig);
+
+ // Set the Berkeley DB config for opening all stores.
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+
+ // Create the Serial class catalog. This holds the serialized class
+ // format for all database records of serial format.
+ //
+ Database catalogDb = env.openDatabase(null, CLASS_CATALOG, null,
+ dbConfig);
+ javaCatalog = new StoredClassCatalog(catalogDb);
+
+ // Use the TupleSerialDbFactory for a Serial/Tuple-based database
+ // where marshalling interfaces are used.
+ //
+ factory = new TupleSerialFactory(javaCatalog);
+
+ // Open the Berkeley DB database for the part, supplier and shipment
+ // stores. The stores are opened with no duplicate keys allowed.
+ //
+ partDb = env.openDatabase(null, PART_STORE, null, dbConfig);
+
+ supplierDb = env.openDatabase(null, SUPPLIER_STORE, null, dbConfig);
+
+ shipmentDb = env.openDatabase(null, SHIPMENT_STORE, null, dbConfig);
+
+ // Open the SecondaryDatabase for the city index of the supplier store,
+ // and for the part and supplier indices of the shipment store.
+ // Duplicate keys are allowed since more than one supplier may be in
+ // the same city, and more than one shipment may exist for the same
+ // supplier or part. A foreign key constraint is defined for the
+ // supplier and part indices to ensure that a shipment only refers to
+ // existing part and supplier keys. The CASCADE delete action means
+ // that shipments will be deleted if their associated part or supplier
+ // is deleted.
+ //
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ secConfig.setType(DatabaseType.BTREE);
+ secConfig.setSortedDuplicates(true);
+
+ secConfig.setKeyCreator(factory.getKeyCreator(Supplier.class,
+ Supplier.CITY_KEY));
+ supplierByCityDb = env.openSecondaryDatabase(null, SUPPLIER_CITY_INDEX,
+ null, supplierDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(partDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(factory.getKeyCreator(Shipment.class,
+ Shipment.PART_KEY));
+ shipmentByPartDb = env.openSecondaryDatabase(null, SHIPMENT_PART_INDEX,
+ null, shipmentDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(supplierDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(factory.getKeyCreator(Shipment.class,
+ Shipment.SUPPLIER_KEY));
+ shipmentBySupplierDb = env.openSecondaryDatabase(null,
+ SHIPMENT_SUPPLIER_INDEX,
+ null, shipmentDb,
+ secConfig);
+ }
+
+ /**
+ * Return the tuple-serial factory.
+ */
+ public final TupleSerialFactory getFactory() {
+
+ return factory;
+ }
+
+ /**
+ * Return the storage environment for the database.
+ */
+ public final Environment getEnvironment() {
+
+ return env;
+ }
+
+ /**
+ * Return the class catalog.
+ */
+ public final StoredClassCatalog getClassCatalog() {
+
+ return javaCatalog;
+ }
+
+ /**
+ * Return the part storage container.
+ */
+ public final Database getPartDatabase() {
+
+ return partDb;
+ }
+
+ /**
+ * Return the supplier storage container.
+ */
+ public final Database getSupplierDatabase() {
+
+ return supplierDb;
+ }
+
+ /**
+ * Return the shipment storage container.
+ */
+ public final Database getShipmentDatabase() {
+
+ return shipmentDb;
+ }
+
+ /**
+ * Return the shipment-by-part index.
+ */
+ public final SecondaryDatabase getShipmentByPartDatabase() {
+
+ return shipmentByPartDb;
+ }
+
+ /**
+ * Return the shipment-by-supplier index.
+ */
+ public final SecondaryDatabase getShipmentBySupplierDatabase() {
+
+ return shipmentBySupplierDb;
+ }
+
+ /**
+ * Return the supplier-by-city index.
+ */
+ public final SecondaryDatabase getSupplierByCityDatabase() {
+
+ return supplierByCityDb;
+ }
+
+ /**
+ * Close all databases and the environment.
+ */
+ public void close()
+ throws DatabaseException {
+
+ // Close secondary databases, then primary databases.
+ supplierByCityDb.close();
+ shipmentByPartDb.close();
+ shipmentBySupplierDb.close();
+ partDb.close();
+ supplierDb.close();
+ shipmentDb.close();
+ // And don't forget to close the catalog and the environment.
+ javaCatalog.close();
+ env.close();
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/SampleViews.java b/examples_java/src/collections/ship/factory/SampleViews.java
new file mode 100644
index 0000000..bb03d8a
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/SampleViews.java
@@ -0,0 +1,142 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.StoredSortedValueSet;
+import com.sleepycat.collections.TupleSerialFactory;
+
+/**
+ * SampleViews defines the data bindings and collection views for the sample
+ * database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleViews {
+
+ private StoredSortedMap partMap;
+ private StoredSortedMap supplierMap;
+ private StoredSortedMap shipmentMap;
+ private StoredSortedMap shipmentByPartMap;
+ private StoredSortedMap shipmentBySupplierMap;
+ private StoredSortedMap supplierByCityMap;
+
+ /**
+ * Create the data bindings and collection views.
+ */
+ public SampleViews(SampleDatabase db) {
+
+ // Use the TupleSerialFactory for a Serial/Tuple-based database
+ // where marshalling interfaces are used.
+ //
+ TupleSerialFactory factory = db.getFactory();
+
+ // Create map views for all stores and indices.
+ // StoredSortedMap is used since the stores and indices are ordered
+ // (they use the DB_BTREE access method).
+ //
+ partMap =
+ factory.newSortedMap(db.getPartDatabase(),
+ PartKey.class, Part.class, true);
+ supplierMap =
+ factory.newSortedMap(db.getSupplierDatabase(),
+ SupplierKey.class, Supplier.class, true);
+ shipmentMap =
+ factory.newSortedMap(db.getShipmentDatabase(),
+ ShipmentKey.class, Shipment.class, true);
+ shipmentByPartMap =
+ factory.newSortedMap(db.getShipmentByPartDatabase(),
+ PartKey.class, Shipment.class, true);
+ shipmentBySupplierMap =
+ factory.newSortedMap(db.getShipmentBySupplierDatabase(),
+ SupplierKey.class, Shipment.class, true);
+ supplierByCityMap =
+ factory.newSortedMap(db.getSupplierByCityDatabase(),
+ String.class, Supplier.class, true);
+ }
+
+ // The views returned below can be accessed using the java.util.Map or
+ // java.util.Set interfaces, or using the StoredMap and StoredValueSet
+ // classes, which provide additional methods. The entity sets could be
+ // obtained directly from the Map.values() method but convenience methods
+ // are provided here to return them in order to avoid down-casting
+ // elsewhere.
+
+ /**
+ * Return a map view of the part storage container.
+ */
+ public StoredSortedMap getPartMap() {
+
+ return partMap;
+ }
+
+ /**
+ * Return a map view of the supplier storage container.
+ */
+ public StoredSortedMap getSupplierMap() {
+
+ return supplierMap;
+ }
+
+ /**
+ * Return a map view of the shipment storage container.
+ */
+ public StoredSortedMap getShipmentMap() {
+
+ return shipmentMap;
+ }
+
+ /**
+ * Return an entity set view of the part storage container.
+ */
+ public StoredSortedValueSet getPartSet() {
+
+ return (StoredSortedValueSet) partMap.values();
+ }
+
+ /**
+ * Return an entity set view of the supplier storage container.
+ */
+ public StoredSortedValueSet getSupplierSet() {
+
+ return (StoredSortedValueSet) supplierMap.values();
+ }
+
+ /**
+ * Return an entity set view of the shipment storage container.
+ */
+ public StoredSortedValueSet getShipmentSet() {
+
+ return (StoredSortedValueSet) shipmentMap.values();
+ }
+
+ /**
+ * Return a map view of the shipment-by-part index.
+ */
+ public StoredSortedMap getShipmentByPartMap() {
+
+ return shipmentByPartMap;
+ }
+
+ /**
+ * Return a map view of the shipment-by-supplier index.
+ */
+ public StoredSortedMap getShipmentBySupplierMap() {
+
+ return shipmentBySupplierMap;
+ }
+
+ /**
+ * Return a map view of the supplier-by-city index.
+ */
+ public StoredSortedMap getSupplierByCityMap() {
+
+ return supplierByCityMap;
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/Shipment.java b/examples_java/src/collections/ship/factory/Shipment.java
new file mode 100644
index 0000000..eabed86
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/Shipment.java
@@ -0,0 +1,102 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import java.io.Serializable;
+
+import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A Shipment represents the combined key/data pair for a shipment entity.
+ *
+ * <p> In this sample, Shipment is bound to the stored key/data entry by
+ * implementing the MarshalledTupleKeyEntity interface. </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s)
+ * are transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a ShipmentData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Shipment implements Serializable, MarshalledTupleKeyEntity {
+
+ static final String PART_KEY = "part";
+ static final String SUPPLIER_KEY = "supplier";
+
+ private transient String partNumber;
+ private transient String supplierNumber;
+ private int quantity;
+
+ public Shipment(String partNumber, String supplierNumber, int quantity) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ this.quantity = quantity;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[Shipment: part=" + partNumber +
+ " supplier=" + supplierNumber +
+ " quantity=" + quantity + ']';
+ }
+
+ // --- MarshalledTupleKeyEntity implementation ---
+
+ public void marshalPrimaryKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.partNumber);
+ keyOutput.writeString(this.supplierNumber);
+ }
+
+ public void unmarshalPrimaryKey(TupleInput keyInput) {
+
+ this.partNumber = keyInput.readString();
+ this.supplierNumber = keyInput.readString();
+ }
+
+ public boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput) {
+
+ if (keyName.equals(PART_KEY)) {
+ keyOutput.writeString(this.partNumber);
+ return true;
+ } else if (keyName.equals(SUPPLIER_KEY)) {
+ keyOutput.writeString(this.supplierNumber);
+ return true;
+ } else {
+ throw new UnsupportedOperationException(keyName);
+ }
+ }
+
+ public boolean nullifyForeignKey(String keyName) {
+
+ throw new UnsupportedOperationException(keyName);
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/ShipmentKey.java b/examples_java/src/collections/ship/factory/ShipmentKey.java
new file mode 100644
index 0000000..5258fbe
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/ShipmentKey.java
@@ -0,0 +1,70 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import com.sleepycat.bind.tuple.MarshalledTupleEntry;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A ShipmentKey serves as the key in the key/data pair for a shipment entity.
+ *
+ * <p> In this sample, ShipmentKey is bound to the stored key tuple entry by
+ * implementing the MarshalledTupleEntry interface, which is called by {@link
+ * SampleViews.MarshalledKeyBinding}. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentKey implements MarshalledTupleEntry {
+
+ private String partNumber;
+ private String supplierNumber;
+
+ public ShipmentKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public String toString() {
+
+ return "[ShipmentKey: supplier=" + supplierNumber +
+ " part=" + partNumber + ']';
+ }
+
+ // --- MarshalledTupleEntry implementation ---
+
+ public ShipmentKey() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void marshalEntry(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.partNumber);
+ keyOutput.writeString(this.supplierNumber);
+ }
+
+ public void unmarshalEntry(TupleInput keyInput) {
+
+ this.partNumber = keyInput.readString();
+ this.supplierNumber = keyInput.readString();
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/Supplier.java b/examples_java/src/collections/ship/factory/Supplier.java
new file mode 100644
index 0000000..0091cbc
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/Supplier.java
@@ -0,0 +1,108 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import java.io.Serializable;
+
+import com.sleepycat.bind.tuple.MarshalledTupleKeyEntity;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A Supplier represents the combined key/data pair for a supplier entity.
+ *
+ * <p> In this sample, Supplier is bound to the stored key/data entry by
+ * implementing the MarshalledTupleKeyEntity interface. </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s) are
+ * transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a SupplierData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Supplier implements Serializable, MarshalledTupleKeyEntity {
+
+ static final String CITY_KEY = "city";
+
+ private transient String number;
+ private String name;
+ private int status;
+ private String city;
+
+ public Supplier(String number, String name, int status, String city) {
+
+ this.number = number;
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Supplier: number=" + number +
+ " name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+
+ // --- MarshalledTupleKeyEntity implementation ---
+
+ public void marshalPrimaryKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.number);
+ }
+
+ public void unmarshalPrimaryKey(TupleInput keyInput) {
+
+ this.number = keyInput.readString();
+ }
+
+ public boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput) {
+
+ if (keyName.equals(CITY_KEY)) {
+ if (this.city != null) {
+ keyOutput.writeString(this.city);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ throw new UnsupportedOperationException(keyName);
+ }
+ }
+
+ public boolean nullifyForeignKey(String keyName) {
+
+ throw new UnsupportedOperationException(keyName);
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/SupplierKey.java b/examples_java/src/collections/ship/factory/SupplierKey.java
new file mode 100644
index 0000000..c4278b9
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/SupplierKey.java
@@ -0,0 +1,60 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import com.sleepycat.bind.tuple.MarshalledTupleEntry;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A SupplierKey serves as the key in the key/data pair for a supplier entity.
+ *
+ * <p> In this sample, SupplierKey is bound to the stored key tuple entry by
+ * implementing the MarshalledTupleEntry interface, which is called by {@link
+ * SampleViews.MarshalledKeyBinding}. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierKey implements MarshalledTupleEntry {
+
+ private String number;
+
+ public SupplierKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[SupplierKey: number=" + number + ']';
+ }
+
+ // --- MarshalledTupleEntry implementation ---
+
+ public SupplierKey() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void marshalEntry(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.number);
+ }
+
+ public void unmarshalEntry(TupleInput keyInput) {
+
+ this.number = keyInput.readString();
+ }
+}
diff --git a/examples_java/src/collections/ship/factory/Weight.java b/examples_java/src/collections/ship/factory/Weight.java
new file mode 100644
index 0000000..95cb0ed
--- /dev/null
+++ b/examples_java/src/collections/ship/factory/Weight.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.factory;
+
+import java.io.Serializable;
+
+/**
+ * Weight represents a weight amount and unit of measure.
+ *
+ * <p> In this sample, Weight is embedded in part data values which are stored
+ * as Java serialized objects; therefore Weight must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Weight implements Serializable {
+
+ public final static String GRAMS = "grams";
+ public final static String OUNCES = "ounces";
+
+ private double amount;
+ private String units;
+
+ public Weight(double amount, String units) {
+
+ this.amount = amount;
+ this.units = units;
+ }
+
+ public final double getAmount() {
+
+ return amount;
+ }
+
+ public final String getUnits() {
+
+ return units;
+ }
+
+ public String toString() {
+
+ return "[" + amount + ' ' + units + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/index/PartData.java b/examples_java/src/collections/ship/index/PartData.java
new file mode 100644
index 0000000..a213f46
--- /dev/null
+++ b/examples_java/src/collections/ship/index/PartData.java
@@ -0,0 +1,64 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.Serializable;
+
+/**
+ * A PartData serves as the data in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartData is used both as the storage data for the data
+ * as well as the object binding to the data. Because it is used directly as
+ * storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartData implements Serializable {
+
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public PartData(String name, String color, Weight weight, String city) {
+
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[PartData: name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/index/PartKey.java b/examples_java/src/collections/ship/index/PartKey.java
new file mode 100644
index 0000000..c9d0f8a
--- /dev/null
+++ b/examples_java/src/collections/ship/index/PartKey.java
@@ -0,0 +1,40 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.Serializable;
+
+/**
+ * A PartKey serves as the key in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartKey is used both as the storage data for the key as
+ * well as the object binding to the key. Because it is used directly as
+ * storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartKey implements Serializable {
+
+ private String number;
+
+ public PartKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[PartKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/index/Sample.java b/examples_java/src/collections/ship/index/Sample.java
new file mode 100644
index 0000000..16cad43
--- /dev/null
+++ b/examples_java/src/collections/ship/index/Sample.java
@@ -0,0 +1,278 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.Map;
+
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * Sample is the main entry point for the sample program and may be run as
+ * follows:
+ *
+ * <pre>
+ * java collections.ship.index.Sample
+ * [-h <home-directory> ]
+ * </pre>
+ *
+ * <p> The default for the home directory is ./tmp -- the tmp subdirectory of
+ * the current directory where the sample is run. The home directory must exist
+ * before running the sample. To recreate the sample database from scratch,
+ * delete all files in the home directory before running the sample. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Sample {
+
+ private SampleDatabase db;
+ private SampleViews views;
+
+ /**
+ * Run the sample program.
+ */
+ public static void main(String[] args) {
+
+ System.out.println("\nRunning sample: " + Sample.class);
+
+ // Parse the command line arguments.
+ //
+ String homeDir = "./tmp";
+ for (int i = 0; i < args.length; i += 1) {
+ if (args[i].equals("-h") && i < args.length - 1) {
+ i += 1;
+ homeDir = args[i];
+ } else {
+ System.err.println("Usage:\n java " + Sample.class.getName() +
+ "\n [-h <home-directory>]");
+ System.exit(2);
+ }
+ }
+
+ // Run the sample.
+ //
+ Sample sample = null;
+ try {
+ sample = new Sample(homeDir);
+ sample.run();
+ } catch (Exception e) {
+ // If an exception reaches this point, the last transaction did not
+ // complete. If the exception is RunRecoveryException, follow
+ // the Berkeley DB recovery procedures before running again.
+ e.printStackTrace();
+ } finally {
+ if (sample != null) {
+ try {
+ // Always attempt to close the database cleanly.
+ sample.close();
+ } catch (Exception e) {
+ System.err.println("Exception during database close:");
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Open the database and views.
+ */
+ private Sample(String homeDir)
+ throws DatabaseException, FileNotFoundException {
+
+ db = new SampleDatabase(homeDir);
+ views = new SampleViews(db);
+ }
+
+ /**
+ * Close the database cleanly.
+ */
+ private void close()
+ throws DatabaseException {
+
+ db.close();
+ }
+
+ /**
+ * Run two transactions to populate and print the database. A
+ * TransactionRunner is used to ensure consistent handling of transactions,
+ * including deadlock retries. But the best transaction handling mechanism
+ * to use depends on the application.
+ */
+ private void run()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(db.getEnvironment());
+ runner.run(new PopulateDatabase());
+ runner.run(new PrintDatabase());
+ }
+
+ /**
+ * Populate the database in a single transaction.
+ */
+ private class PopulateDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ addSuppliers();
+ addParts();
+ addShipments();
+ }
+ }
+
+ /**
+ * Print the database in a single transaction. All entities are printed
+ * and the indices are used to print the entities for certain keys.
+ *
+ * <p> Note the use of special iterator() methods. These are used here
+ * with indices to find the shipments for certain keys.</p>
+ */
+ private class PrintDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ printEntries("Parts",
+ views.getPartEntrySet().iterator());
+ printEntries("Suppliers",
+ views.getSupplierEntrySet().iterator());
+ printValues("Suppliers for City Paris",
+ views.getSupplierByCityMap().duplicates(
+ "Paris").iterator());
+ printEntries("Shipments",
+ views.getShipmentEntrySet().iterator());
+ printValues("Shipments for Part P1",
+ views.getShipmentByPartMap().duplicates(
+ new PartKey("P1")).iterator());
+ printValues("Shipments for Supplier S1",
+ views.getShipmentBySupplierMap().duplicates(
+ new SupplierKey("S1")).iterator());
+ }
+ }
+
+ /**
+ * Populate the part entities in the database. If the part map is not
+ * empty, assume that this has already been done.
+ */
+ private void addParts() {
+
+ Map parts = views.getPartMap();
+ if (parts.isEmpty()) {
+ System.out.println("Adding Parts");
+ parts.put(new PartKey("P1"),
+ new PartData("Nut", "Red",
+ new Weight(12.0, Weight.GRAMS),
+ "London"));
+ parts.put(new PartKey("P2"),
+ new PartData("Bolt", "Green",
+ new Weight(17.0, Weight.GRAMS),
+ "Paris"));
+ parts.put(new PartKey("P3"),
+ new PartData("Screw", "Blue",
+ new Weight(17.0, Weight.GRAMS),
+ "Rome"));
+ parts.put(new PartKey("P4"),
+ new PartData("Screw", "Red",
+ new Weight(14.0, Weight.GRAMS),
+ "London"));
+ parts.put(new PartKey("P5"),
+ new PartData("Cam", "Blue",
+ new Weight(12.0, Weight.GRAMS),
+ "Paris"));
+ parts.put(new PartKey("P6"),
+ new PartData("Cog", "Red",
+ new Weight(19.0, Weight.GRAMS),
+ "London"));
+ }
+ }
+
+ /**
+ * Populate the supplier entities in the database. If the supplier map is
+ * not empty, assume that this has already been done.
+ */
+ private void addSuppliers() {
+
+ Map suppliers = views.getSupplierMap();
+ if (suppliers.isEmpty()) {
+ System.out.println("Adding Suppliers");
+ suppliers.put(new SupplierKey("S1"),
+ new SupplierData("Smith", 20, "London"));
+ suppliers.put(new SupplierKey("S2"),
+ new SupplierData("Jones", 10, "Paris"));
+ suppliers.put(new SupplierKey("S3"),
+ new SupplierData("Blake", 30, "Paris"));
+ suppliers.put(new SupplierKey("S4"),
+ new SupplierData("Clark", 20, "London"));
+ suppliers.put(new SupplierKey("S5"),
+ new SupplierData("Adams", 30, "Athens"));
+ }
+ }
+
+ /**
+ * Populate the shipment entities in the database. If the shipment map
+ * is not empty, assume that this has already been done.
+ */
+ private void addShipments() {
+
+ Map shipments = views.getShipmentMap();
+ if (shipments.isEmpty()) {
+ System.out.println("Adding Shipments");
+ shipments.put(new ShipmentKey("P1", "S1"),
+ new ShipmentData(300));
+ shipments.put(new ShipmentKey("P2", "S1"),
+ new ShipmentData(200));
+ shipments.put(new ShipmentKey("P3", "S1"),
+ new ShipmentData(400));
+ shipments.put(new ShipmentKey("P4", "S1"),
+ new ShipmentData(200));
+ shipments.put(new ShipmentKey("P5", "S1"),
+ new ShipmentData(100));
+ shipments.put(new ShipmentKey("P6", "S1"),
+ new ShipmentData(100));
+ shipments.put(new ShipmentKey("P1", "S2"),
+ new ShipmentData(300));
+ shipments.put(new ShipmentKey("P2", "S2"),
+ new ShipmentData(400));
+ shipments.put(new ShipmentKey("P2", "S3"),
+ new ShipmentData(200));
+ shipments.put(new ShipmentKey("P2", "S4"),
+ new ShipmentData(200));
+ shipments.put(new ShipmentKey("P4", "S4"),
+ new ShipmentData(300));
+ shipments.put(new ShipmentKey("P5", "S4"),
+ new ShipmentData(400));
+ }
+ }
+
+ /**
+ * Print the key/value objects returned by an iterator of Map.Entry
+ * objects.
+ */
+ private void printEntries(String label, Iterator iterator) {
+
+ System.out.println("\n--- " + label + " ---");
+ while (iterator.hasNext()) {
+ Map.Entry entry = (Map.Entry) iterator.next();
+ System.out.println(entry.getKey().toString());
+ System.out.println(entry.getValue().toString());
+ }
+ }
+
+ /**
+ * Print the objects returned by an iterator of value objects.
+ */
+ private void printValues(String label, Iterator iterator) {
+
+ System.out.println("\n--- " + label + " ---");
+ while (iterator.hasNext()) {
+ System.out.println(iterator.next().toString());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/index/SampleDatabase.java b/examples_java/src/collections/ship/index/SampleDatabase.java
new file mode 100644
index 0000000..5903edf
--- /dev/null
+++ b/examples_java/src/collections/ship/index/SampleDatabase.java
@@ -0,0 +1,331 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.SerialSerialKeyCreator;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+
+/**
+ * SampleDatabase defines the storage containers, indices and foreign keys
+ * for the sample database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleDatabase {
+
+ private static final String CLASS_CATALOG = "java_class_catalog";
+ private static final String SUPPLIER_STORE = "supplier_store";
+ private static final String PART_STORE = "part_store";
+ private static final String SHIPMENT_STORE = "shipment_store";
+ private static final String SHIPMENT_PART_INDEX = "shipment_part_index";
+ private static final String SHIPMENT_SUPPLIER_INDEX =
+ "shipment_supplier_index";
+ private static final String SUPPLIER_CITY_INDEX = "supplier_city_index";
+
+ private Environment env;
+ private Database partDb;
+ private Database supplierDb;
+ private Database shipmentDb;
+ private SecondaryDatabase supplierByCityDb;
+ private SecondaryDatabase shipmentByPartDb;
+ private SecondaryDatabase shipmentBySupplierDb;
+ private StoredClassCatalog javaCatalog;
+
+ /**
+ * Open all storage containers, indices, and catalogs.
+ */
+ public SampleDatabase(String homeDirectory)
+ throws DatabaseException, FileNotFoundException {
+
+ // Open the Berkeley DB environment in transactional mode.
+ //
+ System.out.println("Opening environment in: " + homeDirectory);
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setAllowCreate(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(new File(homeDirectory), envConfig);
+
+ // Set the Berkeley DB config for opening all stores.
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+
+ // Create the Serial class catalog. This holds the serialized class
+ // format for all database records of serial format.
+ //
+ Database catalogDb = env.openDatabase(null, CLASS_CATALOG, null,
+ dbConfig);
+ javaCatalog = new StoredClassCatalog(catalogDb);
+
+ // Open the Berkeley DB database for the part, supplier and shipment
+ // stores. The stores are opened with no duplicate keys allowed.
+ //
+ partDb = env.openDatabase(null, PART_STORE, null, dbConfig);
+
+ supplierDb = env.openDatabase(null, SUPPLIER_STORE, null, dbConfig);
+
+ shipmentDb = env.openDatabase(null, SHIPMENT_STORE, null, dbConfig);
+
+ // Open the SecondaryDatabase for the city index of the supplier store,
+ // and for the part and supplier indices of the shipment store.
+ // Duplicate keys are allowed since more than one supplier may be in
+ // the same city, and more than one shipment may exist for the same
+ // supplier or part. A foreign key constraint is defined for the
+ // supplier and part indices to ensure that a shipment only refers to
+ // existing part and supplier keys. The CASCADE delete action means
+ // that shipments will be deleted if their associated part or supplier
+ // is deleted.
+ //
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ secConfig.setType(DatabaseType.BTREE);
+ secConfig.setSortedDuplicates(true);
+
+ secConfig.setKeyCreator(
+ new SupplierByCityKeyCreator(javaCatalog,
+ SupplierKey.class,
+ SupplierData.class,
+ String.class));
+ supplierByCityDb = env.openSecondaryDatabase(null, SUPPLIER_CITY_INDEX,
+ null, supplierDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(partDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(
+ new ShipmentByPartKeyCreator(javaCatalog,
+ ShipmentKey.class,
+ ShipmentData.class,
+ PartKey.class));
+ shipmentByPartDb = env.openSecondaryDatabase(null, SHIPMENT_PART_INDEX,
+ null, shipmentDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(supplierDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(
+ new ShipmentBySupplierKeyCreator(javaCatalog,
+ ShipmentKey.class,
+ ShipmentData.class,
+ SupplierKey.class));
+ shipmentBySupplierDb = env.openSecondaryDatabase(null,
+ SHIPMENT_SUPPLIER_INDEX,
+ null, shipmentDb,
+ secConfig);
+ }
+
+ /**
+ * Return the storage environment for the database.
+ */
+ public final Environment getEnvironment() {
+
+ return env;
+ }
+
+ /**
+ * Return the class catalog.
+ */
+ public final StoredClassCatalog getClassCatalog() {
+
+ return javaCatalog;
+ }
+
+ /**
+ * Return the part storage container.
+ */
+ public final Database getPartDatabase() {
+
+ return partDb;
+ }
+
+ /**
+ * Return the supplier storage container.
+ */
+ public final Database getSupplierDatabase() {
+
+ return supplierDb;
+ }
+
+ /**
+ * Return the shipment storage container.
+ */
+ public final Database getShipmentDatabase() {
+
+ return shipmentDb;
+ }
+
+ /**
+ * Return the shipment-by-part index.
+ */
+ public final SecondaryDatabase getShipmentByPartDatabase() {
+
+ return shipmentByPartDb;
+ }
+
+ /**
+ * Return the shipment-by-supplier index.
+ */
+ public final SecondaryDatabase getShipmentBySupplierDatabase() {
+
+ return shipmentBySupplierDb;
+ }
+
+ /**
+ * Return the supplier-by-city index.
+ */
+ public final SecondaryDatabase getSupplierByCityDatabase() {
+
+ return supplierByCityDb;
+ }
+
+ /**
+ * Close all stores (closing a store automatically closes its indices).
+ */
+ public void close()
+ throws DatabaseException {
+
+ // Close secondary databases, then primary databases.
+ supplierByCityDb.close();
+ shipmentByPartDb.close();
+ shipmentBySupplierDb.close();
+ partDb.close();
+ supplierDb.close();
+ shipmentDb.close();
+ // And don't forget to close the catalog and the environment.
+ javaCatalog.close();
+ env.close();
+ }
+
+ /**
+ * The SecondaryKeyCreator for the SupplierByCity index. This is an
+ * extension of the abstract class SerialSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys and value are all
+ * of the serial format.
+ */
+ private static class SupplierByCityKeyCreator
+ extends SerialSerialKeyCreator {
+
+ /**
+ * Construct the city key extractor.
+ * @param catalog is the class catalog.
+ * @param primaryKeyClass is the supplier key class.
+ * @param valueClass is the supplier value class.
+ * @param indexKeyClass is the city key class.
+ */
+ private SupplierByCityKeyCreator(ClassCatalog catalog,
+ Class primaryKeyClass,
+ Class valueClass,
+ Class indexKeyClass) {
+
+ super(catalog, primaryKeyClass, valueClass, indexKeyClass);
+ }
+
+ /**
+ * Extract the city key from a supplier key/value pair. The city key
+ * is stored in the supplier value, so the supplier key is not used.
+ */
+ public Object createSecondaryKey(Object primaryKeyInput,
+ Object valueInput) {
+
+ SupplierData supplierData = (SupplierData) valueInput;
+ return supplierData.getCity();
+ }
+ }
+
+ /**
+ * The SecondaryKeyCreator for the ShipmentByPart index. This is an
+ * extension of the abstract class SerialSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys and value are all
+ * of the serial format.
+ */
+ private static class ShipmentByPartKeyCreator
+ extends SerialSerialKeyCreator {
+
+ /**
+ * Construct the part key extractor.
+ * @param catalog is the class catalog.
+ * @param primaryKeyClass is the shipment key class.
+ * @param valueClass is the shipment value class.
+ * @param indexKeyClass is the part key class.
+ */
+ private ShipmentByPartKeyCreator(ClassCatalog catalog,
+ Class primaryKeyClass,
+ Class valueClass,
+ Class indexKeyClass) {
+
+ super(catalog, primaryKeyClass, valueClass, indexKeyClass);
+ }
+
+ /**
+ * Extract the part key from a shipment key/value pair. The part key
+ * is stored in the shipment key, so the shipment value is not used.
+ */
+ public Object createSecondaryKey(Object primaryKeyInput,
+ Object valueInput) {
+
+ ShipmentKey shipmentKey = (ShipmentKey) primaryKeyInput;
+ return new PartKey(shipmentKey.getPartNumber());
+ }
+ }
+
+ /**
+ * The SecondaryKeyCreator for the ShipmentBySupplier index. This is an
+ * extension of the abstract class SerialSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys and value are all
+ * of the serial format.
+ */
+ private static class ShipmentBySupplierKeyCreator
+ extends SerialSerialKeyCreator {
+
+ /**
+ * Construct the supplier key extractor.
+ * @param catalog is the class catalog.
+ * @param primaryKeyClass is the shipment key class.
+ * @param valueClass is the shipment value class.
+ * @param indexKeyClass is the supplier key class.
+ */
+ private ShipmentBySupplierKeyCreator(ClassCatalog catalog,
+ Class primaryKeyClass,
+ Class valueClass,
+ Class indexKeyClass) {
+
+ super(catalog, primaryKeyClass, valueClass, indexKeyClass);
+ }
+
+ /**
+ * Extract the supplier key from a shipment key/value pair. The part
+ * key is stored in the shipment key, so the shipment value is not
+ * used.
+ */
+ public Object createSecondaryKey(Object primaryKeyInput,
+ Object valueInput) {
+
+ ShipmentKey shipmentKey = (ShipmentKey) primaryKeyInput;
+ return new SupplierKey(shipmentKey.getSupplierNumber());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/index/SampleViews.java b/examples_java/src/collections/ship/index/SampleViews.java
new file mode 100644
index 0000000..3c101c9
--- /dev/null
+++ b/examples_java/src/collections/ship/index/SampleViews.java
@@ -0,0 +1,161 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.collections.StoredEntrySet;
+import com.sleepycat.collections.StoredSortedMap;
+
+/**
+ * SampleViews defines the data bindings and collection views for the sample
+ * database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleViews {
+
+ private StoredSortedMap partMap;
+ private StoredSortedMap supplierMap;
+ private StoredSortedMap shipmentMap;
+ private StoredSortedMap shipmentByPartMap;
+ private StoredSortedMap shipmentBySupplierMap;
+ private StoredSortedMap supplierByCityMap;
+
+ /**
+ * Create the data bindings and collection views.
+ */
+ public SampleViews(SampleDatabase db) {
+
+ // Create the data bindings.
+ // In this sample, the stored key and data entries are used directly
+ // rather than mapping them to separate objects. Therefore, no binding
+ // classes are defined here and the SerialBinding class is used.
+ //
+ ClassCatalog catalog = db.getClassCatalog();
+ EntryBinding partKeyBinding =
+ new SerialBinding(catalog, PartKey.class);
+ EntryBinding partDataBinding =
+ new SerialBinding(catalog, PartData.class);
+ EntryBinding supplierKeyBinding =
+ new SerialBinding(catalog, SupplierKey.class);
+ EntryBinding supplierDataBinding =
+ new SerialBinding(catalog, SupplierData.class);
+ EntryBinding shipmentKeyBinding =
+ new SerialBinding(catalog, ShipmentKey.class);
+ EntryBinding shipmentDataBinding =
+ new SerialBinding(catalog, ShipmentData.class);
+ EntryBinding cityKeyBinding =
+ new SerialBinding(catalog, String.class);
+
+ // Create map views for all stores and indices.
+ // StoredSortedMap is not used since the stores and indices are
+ // ordered by serialized key objects, which do not provide a very
+ // useful ordering.
+ //
+ partMap =
+ new StoredSortedMap(db.getPartDatabase(),
+ partKeyBinding, partDataBinding, true);
+ supplierMap =
+ new StoredSortedMap(db.getSupplierDatabase(),
+ supplierKeyBinding, supplierDataBinding, true);
+ shipmentMap =
+ new StoredSortedMap(db.getShipmentDatabase(),
+ shipmentKeyBinding, shipmentDataBinding, true);
+ shipmentByPartMap =
+ new StoredSortedMap(db.getShipmentByPartDatabase(),
+ partKeyBinding, shipmentDataBinding, true);
+ shipmentBySupplierMap =
+ new StoredSortedMap(db.getShipmentBySupplierDatabase(),
+ supplierKeyBinding, shipmentDataBinding, true);
+ supplierByCityMap =
+ new StoredSortedMap(db.getSupplierByCityDatabase(),
+ cityKeyBinding, supplierDataBinding, true);
+ }
+
+ // The views returned below can be accessed using the java.util.Map or
+ // java.util.Set interfaces, or using the StoredSortedMap and
+ // StoredEntrySet classes, which provide additional methods. The entry
+ // sets could be obtained directly from the Map.entrySet() method, but
+ // convenience methods are provided here to return them in order to avoid
+ // down-casting elsewhere.
+
+ /**
+ * Return a map view of the part storage container.
+ */
+ public final StoredSortedMap getPartMap() {
+
+ return partMap;
+ }
+
+ /**
+ * Return a map view of the supplier storage container.
+ */
+ public final StoredSortedMap getSupplierMap() {
+
+ return supplierMap;
+ }
+
+ /**
+ * Return a map view of the shipment storage container.
+ */
+ public final StoredSortedMap getShipmentMap() {
+
+ return shipmentMap;
+ }
+
+ /**
+ * Return an entry set view of the part storage container.
+ */
+ public final StoredEntrySet getPartEntrySet() {
+
+ return (StoredEntrySet) partMap.entrySet();
+ }
+
+ /**
+ * Return an entry set view of the supplier storage container.
+ */
+ public final StoredEntrySet getSupplierEntrySet() {
+
+ return (StoredEntrySet) supplierMap.entrySet();
+ }
+
+ /**
+ * Return an entry set view of the shipment storage container.
+ */
+ public final StoredEntrySet getShipmentEntrySet() {
+
+ return (StoredEntrySet) shipmentMap.entrySet();
+ }
+
+ /**
+ * Return a map view of the shipment-by-part index.
+ */
+ public StoredSortedMap getShipmentByPartMap() {
+
+ return shipmentByPartMap;
+ }
+
+ /**
+ * Return a map view of the shipment-by-supplier index.
+ */
+ public StoredSortedMap getShipmentBySupplierMap() {
+
+ return shipmentBySupplierMap;
+ }
+
+ /**
+ * Return a map view of the supplier-by-city index.
+ */
+ public final StoredSortedMap getSupplierByCityMap() {
+
+ return supplierByCityMap;
+ }
+}
diff --git a/examples_java/src/collections/ship/index/ShipmentData.java b/examples_java/src/collections/ship/index/ShipmentData.java
new file mode 100644
index 0000000..3a249da
--- /dev/null
+++ b/examples_java/src/collections/ship/index/ShipmentData.java
@@ -0,0 +1,41 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.Serializable;
+
+/**
+ * A ShipmentData serves as the data in the key/data pair for a shipment
+ * entity.
+ *
+ * <p> In this sample, ShipmentData is used both as the storage data for the
+ * data as well as the object binding to the data. Because it is used
+ * directly as storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentData implements Serializable {
+
+ private int quantity;
+
+ public ShipmentData(int quantity) {
+
+ this.quantity = quantity;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[ShipmentData: quantity=" + quantity + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/index/ShipmentKey.java b/examples_java/src/collections/ship/index/ShipmentKey.java
new file mode 100644
index 0000000..63d1a18
--- /dev/null
+++ b/examples_java/src/collections/ship/index/ShipmentKey.java
@@ -0,0 +1,48 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.Serializable;
+
+/**
+ * A ShipmentKey serves as the key in the key/data pair for a shipment entity.
+ *
+ * <p> In this sample, ShipmentKey is used both as the storage data for the key
+ * as well as the object binding to the key. Because it is used directly as
+ * storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentKey implements Serializable {
+
+ private String partNumber;
+ private String supplierNumber;
+
+ public ShipmentKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public String toString() {
+
+ return "[ShipmentKey: supplier=" + supplierNumber +
+ " part=" + partNumber + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/index/SupplierData.java b/examples_java/src/collections/ship/index/SupplierData.java
new file mode 100644
index 0000000..46727f2
--- /dev/null
+++ b/examples_java/src/collections/ship/index/SupplierData.java
@@ -0,0 +1,57 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.Serializable;
+
+/**
+ * A SupplierData serves as the data in the key/data pair for a supplier
+ * entity.
+ *
+ * <p> In this sample, SupplierData is used both as the storage data for the
+ * data as well as the object binding to the data. Because it is used
+ * directly as storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierData implements Serializable {
+
+ private String name;
+ private int status;
+ private String city;
+
+ public SupplierData(String name, int status, String city) {
+
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[SupplierData: name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/index/SupplierKey.java b/examples_java/src/collections/ship/index/SupplierKey.java
new file mode 100644
index 0000000..71b8f22
--- /dev/null
+++ b/examples_java/src/collections/ship/index/SupplierKey.java
@@ -0,0 +1,40 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.Serializable;
+
+/**
+ * A SupplierKey serves as the key in the key/data pair for a supplier entity.
+ *
+ * <p> In this sample, SupplierKey is used both as the storage data for the key
+ * as well as the object binding to the key. Because it is used directly as
+ * storage data using serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierKey implements Serializable {
+
+ private String number;
+
+ public SupplierKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[SupplierKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/index/Weight.java b/examples_java/src/collections/ship/index/Weight.java
new file mode 100644
index 0000000..ee93d29
--- /dev/null
+++ b/examples_java/src/collections/ship/index/Weight.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.index;
+
+import java.io.Serializable;
+
+/**
+ * Weight represents a weight amount and unit of measure.
+ *
+ * <p> In this sample, Weight is embedded in part data values which are stored
+ * as Serial serialized objects; therefore Weight must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Weight implements Serializable {
+
+ public final static String GRAMS = "grams";
+ public final static String OUNCES = "ounces";
+
+ private double amount;
+ private String units;
+
+ public Weight(double amount, String units) {
+
+ this.amount = amount;
+ this.units = units;
+ }
+
+ public final double getAmount() {
+
+ return amount;
+ }
+
+ public final String getUnits() {
+
+ return units;
+ }
+
+ public String toString() {
+
+ return "[" + amount + ' ' + units + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/MarshalledEnt.java b/examples_java/src/collections/ship/marshal/MarshalledEnt.java
new file mode 100644
index 0000000..fefa2be
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/MarshalledEnt.java
@@ -0,0 +1,42 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * MarshalledEnt is implemented by entity (combined key/data) objects and
+ * called by {@link SampleViews.MarshalledEntityBinding}. In this sample,
+ * MarshalledEnt is implemented by {@link Part}, {@link Supplier}, and
+ * {@link Shipment}. This interface is package-protected rather than public
+ * to hide the marshalling interface from other users of the data objects.
+ * Note that a MarshalledEnt must also have a no arguments constructor so
+ * that it can be instantiated by the binding.
+ *
+ * @author Mark Hayes
+ */
+interface MarshalledEnt {
+
+ /**
+ * Extracts the entity's primary key and writes it to the key output.
+ */
+ void marshalPrimaryKey(TupleOutput keyOutput);
+
+ /**
+ * Completes construction of the entity by setting its primary key from the
+ * stored primary key.
+ */
+ void unmarshalPrimaryKey(TupleInput keyInput);
+
+ /**
+ * Extracts the entity's index key and writes it to the key output.
+ */
+ boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput);
+}
diff --git a/examples_java/src/collections/ship/marshal/MarshalledKey.java b/examples_java/src/collections/ship/marshal/MarshalledKey.java
new file mode 100644
index 0000000..8e2d171
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/MarshalledKey.java
@@ -0,0 +1,36 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * MarshalledKey is implemented by key objects and called by {@link
+ * SampleViews.MarshalledKeyBinding}. In this sample, MarshalledKey is
+ * implemented by {@link PartKey}, {@link SupplierKey}, and {@link
+ * ShipmentKey}. This interface is package-protected rather than public to
+ * hide the marshalling interface from other users of the data objects. Note
+ * that a MarshalledKey must also have a no arguments constructor so
+ * that it can be instantiated by the binding.
+ *
+ * @author Mark Hayes
+ */
+interface MarshalledKey {
+
+ /**
+ * Construct the key tuple entry from the key object.
+ */
+ void marshalKey(TupleOutput keyOutput);
+
+ /**
+ * Construct the key object from the key tuple entry.
+ */
+ void unmarshalKey(TupleInput keyInput);
+}
diff --git a/examples_java/src/collections/ship/marshal/Part.java b/examples_java/src/collections/ship/marshal/Part.java
new file mode 100644
index 0000000..ba0a758
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/Part.java
@@ -0,0 +1,116 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import java.io.Serializable;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A Part represents the combined key/data pair for a part entity.
+ *
+ * <p> In this sample, Part is bound to the stored key/data entry by
+ * implementing the MarshalledEnt interface, which is called by {@link
+ * SampleViews.MarshalledEntityBinding}. </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s) are
+ * transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a PartData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Part implements Serializable, MarshalledEnt {
+
+ private transient String number;
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public Part(String number, String name, String color, Weight weight,
+ String city) {
+
+ this.number = number;
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ /**
+ * Set the transient key fields after deserializing. This method is only
+ * called by data bindings.
+ */
+ final void setKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Part: number=" + number +
+ " name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+
+ // --- MarshalledEnt implementation ---
+
+ Part() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void unmarshalPrimaryKey(TupleInput keyInput) {
+
+ this.number = keyInput.readString();
+ }
+
+ public void marshalPrimaryKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.number);
+ }
+
+ public boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput) {
+
+ throw new UnsupportedOperationException(keyName);
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/PartKey.java b/examples_java/src/collections/ship/marshal/PartKey.java
new file mode 100644
index 0000000..fc81535
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/PartKey.java
@@ -0,0 +1,59 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A PartKey serves as the key in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartKey is bound to the stored key tuple entry by
+ * implementing the MarshalledKey interface, which is called by {@link
+ * SampleViews.MarshalledKeyBinding}. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartKey implements MarshalledKey {
+
+ private String number;
+
+ public PartKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[PartKey: number=" + number + ']';
+ }
+
+ // --- MarshalledKey implementation ---
+
+ PartKey() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void unmarshalKey(TupleInput keyInput) {
+
+ this.number = keyInput.readString();
+ }
+
+ public void marshalKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.number);
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/Sample.java b/examples_java/src/collections/ship/marshal/Sample.java
new file mode 100644
index 0000000..4789921
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/Sample.java
@@ -0,0 +1,236 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * Sample is the main entry point for the sample program and may be run as
+ * follows:
+ *
+ * <pre>
+ * java collections.ship.marshal.Sample
+ * [-h <home-directory> ]
+ * </pre>
+ *
+ * <p> The default for the home directory is ./tmp -- the tmp subdirectory of
+ * the current directory where the sample is run. To specify a different home
+ * directory, use the -home option. The home directory must exist before
+ * running the sample. To recreate the sample database from scratch, delete
+ * all files in the home directory before running the sample. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Sample {
+
+ private SampleDatabase db;
+ private SampleViews views;
+
+ /**
+ * Run the sample program.
+ */
+ public static void main(String[] args) {
+
+ System.out.println("\nRunning sample: " + Sample.class);
+
+ // Parse the command line arguments.
+ //
+ String homeDir = "./tmp";
+ for (int i = 0; i < args.length; i += 1) {
+ if (args[i].equals("-h") && i < args.length - 1) {
+ i += 1;
+ homeDir = args[i];
+ } else {
+ System.err.println("Usage:\n java " + Sample.class.getName() +
+ "\n [-h <home-directory>]");
+ System.exit(2);
+ }
+ }
+
+ // Run the sample.
+ //
+ Sample sample = null;
+ try {
+ sample = new Sample(homeDir);
+ sample.run();
+ } catch (Exception e) {
+ // If an exception reaches this point, the last transaction did not
+ // complete. If the exception is RunRecoveryException, follow
+ // the Berkeley DB recovery procedures before running again.
+ e.printStackTrace();
+ } finally {
+ if (sample != null) {
+ try {
+ // Always attempt to close the database cleanly.
+ sample.close();
+ } catch (Exception e) {
+ System.err.println("Exception during database close:");
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Open the database and views.
+ */
+ private Sample(String homeDir)
+ throws DatabaseException, FileNotFoundException {
+
+ db = new SampleDatabase(homeDir);
+ views = new SampleViews(db);
+ }
+
+ /**
+ * Close the database cleanly.
+ */
+ private void close()
+ throws DatabaseException {
+
+ db.close();
+ }
+
+ /**
+ * Run two transactions to populate and print the database. A
+ * TransactionRunner is used to ensure consistent handling of transactions,
+ * including deadlock retries. But the best transaction handling mechanism
+ * to use depends on the application.
+ */
+ private void run()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(db.getEnvironment());
+ runner.run(new PopulateDatabase());
+ runner.run(new PrintDatabase());
+ }
+
+ /**
+ * Populate the database in a single transaction.
+ */
+ private class PopulateDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ addSuppliers();
+ addParts();
+ addShipments();
+ }
+ }
+
+ /**
+ * Print the database in a single transaction. All entities are printed
+ * and the indices are used to print the entities for certain keys.
+ *
+ * <p> Note the use of special iterator() methods. These are used here
+ * with indices to find the shipments for certain keys.</p>
+ */
+ private class PrintDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ printValues("Parts",
+ views.getPartSet().iterator());
+ printValues("Suppliers",
+ views.getSupplierSet().iterator());
+ printValues("Suppliers for City Paris",
+ views.getSupplierByCityMap().duplicates(
+ "Paris").iterator());
+ printValues("Shipments",
+ views.getShipmentSet().iterator());
+ printValues("Shipments for Part P1",
+ views.getShipmentByPartMap().duplicates(
+ new PartKey("P1")).iterator());
+ printValues("Shipments for Supplier S1",
+ views.getShipmentBySupplierMap().duplicates(
+ new SupplierKey("S1")).iterator());
+ }
+ }
+
+ /**
+ * Populate the part entities in the database. If the part set is not
+ * empty, assume that this has already been done.
+ */
+ private void addParts() {
+
+ Set parts = views.getPartSet();
+ if (parts.isEmpty()) {
+ System.out.println("Adding Parts");
+ parts.add(new Part("P1", "Nut", "Red",
+ new Weight(12.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P2", "Bolt", "Green",
+ new Weight(17.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P3", "Screw", "Blue",
+ new Weight(17.0, Weight.GRAMS), "Rome"));
+ parts.add(new Part("P4", "Screw", "Red",
+ new Weight(14.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P5", "Cam", "Blue",
+ new Weight(12.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P6", "Cog", "Red",
+ new Weight(19.0, Weight.GRAMS), "London"));
+ }
+ }
+
+ /**
+ * Populate the supplier entities in the database. If the supplier set is
+ * not empty, assume that this has already been done.
+ */
+ private void addSuppliers() {
+
+ Set suppliers = views.getSupplierSet();
+ if (suppliers.isEmpty()) {
+ System.out.println("Adding Suppliers");
+ suppliers.add(new Supplier("S1", "Smith", 20, "London"));
+ suppliers.add(new Supplier("S2", "Jones", 10, "Paris"));
+ suppliers.add(new Supplier("S3", "Blake", 30, "Paris"));
+ suppliers.add(new Supplier("S4", "Clark", 20, "London"));
+ suppliers.add(new Supplier("S5", "Adams", 30, "Athens"));
+ }
+ }
+
+ /**
+ * Populate the shipment entities in the database. If the shipment set
+ * is not empty, assume that this has already been done.
+ */
+ private void addShipments() {
+
+ Set shipments = views.getShipmentSet();
+ if (shipments.isEmpty()) {
+ System.out.println("Adding Shipments");
+ shipments.add(new Shipment("P1", "S1", 300));
+ shipments.add(new Shipment("P2", "S1", 200));
+ shipments.add(new Shipment("P3", "S1", 400));
+ shipments.add(new Shipment("P4", "S1", 200));
+ shipments.add(new Shipment("P5", "S1", 100));
+ shipments.add(new Shipment("P6", "S1", 100));
+ shipments.add(new Shipment("P1", "S2", 300));
+ shipments.add(new Shipment("P2", "S2", 400));
+ shipments.add(new Shipment("P2", "S3", 200));
+ shipments.add(new Shipment("P2", "S4", 200));
+ shipments.add(new Shipment("P4", "S4", 300));
+ shipments.add(new Shipment("P5", "S4", 400));
+ }
+ }
+
+ /**
+ * Print the objects returned by an iterator of entity value objects.
+ */
+ private void printValues(String label, Iterator iterator) {
+
+ System.out.println("\n--- " + label + " ---");
+ while (iterator.hasNext()) {
+ System.out.println(iterator.next().toString());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/SampleDatabase.java b/examples_java/src/collections/ship/marshal/SampleDatabase.java
new file mode 100644
index 0000000..53df679
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/SampleDatabase.java
@@ -0,0 +1,260 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialKeyCreator;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+
+/**
+ * SampleDatabase defines the storage containers, indices and foreign keys
+ * for the sample database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleDatabase {
+
+ private static final String CLASS_CATALOG = "java_class_catalog";
+ private static final String SUPPLIER_STORE = "supplier_store";
+ private static final String PART_STORE = "part_store";
+ private static final String SHIPMENT_STORE = "shipment_store";
+ private static final String SHIPMENT_PART_INDEX = "shipment_part_index";
+ private static final String SHIPMENT_SUPPLIER_INDEX =
+ "shipment_supplier_index";
+ private static final String SUPPLIER_CITY_INDEX = "supplier_city_index";
+
+ private Environment env;
+ private Database partDb;
+ private Database supplierDb;
+ private Database shipmentDb;
+ private SecondaryDatabase supplierByCityDb;
+ private SecondaryDatabase shipmentByPartDb;
+ private SecondaryDatabase shipmentBySupplierDb;
+ private StoredClassCatalog javaCatalog;
+
+ /**
+ * Open all storage containers, indices, and catalogs.
+ */
+ public SampleDatabase(String homeDirectory)
+ throws DatabaseException, FileNotFoundException {
+
+ // Open the Berkeley DB environment in transactional mode.
+ //
+ System.out.println("Opening environment in: " + homeDirectory);
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setAllowCreate(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(new File(homeDirectory), envConfig);
+
+ // Set the Berkeley DB config for opening all stores.
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+
+ // Create the Serial class catalog. This holds the serialized class
+ // format for all database records of serial format.
+ //
+ Database catalogDb = env.openDatabase(null, CLASS_CATALOG, null,
+ dbConfig);
+ javaCatalog = new StoredClassCatalog(catalogDb);
+
+ // Open the Berkeley DB database for the part, supplier and shipment
+ // stores. The stores are opened with no duplicate keys allowed.
+ //
+ partDb = env.openDatabase(null, PART_STORE, null, dbConfig);
+
+ supplierDb = env.openDatabase(null, SUPPLIER_STORE, null, dbConfig);
+
+ shipmentDb = env.openDatabase(null, SHIPMENT_STORE, null, dbConfig);
+
+ // Open the SecondaryDatabase for the city index of the supplier store,
+ // and for the part and supplier indices of the shipment store.
+ // Duplicate keys are allowed since more than one supplier may be in
+ // the same city, and more than one shipment may exist for the same
+ // supplier or part. A foreign key constraint is defined for the
+ // supplier and part indices to ensure that a shipment only refers to
+ // existing part and supplier keys. The CASCADE delete action means
+ // that shipments will be deleted if their associated part or supplier
+ // is deleted.
+ //
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ secConfig.setType(DatabaseType.BTREE);
+ secConfig.setSortedDuplicates(true);
+
+ secConfig.setKeyCreator(new MarshalledKeyCreator(javaCatalog,
+ Supplier.class,
+ Supplier.CITY_KEY));
+ supplierByCityDb = env.openSecondaryDatabase(null, SUPPLIER_CITY_INDEX,
+ null, supplierDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(partDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(new MarshalledKeyCreator(javaCatalog,
+ Shipment.class,
+ Shipment.PART_KEY));
+ shipmentByPartDb = env.openSecondaryDatabase(null, SHIPMENT_PART_INDEX,
+ null, shipmentDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(supplierDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(new MarshalledKeyCreator(javaCatalog,
+ Shipment.class,
+ Shipment.SUPPLIER_KEY));
+ shipmentBySupplierDb = env.openSecondaryDatabase(null,
+ SHIPMENT_SUPPLIER_INDEX,
+ null, shipmentDb,
+ secConfig);
+ }
+
+ /**
+ * Return the storage environment for the database.
+ */
+ public final Environment getEnvironment() {
+
+ return env;
+ }
+
+ /**
+ * Return the class catalog.
+ */
+ public final StoredClassCatalog getClassCatalog() {
+
+ return javaCatalog;
+ }
+
+ /**
+ * Return the part storage container.
+ */
+ public final Database getPartDatabase() {
+
+ return partDb;
+ }
+
+ /**
+ * Return the supplier storage container.
+ */
+ public final Database getSupplierDatabase() {
+
+ return supplierDb;
+ }
+
+ /**
+ * Return the shipment storage container.
+ */
+ public final Database getShipmentDatabase() {
+
+ return shipmentDb;
+ }
+
+ /**
+ * Return the shipment-by-part index.
+ */
+ public final SecondaryDatabase getShipmentByPartDatabase() {
+
+ return shipmentByPartDb;
+ }
+
+ /**
+ * Return the shipment-by-supplier index.
+ */
+ public final SecondaryDatabase getShipmentBySupplierDatabase() {
+
+ return shipmentBySupplierDb;
+ }
+
+ /**
+ * Return the supplier-by-city index.
+ */
+ public final SecondaryDatabase getSupplierByCityDatabase() {
+
+ return supplierByCityDb;
+ }
+
+ /**
+ * Close all stores (closing a store automatically closes its indices).
+ */
+ public void close()
+ throws DatabaseException {
+
+ // Close secondary databases, then primary databases.
+ supplierByCityDb.close();
+ shipmentByPartDb.close();
+ shipmentBySupplierDb.close();
+ partDb.close();
+ supplierDb.close();
+ shipmentDb.close();
+ // And don't forget to close the catalog and the environment.
+ javaCatalog.close();
+ env.close();
+ }
+
+ /**
+ * The SecondaryKeyCreator for MarshalledEnt objects. This is an
+ * extension of the abstract class TupleSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys are of the format
+ * TupleFormat and the data values are of the format SerialFormat.
+ */
+ private static class MarshalledKeyCreator
+ extends TupleSerialKeyCreator {
+
+ private String keyName;
+
+ /**
+ * Construct the key creator.
+ * @param catalog is the class catalog.
+ * @param valueClass is the supplier value class.
+ * @param keyName is the key name passed to the marshalling methods.
+ */
+ private MarshalledKeyCreator(ClassCatalog catalog,
+ Class valueClass,
+ String keyName) {
+
+ super(catalog, valueClass);
+ this.keyName = keyName;
+ }
+
+ /**
+ * Extract the city key from a supplier key/value pair. The city key
+ * is stored in the supplier value, so the supplier key is not used.
+ */
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object valueInput,
+ TupleOutput indexKeyOutput) {
+
+ // the primary key is unmarshalled before marshalling the index
+ // key, to account for cases where the index key is composed of
+ // data elements from the primary key
+ MarshalledEnt entity = (MarshalledEnt) valueInput;
+ entity.unmarshalPrimaryKey(primaryKeyInput);
+ return entity.marshalSecondaryKey(keyName, indexKeyOutput);
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/SampleViews.java b/examples_java/src/collections/ship/marshal/SampleViews.java
new file mode 100644
index 0000000..91310e4
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/SampleViews.java
@@ -0,0 +1,276 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialBinding;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.StoredSortedValueSet;
+import com.sleepycat.util.RuntimeExceptionWrapper;
+
+/**
+ * SampleViews defines the data bindings and collection views for the sample
+ * database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleViews {
+
+ private StoredSortedMap partMap;
+ private StoredSortedMap supplierMap;
+ private StoredSortedMap shipmentMap;
+ private StoredSortedMap shipmentByPartMap;
+ private StoredSortedMap shipmentBySupplierMap;
+ private StoredSortedMap supplierByCityMap;
+
+ /**
+ * Create the data bindings and collection views.
+ */
+ public SampleViews(SampleDatabase db) {
+
+ // Create the data bindings.
+ // In this sample, EntityBinding classes are used to bind the stored
+ // key/data entry pair to a combined data object; a "tricky" binding
+ // that uses transient fields is used--see PartBinding, etc, for
+ // details. For keys, a one-to-one binding is implemented with
+ // EntryBinding classes to bind the stored tuple entry to a key Object.
+ //
+ ClassCatalog catalog = db.getClassCatalog();
+ EntryBinding partKeyBinding =
+ new MarshalledKeyBinding(PartKey.class);
+ EntityBinding partDataBinding =
+ new MarshalledEntityBinding(catalog, Part.class);
+ EntryBinding supplierKeyBinding =
+ new MarshalledKeyBinding(SupplierKey.class);
+ EntityBinding supplierDataBinding =
+ new MarshalledEntityBinding(catalog, Supplier.class);
+ EntryBinding shipmentKeyBinding =
+ new MarshalledKeyBinding(ShipmentKey.class);
+ EntityBinding shipmentDataBinding =
+ new MarshalledEntityBinding(catalog, Shipment.class);
+ EntryBinding cityKeyBinding =
+ TupleBinding.getPrimitiveBinding(String.class);
+
+ // Create map views for all stores and indices.
+ // StoredSortedMap is used since the stores and indices are ordered
+ // (they use the DB_BTREE access method).
+ //
+ partMap =
+ new StoredSortedMap(db.getPartDatabase(),
+ partKeyBinding, partDataBinding, true);
+ supplierMap =
+ new StoredSortedMap(db.getSupplierDatabase(),
+ supplierKeyBinding, supplierDataBinding, true);
+ shipmentMap =
+ new StoredSortedMap(db.getShipmentDatabase(),
+ shipmentKeyBinding, shipmentDataBinding, true);
+ shipmentByPartMap =
+ new StoredSortedMap(db.getShipmentByPartDatabase(),
+ partKeyBinding, shipmentDataBinding, true);
+ shipmentBySupplierMap =
+ new StoredSortedMap(db.getShipmentBySupplierDatabase(),
+ supplierKeyBinding, shipmentDataBinding, true);
+ supplierByCityMap =
+ new StoredSortedMap(db.getSupplierByCityDatabase(),
+ cityKeyBinding, supplierDataBinding, true);
+ }
+
+ // The views returned below can be accessed using the java.util.Map or
+ // java.util.Set interfaces, or using the StoredSortedMap and
+ // StoredValueSet classes, which provide additional methods. The entity
+ // sets could be obtained directly from the Map.values() method but
+ // convenience methods are provided here to return them in order to avoid
+ // down-casting elsewhere.
+
+ /**
+ * Return a map view of the part storage container.
+ */
+ public StoredSortedMap getPartMap() {
+
+ return partMap;
+ }
+
+ /**
+ * Return a map view of the supplier storage container.
+ */
+ public StoredSortedMap getSupplierMap() {
+
+ return supplierMap;
+ }
+
+ /**
+ * Return a map view of the shipment storage container.
+ */
+ public StoredSortedMap getShipmentMap() {
+
+ return shipmentMap;
+ }
+
+ /**
+ * Return an entity set view of the part storage container.
+ */
+ public StoredSortedValueSet getPartSet() {
+
+ return (StoredSortedValueSet) partMap.values();
+ }
+
+ /**
+ * Return an entity set view of the supplier storage container.
+ */
+ public StoredSortedValueSet getSupplierSet() {
+
+ return (StoredSortedValueSet) supplierMap.values();
+ }
+
+ /**
+ * Return an entity set view of the shipment storage container.
+ */
+ public StoredSortedValueSet getShipmentSet() {
+
+ return (StoredSortedValueSet) shipmentMap.values();
+ }
+
+ /**
+ * Return a map view of the shipment-by-part index.
+ */
+ public StoredSortedMap getShipmentByPartMap() {
+
+ return shipmentByPartMap;
+ }
+
+ /**
+ * Return a map view of the shipment-by-supplier index.
+ */
+ public StoredSortedMap getShipmentBySupplierMap() {
+
+ return shipmentBySupplierMap;
+ }
+
+ /**
+ * Return a map view of the supplier-by-city index.
+ */
+ public final StoredSortedMap getSupplierByCityMap() {
+
+ return supplierByCityMap;
+ }
+
+ /**
+ * MarshalledKeyBinding is used to bind the stored key tuple entry to a key
+ * object representation. To do this, it calls the MarshalledKey interface
+ * implemented by the key class.
+ */
+ private static class MarshalledKeyBinding extends TupleBinding {
+
+ private Class keyClass;
+
+ /**
+ * Construct the binding object.
+ */
+ private MarshalledKeyBinding(Class keyClass) {
+
+ // The key class will be used to instantiate the key object.
+ //
+ if (!MarshalledKey.class.isAssignableFrom(keyClass)) {
+ throw new IllegalArgumentException(keyClass.toString() +
+ " does not implement MarshalledKey");
+ }
+ this.keyClass = keyClass;
+ }
+
+ /**
+ * Create the key object from the stored key tuple entry.
+ */
+ public Object entryToObject(TupleInput input) {
+
+ try {
+ MarshalledKey key = (MarshalledKey) keyClass.newInstance();
+ key.unmarshalKey(input);
+ return key;
+ } catch (IllegalAccessException e) {
+ throw new RuntimeExceptionWrapper(e);
+ } catch (InstantiationException e) {
+ throw new RuntimeExceptionWrapper(e);
+ }
+ }
+
+ /**
+ * Create the stored key tuple entry from the key object.
+ */
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ MarshalledKey key = (MarshalledKey) object;
+ key.marshalKey(output);
+ }
+ }
+
+ /**
+ * MarshalledEntityBinding is used to bind the stored key/data entry pair
+ * to a combined to an entity object representation. To do this, it calls
+ * the MarshalledEnt interface implemented by the entity class.
+ *
+ * <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,
+ * 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>
+ */
+ private static class MarshalledEntityBinding extends TupleSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private MarshalledEntityBinding(ClassCatalog classCatalog,
+ Class entityClass) {
+
+ super(classCatalog, entityClass);
+
+ // The entity class will be used to instantiate the entity object.
+ //
+ if (!MarshalledEnt.class.isAssignableFrom(entityClass)) {
+ throw new IllegalArgumentException(entityClass.toString() +
+ " does not implement MarshalledEnt");
+ }
+ }
+
+ /**
+ * Create 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.
+ */
+ public Object entryToObject(TupleInput tupleInput, Object javaInput) {
+
+ MarshalledEnt entity = (MarshalledEnt) javaInput;
+ entity.unmarshalPrimaryKey(tupleInput);
+ return entity;
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public void objectToKey(Object object, TupleOutput output) {
+
+ MarshalledEnt entity = (MarshalledEnt) object;
+ entity.marshalPrimaryKey(output);
+ }
+
+ /**
+ * Return the entity as the stored data. There is nothing to do here
+ * since the entity's key fields are transient.
+ */
+ public Object objectToData(Object object) {
+
+ return object;
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/Shipment.java b/examples_java/src/collections/ship/marshal/Shipment.java
new file mode 100644
index 0000000..bbc2ccb
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/Shipment.java
@@ -0,0 +1,113 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import java.io.Serializable;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A Shipment represents the combined key/data pair for a shipment entity.
+ *
+ * <p> In this sample, Shipment is bound to the stored key/data entry by
+ * implementing the MarshalledEnt interface, which is called by {@link
+ * SampleViews.MarshalledEntityBinding}. </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s) are
+ * transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a ShipmentData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Shipment implements Serializable, MarshalledEnt {
+
+ static final String PART_KEY = "part";
+ static final String SUPPLIER_KEY = "supplier";
+
+ private transient String partNumber;
+ private transient String supplierNumber;
+ private int quantity;
+
+ public Shipment(String partNumber, String supplierNumber, int quantity) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ this.quantity = quantity;
+ }
+
+ /**
+ * Set the transient key fields after deserializing. This method is only
+ * called by data bindings.
+ */
+ void setKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[Shipment: part=" + partNumber +
+ " supplier=" + supplierNumber +
+ " quantity=" + quantity + ']';
+ }
+
+ // --- MarshalledEnt implementation ---
+
+ Shipment() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void unmarshalPrimaryKey(TupleInput keyInput) {
+
+ this.partNumber = keyInput.readString();
+ this.supplierNumber = keyInput.readString();
+ }
+
+ public void marshalPrimaryKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.partNumber);
+ keyOutput.writeString(this.supplierNumber);
+ }
+
+ public boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput) {
+
+ if (keyName.equals(PART_KEY)) {
+ keyOutput.writeString(this.partNumber);
+ return true;
+ } else if (keyName.equals(SUPPLIER_KEY)) {
+ keyOutput.writeString(this.supplierNumber);
+ return true;
+ } else {
+ throw new UnsupportedOperationException(keyName);
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/ShipmentKey.java b/examples_java/src/collections/ship/marshal/ShipmentKey.java
new file mode 100644
index 0000000..a066822
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/ShipmentKey.java
@@ -0,0 +1,69 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A ShipmentKey serves as the key in the key/data pair for a shipment entity.
+ *
+ * <p> In this sample, ShipmentKey is bound to the stored key tuple entry by
+ * implementing the MarshalledKey interface, which is called by {@link
+ * SampleViews.MarshalledKeyBinding}. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentKey implements MarshalledKey {
+
+ private String partNumber;
+ private String supplierNumber;
+
+ public ShipmentKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public String toString() {
+
+ return "[ShipmentKey: supplier=" + supplierNumber +
+ " part=" + partNumber + ']';
+ }
+
+ // --- MarshalledKey implementation ---
+
+ ShipmentKey() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void unmarshalKey(TupleInput keyInput) {
+
+ this.partNumber = keyInput.readString();
+ this.supplierNumber = keyInput.readString();
+ }
+
+ public void marshalKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.partNumber);
+ keyOutput.writeString(this.supplierNumber);
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/Supplier.java b/examples_java/src/collections/ship/marshal/Supplier.java
new file mode 100644
index 0000000..43d97c0
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/Supplier.java
@@ -0,0 +1,118 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import java.io.Serializable;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A Supplier represents the combined key/data pair for a supplier entity.
+ *
+ * <p> In this sample, Supplier is bound to the stored key/data entry by
+ * implementing the MarshalledEnt interface, which is called by {@link
+ * SampleViews.MarshalledEntityBinding}. </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s) are
+ * transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a SupplierData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Supplier implements Serializable, MarshalledEnt {
+
+ static final String CITY_KEY = "city";
+
+ private transient String number;
+ private String name;
+ private int status;
+ private String city;
+
+ public Supplier(String number, String name, int status, String city) {
+
+ this.number = number;
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ /**
+ * Set the transient key fields after deserializing. This method is only
+ * called by data bindings.
+ */
+ void setKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Supplier: number=" + number +
+ " name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+
+ // --- MarshalledEnt implementation ---
+
+ Supplier() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void unmarshalPrimaryKey(TupleInput keyInput) {
+
+ this.number = keyInput.readString();
+ }
+
+ public void marshalPrimaryKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.number);
+ }
+
+ public boolean marshalSecondaryKey(String keyName, TupleOutput keyOutput) {
+
+ if (keyName.equals(CITY_KEY)) {
+ if (this.city != null) {
+ keyOutput.writeString(this.city);
+ return true;
+ } else {
+ return false;
+ }
+ } else {
+ throw new UnsupportedOperationException(keyName);
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/SupplierKey.java b/examples_java/src/collections/ship/marshal/SupplierKey.java
new file mode 100644
index 0000000..d8ab019
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/SupplierKey.java
@@ -0,0 +1,59 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+/**
+ * A SupplierKey serves as the key in the key/data pair for a supplier entity.
+ *
+ * <p> In this sample, SupplierKey is bound to the stored key tuple entry by
+ * implementing the MarshalledKey interface, which is called by {@link
+ * SampleViews.MarshalledKeyBinding}. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierKey implements MarshalledKey {
+
+ private String number;
+
+ public SupplierKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[SupplierKey: number=" + number + ']';
+ }
+
+ // --- MarshalledKey implementation ---
+
+ SupplierKey() {
+
+ // A no-argument constructor is necessary only to allow the binding to
+ // instantiate objects of this class.
+ }
+
+ public void unmarshalKey(TupleInput keyInput) {
+
+ this.number = keyInput.readString();
+ }
+
+ public void marshalKey(TupleOutput keyOutput) {
+
+ keyOutput.writeString(this.number);
+ }
+}
diff --git a/examples_java/src/collections/ship/marshal/Weight.java b/examples_java/src/collections/ship/marshal/Weight.java
new file mode 100644
index 0000000..608629d
--- /dev/null
+++ b/examples_java/src/collections/ship/marshal/Weight.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.marshal;
+
+import java.io.Serializable;
+
+/**
+ * Weight represents a weight amount and unit of measure.
+ *
+ * <p> In this sample, Weight is embedded in part data values which are stored
+ * as Java serialized objects; therefore Weight must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Weight implements Serializable {
+
+ public final static String GRAMS = "grams";
+ public final static String OUNCES = "ounces";
+
+ private double amount;
+ private String units;
+
+ public Weight(double amount, String units) {
+
+ this.amount = amount;
+ this.units = units;
+ }
+
+ public final double getAmount() {
+
+ return amount;
+ }
+
+ public final String getUnits() {
+
+ return units;
+ }
+
+ public String toString() {
+
+ return "[" + amount + ' ' + units + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/Part.java b/examples_java/src/collections/ship/sentity/Part.java
new file mode 100644
index 0000000..b50bfe9
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/Part.java
@@ -0,0 +1,90 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+import java.io.Serializable;
+
+/**
+ * A Part represents the combined key/data pair for a part entity.
+ *
+ * <p> In this sample, Part is created from the stored key/data entry using a
+ * TupleSerialEntityBinding. See {@link SampleViews.PartBinding} for details.
+ * </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s) are
+ * transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a PartData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Part implements Serializable {
+
+ private transient String number;
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public Part(String number, String name, String color, Weight weight,
+ String city) {
+
+ this.number = number;
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ /**
+ * Set the transient key fields after deserializing. This method is only
+ * called by data bindings.
+ */
+ final void setKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Part: number=" + number +
+ " name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/PartKey.java b/examples_java/src/collections/ship/sentity/PartKey.java
new file mode 100644
index 0000000..b2ae689
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/PartKey.java
@@ -0,0 +1,38 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+/**
+ * A PartKey serves as the key in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartKey is bound to the key's tuple storage entry using
+ * a TupleBinding. Because it is not used directly as storage data, it does
+ * not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartKey {
+
+ private String number;
+
+ public PartKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[PartKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/Sample.java b/examples_java/src/collections/ship/sentity/Sample.java
new file mode 100644
index 0000000..c7d1b44
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/Sample.java
@@ -0,0 +1,249 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.sleepycat.collections.StoredIterator;
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * Sample is the main entry point for the sample program and may be run as
+ * follows:
+ *
+ * <pre>
+ * java collections.ship.sentity.Sample
+ * [-h <home-directory> ]
+ * </pre>
+ *
+ * <p> The default for the home directory is ./tmp -- the tmp subdirectory of
+ * the current directory where the sample is run. To specify a different home
+ * directory, use the -home option. The home directory must exist before
+ * running the sample. To recreate the sample database from scratch, delete
+ * all files in the home directory before running the sample. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Sample {
+
+ private SampleDatabase db;
+ private SampleViews views;
+
+ /**
+ * Run the sample program.
+ */
+ public static void main(String[] args) {
+
+ System.out.println("\nRunning sample: " + Sample.class);
+
+ // Parse the command line arguments.
+ //
+ String homeDir = "./tmp";
+ for (int i = 0; i < args.length; i += 1) {
+ if (args[i].equals("-h") && i < args.length - 1) {
+ i += 1;
+ homeDir = args[i];
+ } else {
+ System.err.println("Usage:\n java " + Sample.class.getName() +
+ "\n [-h <home-directory>]");
+ System.exit(2);
+ }
+ }
+
+ // Run the sample.
+ //
+ Sample sample = null;
+ try {
+ sample = new Sample(homeDir);
+ sample.run();
+ } catch (Exception e) {
+ // If an exception reaches this point, the last transaction did not
+ // complete. If the exception is RunRecoveryException, follow
+ // the Berkeley DB recovery procedures before running again.
+ e.printStackTrace();
+ } finally {
+ if (sample != null) {
+ try {
+ // Always attempt to close the database cleanly.
+ sample.close();
+ } catch (Exception e) {
+ System.err.println("Exception during database close:");
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Open the database and views.
+ */
+ private Sample(String homeDir)
+ throws DatabaseException, FileNotFoundException {
+
+ db = new SampleDatabase(homeDir);
+ views = new SampleViews(db);
+ }
+
+ /**
+ * Close the database cleanly.
+ */
+ private void close()
+ throws DatabaseException {
+
+ db.close();
+ }
+
+ /**
+ * Run two transactions to populate and print the database. A
+ * TransactionRunner is used to ensure consistent handling of transactions,
+ * including deadlock retries. But the best transaction handling mechanism
+ * to use depends on the application.
+ */
+ private void run()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(db.getEnvironment());
+ runner.run(new PopulateDatabase());
+ runner.run(new PrintDatabase());
+ }
+
+ /**
+ * Populate the database in a single transaction.
+ */
+ private class PopulateDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ addSuppliers();
+ addParts();
+ addShipments();
+ }
+ }
+
+ /**
+ * Print the database in a single transaction. All entities are printed
+ * and the indices are used to print the entities for certain keys.
+ *
+ * <p> Note the use of special iterator() methods. These are used here
+ * with indices to find the shipments for certain keys. For details on
+ * database iterators see {@link StoredIterator}. </p>
+ */
+ private class PrintDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ printValues("Parts",
+ views.getPartSet().iterator());
+ printValues("Suppliers",
+ views.getSupplierSet().iterator());
+ printValues("Suppliers for City Paris",
+ views.getSupplierByCityMap().duplicates(
+ "Paris").iterator());
+ printValues("Shipments",
+ views.getShipmentSet().iterator());
+ printValues("Shipments for Part P1",
+ views.getShipmentByPartMap().duplicates(
+ new PartKey("P1")).iterator());
+ printValues("Shipments for Supplier S1",
+ views.getShipmentBySupplierMap().duplicates(
+ new SupplierKey("S1")).iterator());
+ }
+ }
+
+ /**
+ * Populate the part entities in the database. If the part set is not
+ * empty, assume that this has already been done.
+ */
+ private void addParts() {
+
+ Set parts = views.getPartSet();
+ if (parts.isEmpty()) {
+ System.out.println("Adding Parts");
+ parts.add(new Part("P1", "Nut", "Red",
+ new Weight(12.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P2", "Bolt", "Green",
+ new Weight(17.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P3", "Screw", "Blue",
+ new Weight(17.0, Weight.GRAMS), "Rome"));
+ parts.add(new Part("P4", "Screw", "Red",
+ new Weight(14.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P5", "Cam", "Blue",
+ new Weight(12.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P6", "Cog", "Red",
+ new Weight(19.0, Weight.GRAMS), "London"));
+ }
+ }
+
+ /**
+ * Populate the supplier entities in the database. If the supplier set is
+ * not empty, assume that this has already been done.
+ */
+ private void addSuppliers() {
+
+ Set suppliers = views.getSupplierSet();
+ if (suppliers.isEmpty()) {
+ System.out.println("Adding Suppliers");
+ suppliers.add(new Supplier("S1", "Smith", 20, "London"));
+ suppliers.add(new Supplier("S2", "Jones", 10, "Paris"));
+ suppliers.add(new Supplier("S3", "Blake", 30, "Paris"));
+ suppliers.add(new Supplier("S4", "Clark", 20, "London"));
+ suppliers.add(new Supplier("S5", "Adams", 30, "Athens"));
+ }
+ }
+
+ /**
+ * Populate the shipment entities in the database. If the shipment set
+ * is not empty, assume that this has already been done.
+ */
+ private void addShipments() {
+
+ Set shipments = views.getShipmentSet();
+ if (shipments.isEmpty()) {
+ System.out.println("Adding Shipments");
+ shipments.add(new Shipment("P1", "S1", 300));
+ shipments.add(new Shipment("P2", "S1", 200));
+ shipments.add(new Shipment("P3", "S1", 400));
+ shipments.add(new Shipment("P4", "S1", 200));
+ shipments.add(new Shipment("P5", "S1", 100));
+ shipments.add(new Shipment("P6", "S1", 100));
+ shipments.add(new Shipment("P1", "S2", 300));
+ shipments.add(new Shipment("P2", "S2", 400));
+ shipments.add(new Shipment("P2", "S3", 200));
+ shipments.add(new Shipment("P2", "S4", 200));
+ shipments.add(new Shipment("P4", "S4", 300));
+ shipments.add(new Shipment("P5", "S4", 400));
+ }
+ }
+
+ /**
+ * Print the objects returned by an iterator of entity value objects.
+ *
+ * <p><b> IMPORTANT: All database iterators must be closed to avoid
+ * serious database problems. If the iterator is not closed, the
+ * underlying Berkeley DB cursor is not closed either. </b></p>
+ */
+ private void printValues(String label, Iterator iterator) {
+
+ System.out.println("\n--- " + label + " ---");
+ try {
+ while (iterator.hasNext()) {
+ System.out.println(iterator.next().toString());
+ }
+ } finally {
+ // IMPORTANT: Use StoredIterator to close all database
+ // iterators. If java.util.Iterator is in hand, you can safely
+ // close it by calling StoredIterator.close(Iterator).
+ StoredIterator.close(iterator);
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/SampleDatabase.java b/examples_java/src/collections/ship/sentity/SampleDatabase.java
new file mode 100644
index 0000000..c51e125
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/SampleDatabase.java
@@ -0,0 +1,323 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialKeyCreator;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+
+/**
+ * SampleDatabase defines the storage containers, indices and foreign keys
+ * for the sample database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleDatabase {
+
+ private static final String CLASS_CATALOG = "java_class_catalog";
+ private static final String SUPPLIER_STORE = "supplier_store";
+ private static final String PART_STORE = "part_store";
+ private static final String SHIPMENT_STORE = "shipment_store";
+ private static final String SHIPMENT_PART_INDEX = "shipment_part_index";
+ private static final String SHIPMENT_SUPPLIER_INDEX =
+ "shipment_supplier_index";
+ private static final String SUPPLIER_CITY_INDEX = "supplier_city_index";
+
+ private Environment env;
+ private Database partDb;
+ private Database supplierDb;
+ private Database shipmentDb;
+ private SecondaryDatabase supplierByCityDb;
+ private SecondaryDatabase shipmentByPartDb;
+ private SecondaryDatabase shipmentBySupplierDb;
+ private StoredClassCatalog javaCatalog;
+
+ /**
+ * Open all storage containers, indices, and catalogs.
+ */
+ public SampleDatabase(String homeDirectory)
+ throws DatabaseException, FileNotFoundException {
+
+ // Open the Berkeley DB environment in transactional mode.
+ //
+ System.out.println("Opening environment in: " + homeDirectory);
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setAllowCreate(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(new File(homeDirectory), envConfig);
+
+ // Set the Berkeley DB config for opening all stores.
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+
+ // Create the Serial class catalog. This holds the serialized class
+ // format for all database records of serial format.
+ //
+ Database catalogDb = env.openDatabase(null, CLASS_CATALOG, null,
+ dbConfig);
+ javaCatalog = new StoredClassCatalog(catalogDb);
+
+ // Open the Berkeley DB database for the part, supplier and shipment
+ // stores. The stores are opened with no duplicate keys allowed.
+ //
+ partDb = env.openDatabase(null, PART_STORE, null, dbConfig);
+
+ supplierDb = env.openDatabase(null, SUPPLIER_STORE, null, dbConfig);
+
+ shipmentDb = env.openDatabase(null, SHIPMENT_STORE, null, dbConfig);
+
+ // Open the SecondaryDatabase for the city index of the supplier store,
+ // and for the part and supplier indices of the shipment store.
+ // Duplicate keys are allowed since more than one supplier may be in
+ // the same city, and more than one shipment may exist for the same
+ // supplier or part. A foreign key constraint is defined for the
+ // supplier and part indices to ensure that a shipment only refers to
+ // existing part and supplier keys. The CASCADE delete action means
+ // that shipments will be deleted if their associated part or supplier
+ // is deleted.
+ //
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ secConfig.setType(DatabaseType.BTREE);
+ secConfig.setSortedDuplicates(true);
+
+ secConfig.setKeyCreator(new SupplierByCityKeyCreator(javaCatalog,
+ Supplier.class));
+ supplierByCityDb = env.openSecondaryDatabase(null, SUPPLIER_CITY_INDEX,
+ null, supplierDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(partDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(new ShipmentByPartKeyCreator(javaCatalog,
+ Shipment.class));
+ shipmentByPartDb = env.openSecondaryDatabase(null, SHIPMENT_PART_INDEX,
+ null, shipmentDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(supplierDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(new ShipmentBySupplierKeyCreator(javaCatalog,
+ Shipment.class));
+ shipmentBySupplierDb = env.openSecondaryDatabase(null,
+ SHIPMENT_SUPPLIER_INDEX,
+ null, shipmentDb,
+ secConfig);
+ }
+
+ /**
+ * Return the storage environment for the database.
+ */
+ public final Environment getEnvironment() {
+
+ return env;
+ }
+
+ /**
+ * Return the class catalog.
+ */
+ public final StoredClassCatalog getClassCatalog() {
+
+ return javaCatalog;
+ }
+
+ /**
+ * Return the part storage container.
+ */
+ public final Database getPartDatabase() {
+
+ return partDb;
+ }
+
+ /**
+ * Return the supplier storage container.
+ */
+ public final Database getSupplierDatabase() {
+
+ return supplierDb;
+ }
+
+ /**
+ * Return the shipment storage container.
+ */
+ public final Database getShipmentDatabase() {
+
+ return shipmentDb;
+ }
+
+ /**
+ * Return the shipment-by-part index.
+ */
+ public final SecondaryDatabase getShipmentByPartDatabase() {
+
+ return shipmentByPartDb;
+ }
+
+ /**
+ * Return the shipment-by-supplier index.
+ */
+ public final SecondaryDatabase getShipmentBySupplierDatabase() {
+
+ return shipmentBySupplierDb;
+ }
+
+ /**
+ * Return the supplier-by-city index.
+ */
+ public final SecondaryDatabase getSupplierByCityDatabase() {
+
+ return supplierByCityDb;
+ }
+
+ /**
+ * Close all stores (closing a store automatically closes its indices).
+ */
+ public void close()
+ throws DatabaseException {
+
+ // Close secondary databases, then primary databases.
+ supplierByCityDb.close();
+ shipmentByPartDb.close();
+ shipmentBySupplierDb.close();
+ partDb.close();
+ supplierDb.close();
+ shipmentDb.close();
+ // And don't forget to close the catalog and the environment.
+ javaCatalog.close();
+ env.close();
+ }
+
+ /**
+ * The SecondaryKeyCreator for the SupplierByCity index. This is an
+ * extension of the abstract class TupleSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys are of the format
+ * TupleFormat and the data values are of the format SerialFormat.
+ */
+ private static class SupplierByCityKeyCreator
+ extends TupleSerialKeyCreator {
+
+ /**
+ * Construct the city key extractor.
+ * @param catalog is the class catalog.
+ * @param valueClass is the supplier value class.
+ */
+ private SupplierByCityKeyCreator(ClassCatalog catalog,
+ Class valueClass) {
+
+ super(catalog, valueClass);
+ }
+
+ /**
+ * Extract the city key from a supplier key/value pair. The city key
+ * is stored in the supplier value, so the supplier key is not used.
+ */
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object valueInput,
+ TupleOutput indexKeyOutput) {
+
+ Supplier supplier = (Supplier) valueInput;
+ String city = supplier.getCity();
+ if (city != null) {
+ indexKeyOutput.writeString(supplier.getCity());
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * The SecondaryKeyCreator for the ShipmentByPart index. This is an
+ * extension of the abstract class TupleSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys are of the format
+ * TupleFormat and the data values are of the format SerialFormat.
+ */
+ private static class ShipmentByPartKeyCreator
+ extends TupleSerialKeyCreator {
+
+ /**
+ * Construct the part key extractor.
+ * @param catalog is the class catalog.
+ * @param valueClass is the shipment value class.
+ */
+ private ShipmentByPartKeyCreator(ClassCatalog catalog,
+ Class valueClass) {
+ super(catalog, valueClass);
+ }
+
+ /**
+ * Extract the part key from a shipment key/value pair. The part key
+ * is stored in the shipment key, so the shipment value is not used.
+ */
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object valueInput,
+ TupleOutput indexKeyOutput) {
+
+ String partNumber = primaryKeyInput.readString();
+ // don't bother reading the supplierNumber
+ indexKeyOutput.writeString(partNumber);
+ return true;
+ }
+ }
+
+ /**
+ * The SecondaryKeyCreator for the ShipmentBySupplier index. This is an
+ * extension of the abstract class TupleSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys are of the format
+ * TupleFormat and the data values are of the format SerialFormat.
+ */
+ private static class ShipmentBySupplierKeyCreator
+ extends TupleSerialKeyCreator {
+
+ /**
+ * Construct the supplier key extractor.
+ * @param catalog is the class catalog.
+ * @param valueClass is the shipment value class.
+ */
+ private ShipmentBySupplierKeyCreator(ClassCatalog catalog,
+ Class valueClass) {
+ super(catalog, valueClass);
+ }
+
+ /**
+ * Extract the supplier key from a shipment key/value pair. The
+ * supplier key is stored in the shipment key, so the shipment value is
+ * not used.
+ */
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object valueInput,
+ TupleOutput indexKeyOutput) {
+
+ primaryKeyInput.readString(); // skip the partNumber
+ String supplierNumber = primaryKeyInput.readString();
+ indexKeyOutput.writeString(supplierNumber);
+ return true;
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/SampleViews.java b/examples_java/src/collections/ship/sentity/SampleViews.java
new file mode 100644
index 0000000..9b69061
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/SampleViews.java
@@ -0,0 +1,419 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialBinding;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.StoredSortedValueSet;
+
+/**
+ * SampleViews defines the data bindings and collection views for the sample
+ * database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleViews {
+
+ private StoredSortedMap partMap;
+ private StoredSortedMap supplierMap;
+ private StoredSortedMap shipmentMap;
+ private StoredSortedMap shipmentByPartMap;
+ private StoredSortedMap shipmentBySupplierMap;
+ private StoredSortedMap supplierByCityMap;
+
+ /**
+ * Create the data bindings and collection views.
+ */
+ public SampleViews(SampleDatabase db) {
+
+ // Create the data bindings.
+ // In this sample, EntityBinding classes are used to bind the stored
+ // key/data entry pair to a combined data object; a "tricky" binding
+ // that uses transient fields is used--see PartBinding, etc, for
+ // details. For keys, a one-to-one binding is implemented with
+ // EntryBinding classes to bind the stored tuple entry to a key Object.
+ //
+ ClassCatalog catalog = db.getClassCatalog();
+ EntryBinding partKeyBinding =
+ new PartKeyBinding();
+ EntityBinding partDataBinding =
+ new PartBinding(catalog, Part.class);
+ EntryBinding supplierKeyBinding =
+ new SupplierKeyBinding();
+ EntityBinding supplierDataBinding =
+ new SupplierBinding(catalog, Supplier.class);
+ EntryBinding shipmentKeyBinding =
+ new ShipmentKeyBinding();
+ EntityBinding shipmentDataBinding =
+ new ShipmentBinding(catalog, Shipment.class);
+ EntryBinding cityKeyBinding =
+ TupleBinding.getPrimitiveBinding(String.class);
+
+ // Create map views for all stores and indices.
+ // StoredSortedMap is used since the stores and indices are ordered
+ // (they use the DB_BTREE access method).
+ //
+ partMap =
+ new StoredSortedMap(db.getPartDatabase(),
+ partKeyBinding, partDataBinding, true);
+ supplierMap =
+ new StoredSortedMap(db.getSupplierDatabase(),
+ supplierKeyBinding, supplierDataBinding, true);
+ shipmentMap =
+ new StoredSortedMap(db.getShipmentDatabase(),
+ shipmentKeyBinding, shipmentDataBinding, true);
+ shipmentByPartMap =
+ new StoredSortedMap(db.getShipmentByPartDatabase(),
+ partKeyBinding, shipmentDataBinding, true);
+ shipmentBySupplierMap =
+ new StoredSortedMap(db.getShipmentBySupplierDatabase(),
+ supplierKeyBinding, shipmentDataBinding, true);
+ supplierByCityMap =
+ new StoredSortedMap(db.getSupplierByCityDatabase(),
+ cityKeyBinding, supplierDataBinding, true);
+ }
+
+ // The views returned below can be accessed using the java.util.Map or
+ // java.util.Set interfaces, or using the StoredSortedMap and
+ // StoredValueSet classes, which provide additional methods. The entity
+ // sets could be obtained directly from the Map.values() method but
+ // convenience methods are provided here to return them in order to avoid
+ // down-casting elsewhere.
+
+ /**
+ * Return a map view of the part storage container.
+ */
+ public StoredSortedMap getPartMap() {
+
+ return partMap;
+ }
+
+ /**
+ * Return a map view of the supplier storage container.
+ */
+ public StoredSortedMap getSupplierMap() {
+
+ return supplierMap;
+ }
+
+ /**
+ * Return a map view of the shipment storage container.
+ */
+ public StoredSortedMap getShipmentMap() {
+
+ return shipmentMap;
+ }
+
+ /**
+ * Return an entity set view of the part storage container.
+ */
+ public StoredSortedValueSet getPartSet() {
+
+ return (StoredSortedValueSet) partMap.values();
+ }
+
+ /**
+ * Return an entity set view of the supplier storage container.
+ */
+ public StoredSortedValueSet getSupplierSet() {
+
+ return (StoredSortedValueSet) supplierMap.values();
+ }
+
+ /**
+ * Return an entity set view of the shipment storage container.
+ */
+ public StoredSortedValueSet getShipmentSet() {
+
+ return (StoredSortedValueSet) shipmentMap.values();
+ }
+
+ /**
+ * Return a map view of the shipment-by-part index.
+ */
+ public StoredSortedMap getShipmentByPartMap() {
+
+ return shipmentByPartMap;
+ }
+
+ /**
+ * Return a map view of the shipment-by-supplier index.
+ */
+ public StoredSortedMap getShipmentBySupplierMap() {
+
+ return shipmentBySupplierMap;
+ }
+
+ /**
+ * Return a map view of the supplier-by-city index.
+ */
+ public final StoredSortedMap getSupplierByCityMap() {
+
+ return supplierByCityMap;
+ }
+
+ /**
+ * PartKeyBinding is used to bind the stored key tuple entry for a part to
+ * a key object representation.
+ */
+ private static class PartKeyBinding extends TupleBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private PartKeyBinding() {
+ }
+
+ /**
+ * Create the key object from the stored key tuple entry.
+ */
+ public Object entryToObject(TupleInput input) {
+
+ String number = input.readString();
+ return new PartKey(number);
+ }
+
+ /**
+ * Create the stored key tuple entry from the key object.
+ */
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ PartKey key = (PartKey) object;
+ output.writeString(key.getNumber());
+ }
+ }
+
+ /**
+ * PartBinding is used to bind the stored key/data entry pair for a part
+ * to a combined data object (entity).
+ *
+ * <p> The binding is "tricky" in that it uses the Part class for both the
+ * stored data entry and the combined entity object. To do this, Part'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 PartData class
+ * completely. </p>
+ */
+ private static class PartBinding extends TupleSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private PartBinding(ClassCatalog classCatalog, Class dataClass) {
+
+ super(classCatalog, dataClass);
+ }
+
+ /**
+ * Create 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.
+ */
+ public Object entryToObject(TupleInput keyInput, Object dataInput) {
+
+ String number = keyInput.readString();
+ Part part = (Part) dataInput;
+ part.setKey(number);
+ return part;
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public void objectToKey(Object object, TupleOutput output) {
+
+ Part part = (Part) object;
+ output.writeString(part.getNumber());
+ }
+
+ /**
+ * Return the entity as the stored data. There is nothing to do here
+ * since the entity's key fields are transient.
+ */
+ public Object objectToData(Object object) {
+
+ return object;
+ }
+ }
+
+ /**
+ * SupplierKeyBinding is used to bind the stored key tuple entry for a
+ * supplier to a key object representation.
+ */
+ private static class SupplierKeyBinding extends TupleBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private SupplierKeyBinding() {
+ }
+
+ /**
+ * Create the key object from the stored key tuple entry.
+ */
+ public Object entryToObject(TupleInput input) {
+
+ String number = input.readString();
+ return new SupplierKey(number);
+ }
+
+ /**
+ * Create the stored key tuple entry from the key object.
+ */
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ SupplierKey key = (SupplierKey) object;
+ output.writeString(key.getNumber());
+ }
+ }
+
+ /**
+ * SupplierBinding is used to bind the stored key/data entry pair for a
+ * supplier to a combined data object (entity).
+ *
+ * <p> The binding is "tricky" in that it uses the Supplier class for both
+ * the stored data entry and the combined entity object. To do this,
+ * Supplier'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
+ * SupplierData class completely. </p>
+ */
+ private static class SupplierBinding extends TupleSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private SupplierBinding(ClassCatalog classCatalog, Class dataClass) {
+
+ super(classCatalog, dataClass);
+ }
+
+ /**
+ * Create 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.
+ */
+ public Object entryToObject(TupleInput keyInput, Object dataInput) {
+
+ String number = keyInput.readString();
+ Supplier supplier = (Supplier) dataInput;
+ supplier.setKey(number);
+ return supplier;
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public void objectToKey(Object object, TupleOutput output) {
+
+ Supplier supplier = (Supplier) object;
+ output.writeString(supplier.getNumber());
+ }
+
+ /**
+ * Return the entity as the stored data. There is nothing to do here
+ * since the entity's key fields are transient.
+ */
+ public Object objectToData(Object object) {
+
+ return object;
+ }
+ }
+
+ /**
+ * ShipmentKeyBinding is used to bind the stored key tuple entry for a
+ * shipment to a key object representation.
+ */
+ private static class ShipmentKeyBinding extends TupleBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private ShipmentKeyBinding() {
+ }
+
+ /**
+ * Create the key object from the stored key tuple entry.
+ */
+ public Object entryToObject(TupleInput input) {
+
+ String partNumber = input.readString();
+ String supplierNumber = input.readString();
+ return new ShipmentKey(partNumber, supplierNumber);
+ }
+
+ /**
+ * Create the stored key tuple entry from the key object.
+ */
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ ShipmentKey key = (ShipmentKey) object;
+ output.writeString(key.getPartNumber());
+ output.writeString(key.getSupplierNumber());
+ }
+ }
+
+ /**
+ * ShipmentBinding is used to bind the stored key/data entry pair for a
+ * shipment to a combined data object (entity).
+ *
+ * <p> The binding is "tricky" in that it uses the Shipment class for both
+ * the stored data entry and the combined entity object. To do this,
+ * Shipment'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
+ * ShipmentData class completely. </p>
+ */
+ private static class ShipmentBinding extends TupleSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private ShipmentBinding(ClassCatalog classCatalog, Class dataClass) {
+
+ super(classCatalog, dataClass);
+ }
+
+ /**
+ * Create 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.
+ */
+ public Object entryToObject(TupleInput keyInput, Object dataInput) {
+
+ String partNumber = keyInput.readString();
+ String supplierNumber = keyInput.readString();
+ Shipment shipment = (Shipment) dataInput;
+ shipment.setKey(partNumber, supplierNumber);
+ return shipment;
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public void objectToKey(Object object, TupleOutput output) {
+
+ Shipment shipment = (Shipment) object;
+ output.writeString(shipment.getPartNumber());
+ output.writeString(shipment.getSupplierNumber());
+ }
+
+ /**
+ * Return the entity as the stored data. There is nothing to do here
+ * since the entity's key fields are transient.
+ */
+ public Object objectToData(Object object) {
+
+ return object;
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/Shipment.java b/examples_java/src/collections/ship/sentity/Shipment.java
new file mode 100644
index 0000000..cb6f91e
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/Shipment.java
@@ -0,0 +1,75 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+import java.io.Serializable;
+
+/**
+ * A Shipment represents the combined key/data pair for a shipment entity.
+ *
+ * <p> In this sample, Shipment is created from the stored key/data entry
+ * using TupleSerialEntityBinding. See {@link SampleViews.PartBinding} for
+ * details.
+ * </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s)
+ * are transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a ShipmentData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Shipment implements Serializable {
+
+ private transient String partNumber;
+ private transient String supplierNumber;
+ private int quantity;
+
+ public Shipment(String partNumber, String supplierNumber, int quantity) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ this.quantity = quantity;
+ }
+
+ /**
+ * Set the transient key fields after deserializing. This method is only
+ * called by data bindings.
+ */
+ void setKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[Shipment: part=" + partNumber +
+ " supplier=" + supplierNumber +
+ " quantity=" + quantity + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/ShipmentKey.java b/examples_java/src/collections/ship/sentity/ShipmentKey.java
new file mode 100644
index 0000000..374e1bd
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/ShipmentKey.java
@@ -0,0 +1,46 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+/**
+ * A ShipmentKey serves as the key in the key/data pair for a shipment entity.
+ *
+ * <p> In this sample, ShipmentKey is bound to the key's tuple storage entry
+ * using a TupleBinding. Because it is not used directly as storage data, it
+ * does not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentKey {
+
+ private String partNumber;
+ private String supplierNumber;
+
+ public ShipmentKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public String toString() {
+
+ return "[ShipmentKey: supplier=" + supplierNumber +
+ " part=" + partNumber + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/Supplier.java b/examples_java/src/collections/ship/sentity/Supplier.java
new file mode 100644
index 0000000..a9e7bc7
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/Supplier.java
@@ -0,0 +1,82 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+import java.io.Serializable;
+
+/**
+ * A Supplier represents the combined key/data pair for a supplier entity.
+ *
+ * <p> In this sample, Supplier is created from the stored key/data entry
+ * using TupleSerialEntityBinding. See {@link SampleViews.PartBinding} for
+ * details.
+ * </p>
+ *
+ * <p> The binding is "tricky" in that it uses this class for both the stored
+ * data entry and the combined entity object. To do this, the key field(s) are
+ * transient and are set by the binding after the data object has been
+ * deserialized. This avoids the use of a SupplierData class completely. </p>
+ *
+ * <p> Since this class is used directly for data storage, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Supplier implements Serializable {
+
+ private transient String number;
+ private String name;
+ private int status;
+ private String city;
+
+ public Supplier(String number, String name, int status, String city) {
+
+ this.number = number;
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ /**
+ * Set the transient key fields after deserializing. This method is only
+ * called by data bindings.
+ */
+ void setKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Supplier: number=" + number +
+ " name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/SupplierKey.java b/examples_java/src/collections/ship/sentity/SupplierKey.java
new file mode 100644
index 0000000..1d30a3c
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/SupplierKey.java
@@ -0,0 +1,38 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+/**
+ * A SupplierKey serves as the key in the key/data pair for a supplier entity.
+ *
+ * <p> In this sample, SupplierKey is bound to the key's tuple storage entry
+ * using a TupleBinding. Because it is not used directly as storage data, it
+ * does not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierKey {
+
+ private String number;
+
+ public SupplierKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[SupplierKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/sentity/Weight.java b/examples_java/src/collections/ship/sentity/Weight.java
new file mode 100644
index 0000000..39e6e1c
--- /dev/null
+++ b/examples_java/src/collections/ship/sentity/Weight.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.sentity;
+
+import java.io.Serializable;
+
+/**
+ * Weight represents a weight amount and unit of measure.
+ *
+ * <p> In this sample, Weight is embedded in part data values which are stored
+ * as Java serialized objects; therefore Weight must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Weight implements Serializable {
+
+ public final static String GRAMS = "grams";
+ public final static String OUNCES = "ounces";
+
+ private double amount;
+ private String units;
+
+ public Weight(double amount, String units) {
+
+ this.amount = amount;
+ this.units = units;
+ }
+
+ public final double getAmount() {
+
+ return amount;
+ }
+
+ public final String getUnits() {
+
+ return units;
+ }
+
+ public String toString() {
+
+ return "[" + amount + ' ' + units + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/Part.java b/examples_java/src/collections/ship/tuple/Part.java
new file mode 100644
index 0000000..a821e0a
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/Part.java
@@ -0,0 +1,72 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+/**
+ * A Part represents the combined key/data pair for a part entity.
+ *
+ * <p> In this sample, Part is created from the stored key/data entry using a
+ * SerialSerialBinding. See {@link SampleViews.PartBinding} for details.
+ * Since this class is not directly used for data storage, it does not need to
+ * be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Part {
+
+ private String number;
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public Part(String number, String name, String color, Weight weight,
+ String city) {
+
+ this.number = number;
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Part: number=" + number +
+ " name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/PartData.java b/examples_java/src/collections/ship/tuple/PartData.java
new file mode 100644
index 0000000..8ccc3b1
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/PartData.java
@@ -0,0 +1,65 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+import java.io.Serializable;
+
+/**
+ * A PartData serves as the value in the key/value pair for a part entity.
+ *
+ * <p> In this sample, PartData is used only as the storage data for the
+ * value, while the Part object is used as the value's object representation.
+ * Because it is used directly as storage data using serial format, it must be
+ * Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartData implements Serializable {
+
+ private String name;
+ private String color;
+ private Weight weight;
+ private String city;
+
+ public PartData(String name, String color, Weight weight, String city) {
+
+ this.name = name;
+ this.color = color;
+ this.weight = weight;
+ this.city = city;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final String getColor() {
+
+ return color;
+ }
+
+ public final Weight getWeight() {
+
+ return weight;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[PartData: name=" + name +
+ " color=" + color +
+ " weight=" + weight +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/PartKey.java b/examples_java/src/collections/ship/tuple/PartKey.java
new file mode 100644
index 0000000..ff65812
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/PartKey.java
@@ -0,0 +1,38 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+/**
+ * A PartKey serves as the key in the key/data pair for a part entity.
+ *
+ * <p> In this sample, PartKey is bound to the key's tuple storage entry using
+ * a TupleBinding. Because it is not used directly as storage data, it does
+ * not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class PartKey {
+
+ private String number;
+
+ public PartKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[PartKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/Sample.java b/examples_java/src/collections/ship/tuple/Sample.java
new file mode 100644
index 0000000..9c14fc3
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/Sample.java
@@ -0,0 +1,235 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+import java.io.FileNotFoundException;
+import java.util.Iterator;
+import java.util.Set;
+
+import com.sleepycat.collections.TransactionRunner;
+import com.sleepycat.collections.TransactionWorker;
+import com.sleepycat.db.DatabaseException;
+
+/**
+ * Sample is the main entry point for the sample program and may be run as
+ * follows:
+ *
+ * <pre>
+ * java collections.ship.tuple.Sample
+ * [-h <home-directory> ]
+ * </pre>
+ *
+ * <p> The default for the home directory is ./tmp -- the tmp subdirectory of
+ * the current directory where the sample is run. The home directory must exist
+ * before running the sample. To recreate the sample database from scratch,
+ * delete all files in the home directory before running the sample. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Sample {
+
+ private SampleDatabase db;
+ private SampleViews views;
+
+ /**
+ * Run the sample program.
+ */
+ public static void main(String[] args) {
+
+ System.out.println("\nRunning sample: " + Sample.class);
+
+ // Parse the command line arguments.
+ //
+ String homeDir = "./tmp";
+ for (int i = 0; i < args.length; i += 1) {
+ if (args[i].equals("-h") && i < args.length - 1) {
+ i += 1;
+ homeDir = args[i];
+ } else {
+ System.err.println("Usage:\n java " + Sample.class.getName() +
+ "\n [-h <home-directory>]");
+ System.exit(2);
+ }
+ }
+
+ // Run the sample.
+ //
+ Sample sample = null;
+ try {
+ sample = new Sample(homeDir);
+ sample.run();
+ } catch (Exception e) {
+ // If an exception reaches this point, the last transaction did not
+ // complete. If the exception is RunRecoveryException, follow
+ // the Berkeley DB recovery procedures before running again.
+ e.printStackTrace();
+ } finally {
+ if (sample != null) {
+ try {
+ // Always attempt to close the database cleanly.
+ sample.close();
+ } catch (Exception e) {
+ System.err.println("Exception during database close:");
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+
+ /**
+ * Open the database and views.
+ */
+ private Sample(String homeDir)
+ throws DatabaseException, FileNotFoundException {
+
+ db = new SampleDatabase(homeDir);
+ views = new SampleViews(db);
+ }
+
+ /**
+ * Close the database cleanly.
+ */
+ private void close()
+ throws DatabaseException {
+
+ db.close();
+ }
+
+ /**
+ * Run two transactions to populate and print the database. A
+ * TransactionRunner is used to ensure consistent handling of transactions,
+ * including deadlock retries. But the best transaction handling mechanism
+ * to use depends on the application.
+ */
+ private void run()
+ throws Exception {
+
+ TransactionRunner runner = new TransactionRunner(db.getEnvironment());
+ runner.run(new PopulateDatabase());
+ runner.run(new PrintDatabase());
+ }
+
+ /**
+ * Populate the database in a single transaction.
+ */
+ private class PopulateDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ addSuppliers();
+ addParts();
+ addShipments();
+ }
+ }
+
+ /**
+ * Print the database in a single transaction. All entities are printed
+ * and the indices are used to print the entities for certain keys.
+ *
+ * <p> Note the use of special iterator() methods. These are used here
+ * with indices to find the shipments for certain keys.</p>
+ */
+ private class PrintDatabase implements TransactionWorker {
+
+ public void doWork()
+ throws Exception {
+ printValues("Parts",
+ views.getPartSet().iterator());
+ printValues("Suppliers",
+ views.getSupplierSet().iterator());
+ printValues("Suppliers for City Paris",
+ views.getSupplierByCityMap().duplicates(
+ "Paris").iterator());
+ printValues("Shipments",
+ views.getShipmentSet().iterator());
+ printValues("Shipments for Part P1",
+ views.getShipmentByPartMap().duplicates(
+ new PartKey("P1")).iterator());
+ printValues("Shipments for Supplier S1",
+ views.getShipmentBySupplierMap().duplicates(
+ new SupplierKey("S1")).iterator());
+ }
+ }
+
+ /**
+ * Populate the part entities in the database. If the part set is not
+ * empty, assume that this has already been done.
+ */
+ private void addParts() {
+
+ Set parts = views.getPartSet();
+ if (parts.isEmpty()) {
+ System.out.println("Adding Parts");
+ parts.add(new Part("P1", "Nut", "Red",
+ new Weight(12.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P2", "Bolt", "Green",
+ new Weight(17.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P3", "Screw", "Blue",
+ new Weight(17.0, Weight.GRAMS), "Rome"));
+ parts.add(new Part("P4", "Screw", "Red",
+ new Weight(14.0, Weight.GRAMS), "London"));
+ parts.add(new Part("P5", "Cam", "Blue",
+ new Weight(12.0, Weight.GRAMS), "Paris"));
+ parts.add(new Part("P6", "Cog", "Red",
+ new Weight(19.0, Weight.GRAMS), "London"));
+ }
+ }
+
+ /**
+ * Populate the supplier entities in the database. If the supplier set is
+ * not empty, assume that this has already been done.
+ */
+ private void addSuppliers() {
+
+ Set suppliers = views.getSupplierSet();
+ if (suppliers.isEmpty()) {
+ System.out.println("Adding Suppliers");
+ suppliers.add(new Supplier("S1", "Smith", 20, "London"));
+ suppliers.add(new Supplier("S2", "Jones", 10, "Paris"));
+ suppliers.add(new Supplier("S3", "Blake", 30, "Paris"));
+ suppliers.add(new Supplier("S4", "Clark", 20, "London"));
+ suppliers.add(new Supplier("S5", "Adams", 30, "Athens"));
+ }
+ }
+
+ /**
+ * Populate the shipment entities in the database. If the shipment set
+ * is not empty, assume that this has already been done.
+ */
+ private void addShipments() {
+
+ Set shipments = views.getShipmentSet();
+ if (shipments.isEmpty()) {
+ System.out.println("Adding Shipments");
+ shipments.add(new Shipment("P1", "S1", 300));
+ shipments.add(new Shipment("P2", "S1", 200));
+ shipments.add(new Shipment("P3", "S1", 400));
+ shipments.add(new Shipment("P4", "S1", 200));
+ shipments.add(new Shipment("P5", "S1", 100));
+ shipments.add(new Shipment("P6", "S1", 100));
+ shipments.add(new Shipment("P1", "S2", 300));
+ shipments.add(new Shipment("P2", "S2", 400));
+ shipments.add(new Shipment("P2", "S3", 200));
+ shipments.add(new Shipment("P2", "S4", 200));
+ shipments.add(new Shipment("P4", "S4", 300));
+ shipments.add(new Shipment("P5", "S4", 400));
+ }
+ }
+
+ /**
+ * Print the objects returned by an iterator of entity value objects.
+ */
+ private void printValues(String label, Iterator iterator) {
+
+ System.out.println("\n--- " + label + " ---");
+ while (iterator.hasNext()) {
+ System.out.println(iterator.next().toString());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/SampleDatabase.java b/examples_java/src/collections/ship/tuple/SampleDatabase.java
new file mode 100644
index 0000000..ed42591
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/SampleDatabase.java
@@ -0,0 +1,323 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialKeyCreator;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.ForeignKeyDeleteAction;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+
+/**
+ * SampleDatabase defines the storage containers, indices and foreign keys
+ * for the sample database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleDatabase {
+
+ private static final String CLASS_CATALOG = "java_class_catalog";
+ private static final String SUPPLIER_STORE = "supplier_store";
+ private static final String PART_STORE = "part_store";
+ private static final String SHIPMENT_STORE = "shipment_store";
+ private static final String SHIPMENT_PART_INDEX = "shipment_part_index";
+ private static final String SHIPMENT_SUPPLIER_INDEX =
+ "shipment_supplier_index";
+ private static final String SUPPLIER_CITY_INDEX = "supplier_city_index";
+
+ private Environment env;
+ private Database partDb;
+ private Database supplierDb;
+ private Database shipmentDb;
+ private SecondaryDatabase supplierByCityDb;
+ private SecondaryDatabase shipmentByPartDb;
+ private SecondaryDatabase shipmentBySupplierDb;
+ private StoredClassCatalog javaCatalog;
+
+ /**
+ * Open all storage containers, indices, and catalogs.
+ */
+ public SampleDatabase(String homeDirectory)
+ throws DatabaseException, FileNotFoundException {
+
+ // Open the Berkeley DB environment in transactional mode.
+ //
+ System.out.println("Opening environment in: " + homeDirectory);
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setTransactional(true);
+ envConfig.setAllowCreate(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(new File(homeDirectory), envConfig);
+
+ // Set the Berkeley DB config for opening all stores.
+ //
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+
+ // Create the Serial class catalog. This holds the serialized class
+ // format for all database records of serial format.
+ //
+ Database catalogDb = env.openDatabase(null, CLASS_CATALOG, null,
+ dbConfig);
+ javaCatalog = new StoredClassCatalog(catalogDb);
+
+ // Open the Berkeley DB database for the part, supplier and shipment
+ // stores. The stores are opened with no duplicate keys allowed.
+ //
+ partDb = env.openDatabase(null, PART_STORE, null, dbConfig);
+
+ supplierDb = env.openDatabase(null, SUPPLIER_STORE, null, dbConfig);
+
+ shipmentDb = env.openDatabase(null, SHIPMENT_STORE, null, dbConfig);
+
+ // Open the SecondaryDatabase for the city index of the supplier store,
+ // and for the part and supplier indices of the shipment store.
+ // Duplicate keys are allowed since more than one supplier may be in
+ // the same city, and more than one shipment may exist for the same
+ // supplier or part. A foreign key constraint is defined for the
+ // supplier and part indices to ensure that a shipment only refers to
+ // existing part and supplier keys. The CASCADE delete action means
+ // that shipments will be deleted if their associated part or supplier
+ // is deleted.
+ //
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ secConfig.setType(DatabaseType.BTREE);
+ secConfig.setSortedDuplicates(true);
+
+ secConfig.setKeyCreator(new SupplierByCityKeyCreator(javaCatalog,
+ SupplierData.class));
+ supplierByCityDb = env.openSecondaryDatabase(null, SUPPLIER_CITY_INDEX,
+ null, supplierDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(partDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(new ShipmentByPartKeyCreator(javaCatalog,
+ ShipmentData.class));
+ shipmentByPartDb = env.openSecondaryDatabase(null, SHIPMENT_PART_INDEX,
+ null, shipmentDb,
+ secConfig);
+
+ secConfig.setForeignKeyDatabase(supplierDb);
+ secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
+ secConfig.setKeyCreator(new ShipmentBySupplierKeyCreator(javaCatalog,
+ ShipmentData.class));
+ shipmentBySupplierDb = env.openSecondaryDatabase(null,
+ SHIPMENT_SUPPLIER_INDEX,
+ null, shipmentDb,
+ secConfig);
+ }
+
+ /**
+ * Return the storage environment for the database.
+ */
+ public final Environment getEnvironment() {
+
+ return env;
+ }
+
+ /**
+ * Return the class catalog.
+ */
+ public final StoredClassCatalog getClassCatalog() {
+
+ return javaCatalog;
+ }
+
+ /**
+ * Return the part storage container.
+ */
+ public final Database getPartDatabase() {
+
+ return partDb;
+ }
+
+ /**
+ * Return the supplier storage container.
+ */
+ public final Database getSupplierDatabase() {
+
+ return supplierDb;
+ }
+
+ /**
+ * Return the shipment storage container.
+ */
+ public final Database getShipmentDatabase() {
+
+ return shipmentDb;
+ }
+
+ /**
+ * Return the shipment-by-part index.
+ */
+ public final SecondaryDatabase getShipmentByPartDatabase() {
+
+ return shipmentByPartDb;
+ }
+
+ /**
+ * Return the shipment-by-supplier index.
+ */
+ public final SecondaryDatabase getShipmentBySupplierDatabase() {
+
+ return shipmentBySupplierDb;
+ }
+
+ /**
+ * Return the supplier-by-city index.
+ */
+ public final SecondaryDatabase getSupplierByCityDatabase() {
+
+ return supplierByCityDb;
+ }
+
+ /**
+ * Close all stores (closing a store automatically closes its indices).
+ */
+ public void close()
+ throws DatabaseException {
+
+ // Close secondary databases, then primary databases.
+ supplierByCityDb.close();
+ shipmentByPartDb.close();
+ shipmentBySupplierDb.close();
+ partDb.close();
+ supplierDb.close();
+ shipmentDb.close();
+ // And don't forget to close the catalog and the environment.
+ javaCatalog.close();
+ env.close();
+ }
+
+ /**
+ * The SecondaryKeyCreator for the SupplierByCity index. This is an
+ * extension of the abstract class TupleSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys are of the format
+ * TupleFormat and the data values are of the format SerialFormat.
+ */
+ private static class SupplierByCityKeyCreator
+ extends TupleSerialKeyCreator {
+
+ /**
+ * Construct the city key extractor.
+ * @param catalog is the class catalog.
+ * @param valueClass is the supplier value class.
+ */
+ private SupplierByCityKeyCreator(ClassCatalog catalog,
+ Class valueClass) {
+
+ super(catalog, valueClass);
+ }
+
+ /**
+ * Extract the city key from a supplier key/value pair. The city key
+ * is stored in the supplier value, so the supplier key is not used.
+ */
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object valueInput,
+ TupleOutput indexKeyOutput) {
+
+ SupplierData supplierData = (SupplierData) valueInput;
+ String city = supplierData.getCity();
+ if (city != null) {
+ indexKeyOutput.writeString(supplierData.getCity());
+ return true;
+ } else {
+ return false;
+ }
+ }
+ }
+
+ /**
+ * The SecondaryKeyCreator for the ShipmentByPart index. This is an
+ * extension of the abstract class TupleSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys are of the format
+ * TupleFormat and the data values are of the format SerialFormat.
+ */
+ private static class ShipmentByPartKeyCreator
+ extends TupleSerialKeyCreator {
+
+ /**
+ * Construct the part key extractor.
+ * @param catalog is the class catalog.
+ * @param valueClass is the shipment value class.
+ */
+ private ShipmentByPartKeyCreator(ClassCatalog catalog,
+ Class valueClass) {
+ super(catalog, valueClass);
+ }
+
+ /**
+ * Extract the part key from a shipment key/value pair. The part key
+ * is stored in the shipment key, so the shipment value is not used.
+ */
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object valueInput,
+ TupleOutput indexKeyOutput) {
+
+ String partNumber = primaryKeyInput.readString();
+ // don't bother reading the supplierNumber
+ indexKeyOutput.writeString(partNumber);
+ return true;
+ }
+ }
+
+ /**
+ * The SecondaryKeyCreator for the ShipmentBySupplier index. This is an
+ * extension of the abstract class TupleSerialKeyCreator, which implements
+ * SecondaryKeyCreator for the case where the data keys are of the format
+ * TupleFormat and the data values are of the format SerialFormat.
+ */
+ private static class ShipmentBySupplierKeyCreator
+ extends TupleSerialKeyCreator {
+
+ /**
+ * Construct the supplier key extractor.
+ * @param catalog is the class catalog.
+ * @param valueClass is the shipment value class.
+ */
+ private ShipmentBySupplierKeyCreator(ClassCatalog catalog,
+ Class valueClass) {
+ super(catalog, valueClass);
+ }
+
+ /**
+ * Extract the supplier key from a shipment key/value pair. The
+ * supplier key is stored in the shipment key, so the shipment value is
+ * not used.
+ */
+ public boolean createSecondaryKey(TupleInput primaryKeyInput,
+ Object valueInput,
+ TupleOutput indexKeyOutput) {
+
+ primaryKeyInput.readString(); // skip the partNumber
+ String supplierNumber = primaryKeyInput.readString();
+ indexKeyOutput.writeString(supplierNumber);
+ return true;
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/SampleViews.java b/examples_java/src/collections/ship/tuple/SampleViews.java
new file mode 100644
index 0000000..3bb4b1d
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/SampleViews.java
@@ -0,0 +1,396 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+import com.sleepycat.bind.EntityBinding;
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.ClassCatalog;
+import com.sleepycat.bind.serial.TupleSerialBinding;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+import com.sleepycat.collections.StoredSortedMap;
+import com.sleepycat.collections.StoredSortedValueSet;
+
+/**
+ * SampleViews defines the data bindings and collection views for the sample
+ * database.
+ *
+ * @author Mark Hayes
+ */
+public class SampleViews {
+
+ private StoredSortedMap partMap;
+ private StoredSortedMap supplierMap;
+ private StoredSortedMap shipmentMap;
+ private StoredSortedMap shipmentByPartMap;
+ private StoredSortedMap shipmentBySupplierMap;
+ private StoredSortedMap supplierByCityMap;
+
+ /**
+ * Create the data bindings and collection views.
+ */
+ public SampleViews(SampleDatabase db) {
+
+ // Create the data bindings.
+ // In this sample, EntityBinding classes are used to bind the stored
+ // key/data entry pair to a combined data object. For keys, a
+ // one-to-one binding is implemented with EntryBinding classes to bind
+ // the stored tuple entry to a key Object.
+ //
+ ClassCatalog catalog = db.getClassCatalog();
+ EntryBinding partKeyBinding =
+ new PartKeyBinding();
+ EntityBinding partDataBinding =
+ new PartBinding(catalog, PartData.class);
+ EntryBinding supplierKeyBinding =
+ new SupplierKeyBinding();
+ EntityBinding supplierDataBinding =
+ new SupplierBinding(catalog, SupplierData.class);
+ EntryBinding shipmentKeyBinding =
+ new ShipmentKeyBinding();
+ EntityBinding shipmentDataBinding =
+ new ShipmentBinding(catalog, ShipmentData.class);
+ EntryBinding cityKeyBinding =
+ TupleBinding.getPrimitiveBinding(String.class);
+
+ // Create map views for all stores and indices.
+ // StoredSortedMap is used since the stores and indices are ordered
+ // (they use the DB_BTREE access method).
+ //
+ partMap =
+ new StoredSortedMap(db.getPartDatabase(),
+ partKeyBinding, partDataBinding, true);
+ supplierMap =
+ new StoredSortedMap(db.getSupplierDatabase(),
+ supplierKeyBinding, supplierDataBinding, true);
+ shipmentMap =
+ new StoredSortedMap(db.getShipmentDatabase(),
+ shipmentKeyBinding, shipmentDataBinding, true);
+ shipmentByPartMap =
+ new StoredSortedMap(db.getShipmentByPartDatabase(),
+ partKeyBinding, shipmentDataBinding, true);
+ shipmentBySupplierMap =
+ new StoredSortedMap(db.getShipmentBySupplierDatabase(),
+ supplierKeyBinding, shipmentDataBinding, true);
+ supplierByCityMap =
+ new StoredSortedMap(db.getSupplierByCityDatabase(),
+ cityKeyBinding, supplierDataBinding, true);
+ }
+
+ // The views returned below can be accessed using the java.util.Map or
+ // java.util.Set interfaces, or using the StoredSortedMap and
+ // StoredValueSet classes, which provide additional methods. The entity
+ // sets could be obtained directly from the Map.values() method but
+ // convenience methods are provided here to return them in order to avoid
+ // down-casting elsewhere.
+
+ /**
+ * Return a map view of the part storage container.
+ */
+ public StoredSortedMap getPartMap() {
+
+ return partMap;
+ }
+
+ /**
+ * Return a map view of the supplier storage container.
+ */
+ public StoredSortedMap getSupplierMap() {
+
+ return supplierMap;
+ }
+
+ /**
+ * Return a map view of the shipment storage container.
+ */
+ public StoredSortedMap getShipmentMap() {
+
+ return shipmentMap;
+ }
+
+ /**
+ * Return an entity set view of the part storage container.
+ */
+ public StoredSortedValueSet getPartSet() {
+
+ return (StoredSortedValueSet) partMap.values();
+ }
+
+ /**
+ * Return an entity set view of the supplier storage container.
+ */
+ public StoredSortedValueSet getSupplierSet() {
+
+ return (StoredSortedValueSet) supplierMap.values();
+ }
+
+ /**
+ * Return an entity set view of the shipment storage container.
+ */
+ public StoredSortedValueSet getShipmentSet() {
+
+ return (StoredSortedValueSet) shipmentMap.values();
+ }
+
+ /**
+ * Return a map view of the shipment-by-part index.
+ */
+ public StoredSortedMap getShipmentByPartMap() {
+
+ return shipmentByPartMap;
+ }
+
+ /**
+ * Return a map view of the shipment-by-supplier index.
+ */
+ public StoredSortedMap getShipmentBySupplierMap() {
+
+ return shipmentBySupplierMap;
+ }
+
+ /**
+ * Return a map view of the supplier-by-city index.
+ */
+ public final StoredSortedMap getSupplierByCityMap() {
+
+ return supplierByCityMap;
+ }
+
+ /**
+ * PartKeyBinding is used to bind the stored key tuple entry for a part to
+ * a key object representation.
+ */
+ private static class PartKeyBinding extends TupleBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private PartKeyBinding() {
+ }
+
+ /**
+ * Create the key object from the stored key tuple entry.
+ */
+ public Object entryToObject(TupleInput input) {
+
+ String number = input.readString();
+ return new PartKey(number);
+ }
+
+ /**
+ * Create the stored key tuple entry from the key object.
+ */
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ PartKey key = (PartKey) object;
+ output.writeString(key.getNumber());
+ }
+ }
+
+ /**
+ * PartBinding is used to bind the stored key/data entry pair for a part
+ * to a combined data object (entity).
+ */
+ private static class PartBinding extends TupleSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private PartBinding(ClassCatalog classCatalog, Class dataClass) {
+
+ super(classCatalog, dataClass);
+ }
+
+ /**
+ * Create the entity by combining the stored key and data.
+ */
+ public Object entryToObject(TupleInput keyInput, Object dataInput) {
+
+ String number = keyInput.readString();
+ PartData data = (PartData) dataInput;
+ return new Part(number, data.getName(), data.getColor(),
+ data.getWeight(), data.getCity());
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public void objectToKey(Object object, TupleOutput output) {
+
+ Part part = (Part) object;
+ output.writeString(part.getNumber());
+ }
+
+ /**
+ * Create the stored data from the entity.
+ */
+ public Object objectToData(Object object) {
+
+ Part part = (Part) object;
+ return new PartData(part.getName(), part.getColor(),
+ part.getWeight(), part.getCity());
+ }
+ }
+
+ /**
+ * SupplierKeyBinding is used to bind the stored key tuple entry for a
+ * supplier to a key object representation.
+ */
+ private static class SupplierKeyBinding extends TupleBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private SupplierKeyBinding() {
+ }
+
+ /**
+ * Create the key object from the stored key tuple entry.
+ */
+ public Object entryToObject(TupleInput input) {
+
+ String number = input.readString();
+ return new SupplierKey(number);
+ }
+
+ /**
+ * Create the stored key tuple entry from the key object.
+ */
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ SupplierKey key = (SupplierKey) object;
+ output.writeString(key.getNumber());
+ }
+ }
+
+ /**
+ * SupplierBinding is used to bind the stored key/data entry pair for a
+ * supplier to a combined data object (entity).
+ */
+ private static class SupplierBinding extends TupleSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private SupplierBinding(ClassCatalog classCatalog, Class dataClass) {
+
+ super(classCatalog, dataClass);
+ }
+
+ /**
+ * Create the entity by combining the stored key and data.
+ */
+ public Object entryToObject(TupleInput keyInput, Object dataInput) {
+
+ String number = keyInput.readString();
+ SupplierData data = (SupplierData) dataInput;
+ return new Supplier(number, data.getName(),
+ data.getStatus(), data.getCity());
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public void objectToKey(Object object, TupleOutput output) {
+
+ Supplier supplier = (Supplier) object;
+ output.writeString(supplier.getNumber());
+ }
+
+ /**
+ * Create the stored data from the entity.
+ */
+ public Object objectToData(Object object) {
+
+ Supplier supplier = (Supplier) object;
+ return new SupplierData(supplier.getName(), supplier.getStatus(),
+ supplier.getCity());
+ }
+ }
+
+ /**
+ * ShipmentKeyBinding is used to bind the stored key tuple entry for a
+ * shipment to a key object representation.
+ */
+ private static class ShipmentKeyBinding extends TupleBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private ShipmentKeyBinding() {
+ }
+
+ /**
+ * Create the key object from the stored key tuple entry.
+ */
+ public Object entryToObject(TupleInput input) {
+
+ String partNumber = input.readString();
+ String supplierNumber = input.readString();
+ return new ShipmentKey(partNumber, supplierNumber);
+ }
+
+ /**
+ * Create the stored key tuple entry from the key object.
+ */
+ public void objectToEntry(Object object, TupleOutput output) {
+
+ ShipmentKey key = (ShipmentKey) object;
+ output.writeString(key.getPartNumber());
+ output.writeString(key.getSupplierNumber());
+ }
+ }
+
+ /**
+ * ShipmentBinding is used to bind the stored key/data entry pair for a
+ * shipment to a combined data object (entity).
+ */
+ private static class ShipmentBinding extends TupleSerialBinding {
+
+ /**
+ * Construct the binding object.
+ */
+ private ShipmentBinding(ClassCatalog classCatalog, Class dataClass) {
+
+ super(classCatalog, dataClass);
+ }
+
+ /**
+ * Create the entity by combining the stored key and data.
+ */
+ public Object entryToObject(TupleInput keyInput, Object dataInput) {
+
+ String partNumber = keyInput.readString();
+ String supplierNumber = keyInput.readString();
+ ShipmentData data = (ShipmentData) dataInput;
+ return new Shipment(partNumber, supplierNumber,
+ data.getQuantity());
+ }
+
+ /**
+ * Create the stored key from the entity.
+ */
+ public void objectToKey(Object object, TupleOutput output) {
+
+ Shipment shipment = (Shipment) object;
+ output.writeString(shipment.getPartNumber());
+ output.writeString(shipment.getSupplierNumber());
+ }
+
+ /**
+ * Create the stored data from the entity.
+ */
+ public Object objectToData(Object object) {
+
+ Shipment shipment = (Shipment) object;
+ return new ShipmentData(shipment.getQuantity());
+ }
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/Shipment.java b/examples_java/src/collections/ship/tuple/Shipment.java
new file mode 100644
index 0000000..6c15ea0
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/Shipment.java
@@ -0,0 +1,55 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+/**
+ * A Shipment represents the combined key/data pair for a shipment entity.
+ *
+ * <p> In this sample, Shipment is created from the stored key/data entry
+ * using a SerialSerialBinding. See {@link SampleViews.ShipmentBinding} for
+ * details. Since this class is not used directly for data storage, it does
+ * not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Shipment {
+
+ private String partNumber;
+ private String supplierNumber;
+ private int quantity;
+
+ public Shipment(String partNumber, String supplierNumber, int quantity) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ this.quantity = quantity;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[Shipment: part=" + partNumber +
+ " supplier=" + supplierNumber +
+ " quantity=" + quantity + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/ShipmentData.java b/examples_java/src/collections/ship/tuple/ShipmentData.java
new file mode 100644
index 0000000..4009c61
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/ShipmentData.java
@@ -0,0 +1,42 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+import java.io.Serializable;
+
+/**
+ * A ShipmentData serves as the value in the key/value pair for a shipment
+ * entity.
+ *
+ * <p> In this sample, ShipmentData is used only as the storage data for the
+ * value, while the Shipment object is used as the value's object
+ * representation. Because it is used directly as storage data using
+ * serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentData implements Serializable {
+
+ private int quantity;
+
+ public ShipmentData(int quantity) {
+
+ this.quantity = quantity;
+ }
+
+ public final int getQuantity() {
+
+ return quantity;
+ }
+
+ public String toString() {
+
+ return "[ShipmentData: quantity=" + quantity + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/ShipmentKey.java b/examples_java/src/collections/ship/tuple/ShipmentKey.java
new file mode 100644
index 0000000..83d4a18
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/ShipmentKey.java
@@ -0,0 +1,46 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+/**
+ * A ShipmentKey serves as the key in the key/data pair for a shipment entity.
+ *
+ * <p> In this sample, ShipmentKey is bound to the key's tuple storage entry
+ * using a TupleBinding. Because it is not used directly as storage data, it
+ * does not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class ShipmentKey {
+
+ private String partNumber;
+ private String supplierNumber;
+
+ public ShipmentKey(String partNumber, String supplierNumber) {
+
+ this.partNumber = partNumber;
+ this.supplierNumber = supplierNumber;
+ }
+
+ public final String getPartNumber() {
+
+ return partNumber;
+ }
+
+ public final String getSupplierNumber() {
+
+ return supplierNumber;
+ }
+
+ public String toString() {
+
+ return "[ShipmentKey: supplier=" + supplierNumber +
+ " part=" + partNumber + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/Supplier.java b/examples_java/src/collections/ship/tuple/Supplier.java
new file mode 100644
index 0000000..8466271
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/Supplier.java
@@ -0,0 +1,63 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+/**
+ * A Supplier represents the combined key/data pair for a supplier entity.
+ *
+ * <p> In this sample, Supplier is created from the stored key/data entry
+ * using a SerialSerialBinding. See {@link SampleViews.SupplierBinding} for
+ * details. Since this class is not used directly for data storage, it does
+ * not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Supplier {
+
+ private String number;
+ private String name;
+ private int status;
+ private String city;
+
+ public Supplier(String number, String name, int status, String city) {
+
+ this.number = number;
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[Supplier: number=" + number +
+ " name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/SupplierData.java b/examples_java/src/collections/ship/tuple/SupplierData.java
new file mode 100644
index 0000000..6e863b9
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/SupplierData.java
@@ -0,0 +1,58 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+import java.io.Serializable;
+
+/**
+ * A SupplierData serves as the value in the key/value pair for a supplier
+ * entity.
+ *
+ * <p> In this sample, SupplierData is used only as the storage data for the
+ * value, while the Supplier object is used as the value's object
+ * representation. Because it is used directly as storage data using
+ * serial format, it must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierData implements Serializable {
+
+ private String name;
+ private int status;
+ private String city;
+
+ public SupplierData(String name, int status, String city) {
+
+ this.name = name;
+ this.status = status;
+ this.city = city;
+ }
+
+ public final String getName() {
+
+ return name;
+ }
+
+ public final int getStatus() {
+
+ return status;
+ }
+
+ public final String getCity() {
+
+ return city;
+ }
+
+ public String toString() {
+
+ return "[SupplierData: name=" + name +
+ " status=" + status +
+ " city=" + city + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/SupplierKey.java b/examples_java/src/collections/ship/tuple/SupplierKey.java
new file mode 100644
index 0000000..afc1fa0
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/SupplierKey.java
@@ -0,0 +1,38 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+/**
+ * A SupplierKey serves as the key in the key/data pair for a supplier entity.
+ *
+ * <p> In this sample, SupplierKey is bound to the key's tuple storage entry
+ * using a TupleBinding. Because it is not used directly as storage data, it
+ * does not need to be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class SupplierKey {
+
+ private String number;
+
+ public SupplierKey(String number) {
+
+ this.number = number;
+ }
+
+ public final String getNumber() {
+
+ return number;
+ }
+
+ public String toString() {
+
+ return "[SupplierKey: number=" + number + ']';
+ }
+}
diff --git a/examples_java/src/collections/ship/tuple/Weight.java b/examples_java/src/collections/ship/tuple/Weight.java
new file mode 100644
index 0000000..850e5c4
--- /dev/null
+++ b/examples_java/src/collections/ship/tuple/Weight.java
@@ -0,0 +1,49 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package collections.ship.tuple;
+
+import java.io.Serializable;
+
+/**
+ * Weight represents a weight amount and unit of measure.
+ *
+ * <p> In this sample, Weight is embedded in part data values which are stored
+ * as Java serialized objects; therefore Weight must be Serializable. </p>
+ *
+ * @author Mark Hayes
+ */
+public class Weight implements Serializable {
+
+ public final static String GRAMS = "grams";
+ public final static String OUNCES = "ounces";
+
+ private double amount;
+ private String units;
+
+ public Weight(double amount, String units) {
+
+ this.amount = amount;
+ this.units = units;
+ }
+
+ public final double getAmount() {
+
+ return amount;
+ }
+
+ public final String getUnits() {
+
+ return units;
+ }
+
+ public String toString() {
+
+ return "[" + amount + ' ' + units + ']';
+ }
+}
diff --git a/examples_java/src/db/AccessExample.java b/examples_java/src/db/AccessExample.java
new file mode 100644
index 0000000..841dae5
--- /dev/null
+++ b/examples_java/src/db/AccessExample.java
@@ -0,0 +1,182 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+
+package db;
+
+import com.sleepycat.db.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.PrintStream;
+
+class AccessExample {
+ private static final int EXIT_SUCCESS = 0;
+ private static final int EXIT_FAILURE = 1;
+
+ public AccessExample() {
+ }
+
+ public static void usage() {
+ System.out.println("usage: java " +
+ "db.AccessExample [-r] [database]\n");
+ System.exit(EXIT_FAILURE);
+ }
+
+ public static void main(String[] argv) {
+ boolean removeExistingDatabase = false;
+ String databaseName = "access.db";
+
+ for (int i = 0; i < argv.length; i++) {
+ if (argv[i].equals("-r"))
+ removeExistingDatabase = true;
+ else if (argv[i].equals("-?"))
+ usage();
+ else if (argv[i].startsWith("-"))
+ usage();
+ else {
+ if ((argv.length - i) != 1)
+ usage();
+ databaseName = argv[i];
+ break;
+ }
+ }
+
+ try {
+ AccessExample app = new AccessExample();
+ app.run(removeExistingDatabase, databaseName);
+ } catch (DatabaseException dbe) {
+ System.err.println("AccessExample: " + dbe.toString());
+ System.exit(EXIT_FAILURE);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("AccessExample: " + fnfe.toString());
+ System.exit(EXIT_FAILURE);
+ }
+ System.exit(EXIT_SUCCESS);
+ }
+
+ // Prompts for a line, and keeps prompting until a non blank
+ // line is returned. Returns null on error.
+ //
+ public static String askForLine(InputStreamReader reader,
+ PrintStream out, String prompt) {
+ String result = "";
+ while (result != null && result.length() == 0) {
+ out.print(prompt);
+ out.flush();
+ result = getLine(reader);
+ }
+ return result;
+ }
+
+ // Not terribly efficient, but does the job.
+ // Works for reading a line from stdin or a file.
+ // Returns null on EOF. If EOF appears in the middle
+ // of a line, returns that line, then null on next call.
+ //
+ public static String getLine(InputStreamReader reader) {
+ StringBuffer b = new StringBuffer();
+ int c;
+ try {
+ while ((c = reader.read()) != -1 && c != '\n') {
+ if (c != '\r')
+ b.append((char)c);
+ }
+ } catch (IOException ioe) {
+ c = -1;
+ }
+
+ if (c == -1 && b.length() == 0)
+ return null;
+ else
+ return b.toString();
+ }
+
+ public void run(boolean removeExistingDatabase, String databaseName)
+ throws DatabaseException, FileNotFoundException {
+
+ // Remove the previous database.
+ if (removeExistingDatabase)
+ new File(databaseName).delete();
+
+ // Create the database object.
+ // There is no environment for this simple example.
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setErrorStream(System.err);
+ dbConfig.setErrorPrefix("AccessExample");
+ dbConfig.setType(DatabaseType.BTREE);
+ dbConfig.setAllowCreate(true);
+ Database table = new Database(databaseName, null, dbConfig);
+
+ //
+ // Insert records into the database, where the key is the user
+ // input and the data is the user input in reverse order.
+ //
+ InputStreamReader reader = new InputStreamReader(System.in);
+
+ for (;;) {
+ String line = askForLine(reader, System.out, "input> ");
+ if (line == null)
+ break;
+
+ String reversed = (new StringBuffer(line)).reverse().toString();
+
+ // See definition of StringDbt below
+ //
+ StringEntry key = new StringEntry(line);
+ StringEntry data = new StringEntry(reversed);
+
+ try {
+ if (table.putNoOverwrite(null, key, data) == OperationStatus.KEYEXIST)
+ System.out.println("Key " + line + " already exists.");
+ } catch (DatabaseException dbe) {
+ System.out.println(dbe.toString());
+ }
+ }
+
+ // Acquire an iterator for the table.
+ Cursor cursor;
+ cursor = table.openCursor(null, null);
+
+ // Walk through the table, printing the key/data pairs.
+ // See class StringDbt defined below.
+ //
+ StringEntry key = new StringEntry();
+ StringEntry data = new StringEntry();
+ while (cursor.getNext(key, data, null) == OperationStatus.SUCCESS)
+ System.out.println(key.getString() + " : " + data.getString());
+ cursor.close();
+ table.close();
+ }
+
+ // Here's an example of how you can extend DatabaseEntry in a
+ // straightforward way to allow easy storage/retrieval of strings,
+ // or whatever kind of data you wish. We've declared it as a static
+ // inner class, but it need not be.
+ //
+ static /*inner*/
+ class StringEntry extends DatabaseEntry {
+ StringEntry() {
+ }
+
+ StringEntry(String value) {
+ setString(value);
+ }
+
+ void setString(String value) {
+ byte[] data = value.getBytes();
+ setData(data);
+ setSize(data.length);
+ }
+
+ String getString() {
+ return new String(getData(), getOffset(), getSize());
+ }
+ }
+}
diff --git a/examples_java/src/db/BtRecExample.java b/examples_java/src/db/BtRecExample.java
new file mode 100644
index 0000000..71beda0
--- /dev/null
+++ b/examples_java/src/db/BtRecExample.java
@@ -0,0 +1,287 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+
+package db;
+
+import com.sleepycat.db.*;
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.PrintStream;
+
+public class BtRecExample {
+ static final String progname = "BtRecExample"; // Program name.
+ static final String database = "access.db";
+ static final String wordlist = "../test/wordlist";
+
+ BtRecExample(BufferedReader reader)
+ throws DatabaseException, IOException, FileNotFoundException {
+
+ OperationStatus status;
+
+ // Remove the previous database.
+ File f = new File(database);
+ f.delete();
+
+ DatabaseConfig config = new DatabaseConfig();
+
+ config.setErrorStream(System.err);
+ config.setErrorPrefix(progname);
+ config.setPageSize(1024); // 1K page sizes.
+
+ config.setBtreeRecordNumbers(true);
+ config.setType(DatabaseType.BTREE);
+ config.setAllowCreate(true);
+ db = new Database(database, null, config);
+
+ //
+ // Insert records into the database, where the key is the word
+ // preceded by its record number, and the data is the same, but
+ // in reverse order.
+ //
+
+ for (int cnt = 1; cnt <= 1000; ++cnt) {
+ String numstr = String.valueOf(cnt);
+ while (numstr.length() < 4)
+ numstr = "0" + numstr;
+ String buf = numstr + '_' + reader.readLine();
+ StringBuffer rbuf = new StringBuffer(buf).reverse();
+
+ StringEntry key = new StringEntry(buf);
+ StringEntry data = new StringEntry(rbuf.toString());
+
+ status = db.putNoOverwrite(null, key, data);
+ if (status != OperationStatus.SUCCESS &&
+ status!= OperationStatus.KEYEXIST)
+ throw new DatabaseException("Database.put failed " + status);
+ }
+ }
+
+ void run() throws DatabaseException {
+ int recno;
+ OperationStatus status;
+
+ // Acquire a cursor for the database.
+ cursor = db.openCursor(null, null);
+
+ //
+ // Prompt the user for a record number, then retrieve and display
+ // that record.
+ //
+ InputStreamReader reader = new InputStreamReader(System.in);
+
+ for (;;) {
+ // Get a record number.
+ String line = askForLine(reader, System.out, "recno #> ");
+ if (line == null)
+ break;
+
+ try {
+ recno = Integer.parseInt(line);
+ } catch (NumberFormatException nfe) {
+ System.err.println("Bad record number: " + nfe);
+ continue;
+ }
+
+ //
+ // Start with a fresh key each time, the db.get() routine returns
+ // the key and data pair, not just the key!
+ //
+ RecnoStringEntry key = new RecnoStringEntry(recno, 4);
+ RecnoStringEntry data = new RecnoStringEntry(4);
+
+ status = cursor.getSearchRecordNumber(key, data, null);
+ if (status != OperationStatus.SUCCESS)
+ throw new DatabaseException("Cursor.setRecno failed: " + status);
+
+ // Display the key and data.
+ show("k/d\t", key, data);
+
+ // Move the cursor a record forward.
+ status = cursor.getNext(key, data, null);
+ if (status != OperationStatus.SUCCESS)
+ throw new DatabaseException("Cursor.getNext failed: " + status);
+
+ // Display the key and data.
+ show("next\t", key, data);
+
+ RecnoStringEntry datano = new RecnoStringEntry(4);
+
+ //
+ // Retrieve the record number for the following record into
+ // local memory.
+ //
+ status = cursor.getRecordNumber(datano, null);
+ if (status != OperationStatus.SUCCESS &&
+ status != OperationStatus.NOTFOUND &&
+ status != OperationStatus.KEYEMPTY)
+ throw new DatabaseException("Cursor.get failed: " + status);
+ else {
+ recno = datano.getRecordNumber();
+ System.out.println("retrieved recno: " + recno);
+ }
+ }
+
+ cursor.close();
+ cursor = null;
+ }
+
+ //
+ // Print out the number of records in the database.
+ //
+ void stats() throws DatabaseException {
+ BtreeStats stats;
+
+ stats = (BtreeStats)db.getStats(null, null);
+ System.out.println(progname + ": database contains " +
+ stats.getNumData() + " records");
+ }
+
+ void show(String msg, RecnoStringEntry key, RecnoStringEntry data)
+ throws DatabaseException {
+
+ System.out.println(msg + key.getString() + ": " + data.getString());
+ }
+
+ public void shutdown() throws DatabaseException {
+ if (cursor != null) {
+ cursor.close();
+ cursor = null;
+ }
+ if (db != null) {
+ db.close();
+ db = null;
+ }
+ }
+
+ public static void main(String[] argv) {
+ try {
+ // Open the word database.
+ FileReader freader = new FileReader(wordlist);
+
+ BtRecExample app = new BtRecExample(new BufferedReader(freader));
+
+ // Close the word database.
+ freader.close();
+ freader = null;
+
+ app.stats();
+ app.run();
+ } catch (FileNotFoundException fnfe) {
+ System.err.println(progname + ": unexpected open error " + fnfe);
+ System.exit (1);
+ } catch (IOException ioe) {
+ System.err.println(progname + ": open " + wordlist + ": " + ioe);
+ System.exit (1);
+ } catch (DatabaseException dbe) {
+ System.err.println("Exception: " + dbe);
+ System.exit(dbe.getErrno());
+ }
+
+ System.exit(0);
+ }
+
+ // Prompts for a line, and keeps prompting until a non blank
+ // line is returned. Returns null on error.
+ //
+ public static String askForLine(InputStreamReader reader,
+ PrintStream out, String prompt) {
+ String result = "";
+ while (result != null && result.length() == 0) {
+ out.print(prompt);
+ out.flush();
+ result = getLine(reader);
+ }
+ return result;
+ }
+
+ // Not terribly efficient, but does the job.
+ // Works for reading a line from stdin or a file.
+ // Returns null on EOF. If EOF appears in the middle
+ // of a line, returns that line, then null on next call.
+ //
+ public static String getLine(InputStreamReader reader) {
+ StringBuffer b = new StringBuffer();
+ int c;
+ try {
+ while ((c = reader.read()) != -1 && c != '\n') {
+ if (c != '\r')
+ b.append((char)c);
+ }
+ } catch (IOException ioe) {
+ c = -1;
+ }
+
+ if (c == -1 && b.length() == 0)
+ return null;
+ else
+ return b.toString();
+ }
+
+ private Cursor cursor;
+ private Database db;
+
+ // Here's an example of how you can extend DatabaseEntry in a
+ // straightforward way to allow easy storage/retrieval of strings.
+ // We've declared it as a static inner class, but it need not be.
+ //
+ static class StringEntry extends DatabaseEntry {
+ StringEntry() {}
+
+ StringEntry(String value) {
+ setString(value);
+ }
+
+ void setString(String value) {
+ byte[] data = value.getBytes();
+ setData(data);
+ setSize(data.length);
+ }
+
+ String getString() {
+ return new String(getData(), 0, getSize());
+ }
+ }
+
+ // Here's an example of how you can extend DatabaseEntry to store
+ // (potentially) both recno's and strings in the same structure.
+ //
+ static class RecnoStringEntry extends DatabaseEntry {
+ RecnoStringEntry(int maxsize) {
+ this(0, maxsize); // let other constructor do most of the work
+ }
+
+ RecnoStringEntry(int value, int maxsize) {
+ arr = new byte[maxsize];
+ setRecordNumber(value);
+ setSize(arr.length);
+ }
+
+ RecnoStringEntry(String value) {
+ byte[] data = value.getBytes();
+ setData(data); // use our local array for data
+ setUserBuffer(data.length, true);
+ }
+
+ void setString(String value) {
+ byte[] data = value.getBytes();
+ setData(data);
+ setSize(data.length);
+ }
+
+ String getString() {
+ return new String(getData(), getOffset(), getSize());
+ }
+
+ byte[] arr;
+ }
+}
diff --git a/examples_java/src/db/BulkAccessExample.java b/examples_java/src/db/BulkAccessExample.java
new file mode 100644
index 0000000..6d0be08
--- /dev/null
+++ b/examples_java/src/db/BulkAccessExample.java
@@ -0,0 +1,161 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db;
+
+import com.sleepycat.db.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.PrintStream;
+
+class BulkAccessExample {
+ private static final String FileName = "access.db";
+
+ public BulkAccessExample() {
+ }
+
+ public static void main(String[] argv) {
+ try {
+ BulkAccessExample app = new BulkAccessExample();
+ app.run();
+ } catch (DatabaseException dbe) {
+ System.err.println("BulkAccessExample: " + dbe.toString());
+ System.exit(1);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("BulkAccessExample: " + fnfe.toString());
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+
+ // Prompts for a line, and keeps prompting until a non blank
+ // line is returned. Returns null on error.
+ //
+ public static String askForLine(InputStreamReader reader,
+ PrintStream out, String prompt) {
+ String result = "";
+ while (result != null && result.length() == 0) {
+ out.print(prompt);
+ out.flush();
+ result = getLine(reader);
+ }
+ return result;
+ }
+
+ // Not terribly efficient, but does the job.
+ // Works for reading a line from stdin or a file.
+ // Returns null on EOF. If EOF appears in the middle
+ // of a line, returns that line, then null on next call.
+ //
+ public static String getLine(InputStreamReader reader) {
+ StringBuffer b = new StringBuffer();
+ int c;
+ try {
+ while ((c = reader.read()) != -1 && c != '\n') {
+ if (c != '\r')
+ b.append((char)c);
+ }
+ } catch (IOException ioe) {
+ c = -1;
+ }
+
+ if (c == -1 && b.length() == 0)
+ return null;
+ else
+ return b.toString();
+ }
+
+ public void run() throws DatabaseException, FileNotFoundException {
+ // Remove the previous database.
+ new File(FileName).delete();
+
+ // Create the database object.
+ // There is no environment for this simple example.
+ DatabaseConfig config = new DatabaseConfig();
+ config.setErrorStream(System.err);
+ config.setErrorPrefix("BulkAccessExample");
+ config.setType(DatabaseType.BTREE);
+ config.setAllowCreate(true);
+ config.setMode(0644);
+ Database table = new Database(FileName, null, config);
+
+ //
+ // Insert records into the database, where the key is the user
+ // input and the data is the user input in reverse order.
+ //
+ InputStreamReader reader = new InputStreamReader(System.in);
+
+ for (;;) {
+ String line = askForLine(reader, System.out, "input> ");
+ if (line == null || (line.compareToIgnoreCase("end") == 0))
+ break;
+
+ String reversed = (new StringBuffer(line)).reverse().toString();
+
+ // See definition of StringEntry below
+ //
+ StringEntry key = new StringEntry(line);
+ StringEntry data = new StringEntry(reversed);
+
+ try {
+ if (table.putNoOverwrite(null, key, data) == OperationStatus.KEYEXIST)
+ System.out.println("Key " + line + " already exists.");
+ } catch (DatabaseException dbe) {
+ System.out.println(dbe.toString());
+ }
+ System.out.println("");
+ }
+
+ // Acquire a cursor for the table.
+ Cursor cursor = table.openCursor(null, null);
+ DatabaseEntry foo = new DatabaseEntry();
+
+ MultipleKeyDataEntry bulk_data = new MultipleKeyDataEntry();
+ bulk_data.setData(new byte[1024 * 1024]);
+ bulk_data.setUserBuffer(1024 * 1024, true);
+
+ // Walk through the table, printing the key/data pairs.
+ //
+ while (cursor.getNext(foo, bulk_data, null) == OperationStatus.SUCCESS) {
+ StringEntry key, data;
+ key = new StringEntry();
+ data = new StringEntry();
+
+ while (bulk_data.next(key, data))
+ System.out.println(key.getString() + " : " + data.getString());
+ }
+ cursor.close();
+ table.close();
+ }
+
+ // Here's an example of how you can extend DatabaseEntry in a
+ // straightforward way to allow easy storage/retrieval of strings, or
+ // whatever kind of data you wish. We've declared it as a static inner
+ // class, but it need not be.
+ //
+ static class StringEntry extends DatabaseEntry {
+ StringEntry() {
+ }
+
+ StringEntry(String value) {
+ setString(value);
+ }
+
+ void setString(String value) {
+ byte[] data = value.getBytes();
+ setData(data);
+ setSize(data.length);
+ }
+
+ String getString() {
+ return new String(getData(), getOffset(), getSize());
+ }
+ }
+}
diff --git a/examples_java/src/db/BulkAccessNIOExample.java b/examples_java/src/db/BulkAccessNIOExample.java
new file mode 100644
index 0000000..9f97c4b
--- /dev/null
+++ b/examples_java/src/db/BulkAccessNIOExample.java
@@ -0,0 +1,180 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db;
+
+import com.sleepycat.db.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.nio.ByteBuffer;
+
+class BulkAccessNIOExample {
+ private static final String FileName = "access.db";
+
+ public BulkAccessNIOExample() {
+ }
+
+ public static void main(String[] argv) {
+ try {
+ BulkAccessNIOExample app = new BulkAccessNIOExample();
+ app.run();
+ } catch (DatabaseException dbe) {
+ System.err.println("BulkAccessNIOExample: " + dbe.toString());
+ System.exit(1);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("BulkAccessNIOExample: " + fnfe.toString());
+ System.exit(1);
+ }
+ System.exit(0);
+ }
+
+ // Prompts for a line, and keeps prompting until a non blank
+ // line is returned. Returns null on error.
+ //
+ public static String askForLine(InputStreamReader reader,
+ PrintStream out, String prompt) {
+ String result = "";
+ while (result != null && result.length() == 0) {
+ out.print(prompt);
+ out.flush();
+ result = getLine(reader);
+ }
+ return result;
+ }
+
+ // Not terribly efficient, but does the job.
+ // Works for reading a line from stdin or a file.
+ // Returns null on EOF. If EOF appears in the middle
+ // of a line, returns that line, then null on next call.
+ //
+ public static String getLine(InputStreamReader reader) {
+ StringBuffer b = new StringBuffer();
+ int c;
+ try {
+ while ((c = reader.read()) != -1 && c != '\n') {
+ if (c != '\r')
+ b.append((char)c);
+ }
+ } catch (IOException ioe) {
+ c = -1;
+ }
+
+ if (c == -1 && b.length() == 0)
+ return null;
+ else
+ return b.toString();
+ }
+
+ public void run() throws DatabaseException, FileNotFoundException {
+ // Remove the previous database.
+ new File(FileName).delete();
+
+ // Create the database object.
+ // There is no environment for this simple example.
+ DatabaseConfig config = new DatabaseConfig();
+ config.setErrorStream(System.err);
+ config.setErrorPrefix("BulkAccessNIOExample");
+ config.setType(DatabaseType.BTREE);
+ config.setAllowCreate(true);
+ config.setMode(0644);
+ Database table = new Database(FileName, null, config);
+
+ //
+ // Insert records into the database, where the key is the user
+ // input and the data is the user input in reverse order.
+ //
+ InputStreamReader reader = new InputStreamReader(System.in);
+
+ for (;;) {
+ String line = askForLine(reader, System.out, "input> ");
+ if (line == null || (line.compareToIgnoreCase("end") == 0))
+ break;
+
+ String reversed = (new StringBuffer(line)).reverse().toString();
+
+ // See definition of StringEntry below
+ //
+ StringEntry key = new StringEntry(line, true);
+ StringEntry data = new StringEntry(reversed, true);
+
+ try {
+ if (table.putNoOverwrite(null, key, data) == OperationStatus.KEYEXIST)
+ System.out.println("Key " + line + " already exists.");
+ } catch (DatabaseException dbe) {
+ System.out.println(dbe.toString());
+ }
+ }
+
+ // Acquire a cursor for the table.
+ Cursor cursor = table.openCursor(null, null);
+ DatabaseEntry foo = new DatabaseEntry();
+
+ MultipleKeyNIODataEntry bulk_data = new MultipleKeyNIODataEntry();
+
+ ByteBuffer rawData = ByteBuffer.allocateDirect(1024*1024);
+ bulk_data.setDataNIO(rawData);
+ bulk_data.setUserBuffer(1024 * 1024, true);
+
+ // Walk through the table, printing the key/data pairs.
+ //
+ while (cursor.getNext(foo, bulk_data, null) == OperationStatus.SUCCESS) {
+ StringEntry key, data;
+ key = new StringEntry();
+ data = new StringEntry();
+
+ while (bulk_data.next(key, data))
+ System.out.println(key.getString() + " : " + data.getString());
+ }
+ cursor.close();
+ table.close();
+ }
+
+ // Here's an example of how you can extend DatabaseEntry in a
+ // straightforward way to allow easy storage/retrieval of strings, or
+ // whatever kind of data you wish. We've declared it as a static inner
+ // class, but it need not be.
+ //
+ static class StringEntry extends DatabaseEntry {
+ StringEntry() {
+ }
+
+ StringEntry(String value, boolean nioData) {
+ setString(value, nioData);
+ }
+
+ void setString(String value, boolean nioData) {
+ byte[] data = value.getBytes();
+ if(nioData) {
+ ByteBuffer newBuf = ByteBuffer.allocateDirect(data.length);
+ newBuf.position(0);
+ newBuf.put(data, 0, data.length);
+ newBuf.position(0);
+ setDataNIO(newBuf);
+ } else {
+ setData(data);
+ }
+ }
+
+ String getString() {
+ String ret;
+ if(getData() == null) {
+ ByteBuffer tmp = getDataNIO();
+ tmp.position(getOffset());
+ byte[] data = new byte[getSize()];
+ tmp.get(data, 0, getSize());
+ ret = new String(data, 0, getSize());
+ } else {
+ ret = new String(getData(), getOffset(), getSize());
+ }
+ return ret;
+ }
+ }
+}
diff --git a/examples_java/src/db/EnvExample.java b/examples_java/src/db/EnvExample.java
new file mode 100644
index 0000000..a8c1f75
--- /dev/null
+++ b/examples_java/src/db/EnvExample.java
@@ -0,0 +1,144 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db;
+
+import com.sleepycat.db.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.OutputStream;
+
+/*
+ * An example of a program configuring a database environment.
+ *
+ * For comparison purposes, this example uses a similar structure
+ * as examples/ex_env.c and examples_cxx/EnvExample.cpp.
+ */
+public class EnvExample {
+ private static final String progname = "EnvExample";
+
+ private static void runApplication(Environment dbenv)
+ throws DatabaseException, FileNotFoundException {
+
+ // Open a database in the environment to verify the data_dir
+ // has been set correctly.
+ DatabaseConfig dbconfig = new DatabaseConfig();
+
+ // The database is DB_BTREE.
+ dbconfig.setAllowCreate(true);
+ dbconfig.setMode(0644);
+ dbconfig.setType(DatabaseType.BTREE);
+ Database db=dbenv.openDatabase(null,
+ "jEnvExample_db1.db", null, dbconfig);
+
+ // Close the database.
+ db.close();
+ }
+
+ private static void setupEnvironment(File home,
+ File dataDir,
+ OutputStream errs)
+ throws DatabaseException, FileNotFoundException {
+
+ // Create an environment object and initialize it for error reporting.
+ EnvironmentConfig config = new EnvironmentConfig();
+ config.setErrorStream(errs);
+ config.setErrorPrefix(progname);
+
+ //
+ // We want to specify the shared memory buffer pool cachesize,
+ // but everything else is the default.
+ //
+ config.setCacheSize(64 * 1024);
+
+ // Databases are in a separate directory.
+ config.addDataDir(dataDir);
+
+ // Open the environment with full transactional support.
+ config.setAllowCreate(true);
+ config.setInitializeCache(true);
+ config.setTransactional(true);
+ config.setInitializeLocking(true);
+
+ //
+ // open is declared to throw a FileNotFoundException, which normally
+ // shouldn't occur when allowCreate is set.
+ //
+ Environment dbenv = new Environment(home, config);
+
+ try {
+ // Start your application.
+ runApplication(dbenv);
+ } finally {
+ // Close the environment. Doing this in the finally block ensures
+ // it is done, even if an error is thrown.
+ dbenv.close();
+ }
+ }
+
+ private static void teardownEnvironment(File home,
+ File dataDir,
+ OutputStream errs)
+ throws DatabaseException, FileNotFoundException {
+
+ // Remove the shared database regions.
+ EnvironmentConfig config = new EnvironmentConfig();
+
+ config.setErrorStream(errs);
+ config.setErrorPrefix(progname);
+ config.addDataDir(dataDir);
+ Environment.remove(home, true, config);
+ }
+
+ private static void usage() {
+ System.err.println("usage: java db.EnvExample [-h home] [-d data_dir]");
+ System.exit(1);
+ }
+
+ public static void main(String[] argv) {
+ //
+ // All of the shared database files live in home,
+ // but data files live in dataDir.
+ //
+ // Using Berkeley DB in C/C++, we need to allocate two elements
+ // in the array and set config[1] to NULL. This is not
+ // necessary in Java.
+ //
+ File home = new File("TESTDIR");
+ File dataDir = new File("data");
+
+ for (int i = 0; i < argv.length; ++i) {
+ if (argv[i].equals("-h")) {
+ if (++i >= argv.length)
+ usage();
+ home = new File(argv[i]);
+ } else if (argv[i].equals("-d")) {
+ if (++i >= argv.length)
+ usage();
+ dataDir = new File(argv[i]);
+ } else if (argv[i].equals("-u")) {
+ usage();
+ }
+ }
+
+ try {
+ System.out.println("Setup env");
+ setupEnvironment(home, dataDir, System.err);
+
+ System.out.println("Teardown env");
+ teardownEnvironment(home, dataDir, System.err);
+ } catch (DatabaseException dbe) {
+ System.err.println(progname + ": environment open: " + dbe.toString());
+ System.exit (1);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println(progname + ": unexpected open environment error " + fnfe);
+ System.exit (1);
+ }
+ }
+
+}
diff --git a/examples_java/src/db/GettingStarted/ExampleDatabaseLoad.java b/examples_java/src/db/GettingStarted/ExampleDatabaseLoad.java
new file mode 100644
index 0000000..8346f6d
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/ExampleDatabaseLoad.java
@@ -0,0 +1,231 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File: ExampleDatabaseLoad.java
+
+package db.GettingStarted;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+import java.util.Vector;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+
+public class ExampleDatabaseLoad {
+
+ private static String myDbsPath = "./";
+ private static File inventoryFile = new File("./inventory.txt");
+ private static File vendorsFile = new File("./vendors.txt");
+
+ // DatabaseEntries used for loading records
+ private static DatabaseEntry theKey = new DatabaseEntry();
+ private static DatabaseEntry theData = new DatabaseEntry();
+
+ // Encapsulates the databases.
+ private static MyDbs myDbs = new MyDbs();
+
+ private static void usage() {
+ System.out.println("ExampleDatabaseLoad [-h <database home>]");
+ System.out.println(" [-i <inventory file>] [-v <vendors file>]");
+ System.exit(-1);
+ }
+
+
+ public static void main(String args[]) {
+ ExampleDatabaseLoad edl = new ExampleDatabaseLoad();
+ try {
+ edl.run(args);
+ } catch (DatabaseException dbe) {
+ System.err.println("ExampleDatabaseLoad: " + dbe.toString());
+ dbe.printStackTrace();
+ } catch (Exception e) {
+ System.out.println("Exception: " + e.toString());
+ e.printStackTrace();
+ } finally {
+ myDbs.close();
+ }
+ System.out.println("All done.");
+ }
+
+
+ private void run(String args[])
+ throws DatabaseException {
+ // Parse the arguments list
+ parseArgs(args);
+
+ myDbs.setup(myDbsPath);
+
+ System.out.println("loading vendors db....");
+ loadVendorsDb();
+
+ System.out.println("loading inventory db....");
+ loadInventoryDb();
+ }
+
+
+ private void loadVendorsDb()
+ throws DatabaseException {
+
+ // loadFile opens a flat-text file that contains our data
+ // and loads it into a list for us to work with. The integer
+ // parameter represents the number of fields expected in the
+ // file.
+ List vendors = loadFile(vendorsFile, 8);
+
+ // Now load the data into the database. The vendor's name is the
+ // key, and the data is a Vendor class object.
+
+ // Need a serial binding for the data
+ EntryBinding dataBinding =
+ new SerialBinding(myDbs.getClassCatalog(), Vendor.class);
+
+ for (int i = 0; i < vendors.size(); i++) {
+ String[] sArray = (String[])vendors.get(i);
+ Vendor theVendor = new Vendor();
+ theVendor.setVendorName(sArray[0]);
+ theVendor.setAddress(sArray[1]);
+ theVendor.setCity(sArray[2]);
+ theVendor.setState(sArray[3]);
+ theVendor.setZipcode(sArray[4]);
+ theVendor.setBusinessPhoneNumber(sArray[5]);
+ theVendor.setRepName(sArray[6]);
+ theVendor.setRepPhoneNumber(sArray[7]);
+
+ // The key is the vendor's name.
+ // ASSUMES THE VENDOR'S NAME IS UNIQUE!
+ String vendorName = theVendor.getVendorName();
+ try {
+ theKey = new DatabaseEntry(vendorName.getBytes("UTF-8"));
+ } catch (IOException willNeverOccur) {}
+
+ // Convert the Vendor object to a DatabaseEntry object
+ // using our SerialBinding
+ dataBinding.objectToEntry(theVendor, theData);
+
+ // Put it in the database.
+ myDbs.getVendorDB().put(null, theKey, theData);
+ }
+ }
+
+
+ private void loadInventoryDb()
+ throws DatabaseException {
+
+ // loadFile opens a flat-text file that contains our data
+ // and loads it into a list for us to work with. The integer
+ // parameter represents the number of fields expected in the
+ // file.
+ List inventoryArray = loadFile(inventoryFile, 6);
+
+ // Now load the data into the database. The item's sku is the
+ // key, and the data is an Inventory class object.
+
+ // Need a tuple binding for the Inventory class.
+ TupleBinding inventoryBinding = new InventoryBinding();
+
+ for (int i = 0; i < inventoryArray.size(); i++) {
+ String[] sArray = (String[])inventoryArray.get(i);
+ String sku = sArray[1];
+ try {
+ theKey = new DatabaseEntry(sku.getBytes("UTF-8"));
+ } catch (IOException willNeverOccur) {}
+
+ Inventory theInventory = new Inventory();
+ theInventory.setItemName(sArray[0]);
+ theInventory.setSku(sArray[1]);
+ theInventory.setVendorPrice((new Float(sArray[2])).floatValue());
+ theInventory.setVendorInventory((new Integer(sArray[3])).intValue());
+ theInventory.setCategory(sArray[4]);
+ theInventory.setVendor(sArray[5]);
+
+ // Place the Vendor object on the DatabaseEntry object using our
+ // the tuple binding we implemented in InventoryBinding.java
+ inventoryBinding.objectToEntry(theInventory, theData);
+
+ // Put it in the database. Note that this causes our secondary database
+ // to be automatically updated for us.
+ myDbs.getInventoryDB().put(null, theKey, theData);
+ }
+ }
+
+
+ private static void parseArgs(String args[]) {
+ for(int i = 0; i < args.length; ++i) {
+ if (args[i].startsWith("-")) {
+ switch(args[i].charAt(1)) {
+ case 'h':
+ myDbsPath = new String(args[++i]);
+ break;
+ case 'i':
+ inventoryFile = new File(args[++i]);
+ break;
+ case 'v':
+ vendorsFile = new File(args[++i]);
+ break;
+ default:
+ usage();
+ }
+ }
+ }
+ }
+
+
+ private List loadFile(File theFile, int numFields) {
+ List records = new ArrayList();
+ try {
+ String theLine = null;
+ FileInputStream fis = new FileInputStream(theFile);
+ BufferedReader br = new BufferedReader(new InputStreamReader(fis));
+ while((theLine=br.readLine()) != null) {
+ String[] theLineArray = splitString(theLine, "#");
+ if (theLineArray.length != numFields) {
+ System.out.println("Malformed line found in " + theFile.getPath());
+ System.out.println("Line was: '" + theLine);
+ System.out.println("length found was: " + theLineArray.length);
+ System.exit(-1);
+ }
+ records.add(theLineArray);
+ }
+ fis.close();
+ } catch (FileNotFoundException e) {
+ System.err.println(theFile.getPath() + " does not exist.");
+ e.printStackTrace();
+ usage();
+ } catch (IOException e) {
+ System.err.println("IO Exception: " + e.toString());
+ e.printStackTrace();
+ System.exit(-1);
+ }
+ return records;
+ }
+
+
+ private static String[] splitString(String s, String delimiter) {
+ Vector resultVector = new Vector();
+ StringTokenizer tokenizer = new StringTokenizer(s, delimiter);
+ while (tokenizer.hasMoreTokens())
+ resultVector.add(tokenizer.nextToken());
+ String[] resultArray = new String[resultVector.size()];
+ resultVector.copyInto(resultArray);
+ return resultArray;
+ }
+
+
+ protected ExampleDatabaseLoad() {}
+}
diff --git a/examples_java/src/db/GettingStarted/ExampleDatabaseRead.java b/examples_java/src/db/GettingStarted/ExampleDatabaseRead.java
new file mode 100644
index 0000000..dbaab60
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/ExampleDatabaseRead.java
@@ -0,0 +1,205 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File: ExampleDatabaseRead
+
+package db.GettingStarted;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.LockMode;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.SecondaryCursor;
+
+import java.io.IOException;
+
+public class ExampleDatabaseRead {
+
+ private static String myDbsPath = "./";
+
+ // Encapsulates the database environment and databases.
+ private static MyDbs myDbs = new MyDbs();
+
+ private static TupleBinding inventoryBinding;
+ private static EntryBinding vendorBinding;
+
+ // The item to locate if the -s switch is used
+ private static String locateItem;
+
+ private static void usage() {
+ System.out.println("ExampleDatabaseRead [-h <env directory>]" +
+ "[-s <item to locate>]");
+ System.exit(-1);
+ }
+
+ public static void main(String args[]) {
+ ExampleDatabaseRead edr = new ExampleDatabaseRead();
+ try {
+ edr.run(args);
+ } catch (DatabaseException dbe) {
+ System.err.println("ExampleDatabaseRead: " + dbe.toString());
+ dbe.printStackTrace();
+ } finally {
+ myDbs.close();
+ }
+ System.out.println("All done.");
+ }
+
+ private void run(String args[])
+ throws DatabaseException {
+ // Parse the arguments list
+ parseArgs(args);
+
+ myDbs.setup(myDbsPath);
+
+ // Setup our bindings.
+ inventoryBinding = new InventoryBinding();
+ vendorBinding =
+ new SerialBinding(myDbs.getClassCatalog(),
+ Vendor.class);
+
+ if (locateItem != null) {
+ showItem();
+ } else {
+ showAllInventory();
+ }
+ }
+
+ private void showItem() throws DatabaseException {
+
+ SecondaryCursor secCursor = null;
+ try {
+ // searchKey is the key that we want to find in the
+ // secondary db.
+ DatabaseEntry searchKey =
+ new DatabaseEntry(locateItem.getBytes("UTF-8"));
+
+ // foundKey and foundData are populated from the primary
+ // entry that is associated with the secondary db key.
+ DatabaseEntry foundKey = new DatabaseEntry();
+ DatabaseEntry foundData = new DatabaseEntry();
+
+ // open a secondary cursor
+ secCursor =
+ myDbs.getNameIndexDB().openSecondaryCursor(null, null);
+
+ // Search for the secondary database entry.
+ OperationStatus retVal =
+ secCursor.getSearchKey(searchKey, foundKey,
+ foundData, LockMode.DEFAULT);
+
+ // Display the entry, if one is found. Repeat until no more
+ // secondary duplicate entries are found
+ while(retVal == OperationStatus.SUCCESS) {
+ Inventory theInventory =
+ (Inventory)inventoryBinding.entryToObject(foundData);
+ displayInventoryRecord(foundKey, theInventory);
+ retVal = secCursor.getNextDup(searchKey, foundKey,
+ foundData, LockMode.DEFAULT);
+ }
+ } catch (Exception e) {
+ System.err.println("Error on inventory secondary cursor:");
+ System.err.println(e.toString());
+ e.printStackTrace();
+ } finally {
+ if (secCursor != null) {
+ secCursor.close();
+ }
+ }
+ }
+
+ private void showAllInventory()
+ throws DatabaseException {
+ // Get a cursor
+ Cursor cursor = myDbs.getInventoryDB().openCursor(null, null);
+
+ // DatabaseEntry objects used for reading records
+ DatabaseEntry foundKey = new DatabaseEntry();
+ DatabaseEntry foundData = new DatabaseEntry();
+
+ try { // always want to make sure the cursor gets closed
+ while (cursor.getNext(foundKey, foundData,
+ LockMode.DEFAULT) == OperationStatus.SUCCESS) {
+ Inventory theInventory =
+ (Inventory)inventoryBinding.entryToObject(foundData);
+ displayInventoryRecord(foundKey, theInventory);
+ }
+ } catch (Exception e) {
+ System.err.println("Error on inventory cursor:");
+ System.err.println(e.toString());
+ e.printStackTrace();
+ } finally {
+ cursor.close();
+ }
+ }
+
+ private void displayInventoryRecord(DatabaseEntry theKey,
+ Inventory theInventory)
+ throws DatabaseException {
+
+ String theSKU = new String(theKey.getData());
+ System.out.println(theSKU + ":");
+ System.out.println("\t " + theInventory.getItemName());
+ System.out.println("\t " + theInventory.getCategory());
+ System.out.println("\t " + theInventory.getVendor());
+ System.out.println("\t\tNumber in stock: " +
+ theInventory.getVendorInventory());
+ System.out.println("\t\tPrice per unit: " +
+ theInventory.getVendorPrice());
+ System.out.println("\t\tContact: ");
+
+ DatabaseEntry searchKey = null;
+ try {
+ searchKey =
+ new DatabaseEntry(theInventory.getVendor().getBytes("UTF-8"));
+ } catch (IOException willNeverOccur) {}
+ DatabaseEntry foundVendor = new DatabaseEntry();
+
+ if (myDbs.getVendorDB().get(null, searchKey, foundVendor,
+ LockMode.DEFAULT) != OperationStatus.SUCCESS) {
+ System.out.println("Could not find vendor: " +
+ theInventory.getVendor() + ".");
+ System.exit(-1);
+ } else {
+ Vendor theVendor =
+ (Vendor)vendorBinding.entryToObject(foundVendor);
+ System.out.println("\t\t " + theVendor.getAddress());
+ System.out.println("\t\t " + theVendor.getCity() + ", " +
+ theVendor.getState() + " " + theVendor.getZipcode());
+ System.out.println("\t\t Business Phone: " +
+ theVendor.getBusinessPhoneNumber());
+ System.out.println("\t\t Sales Rep: " +
+ theVendor.getRepName());
+ System.out.println("\t\t " +
+ theVendor.getRepPhoneNumber());
+ }
+ }
+
+ protected ExampleDatabaseRead() {}
+
+ private static void parseArgs(String args[]) {
+ for(int i = 0; i < args.length; ++i) {
+ if (args[i].startsWith("-")) {
+ switch(args[i].charAt(1)) {
+ case 'h':
+ myDbsPath = new String(args[++i]);
+ break;
+ case 's':
+ locateItem = new String(args[++i]);
+ break;
+ default:
+ usage();
+ }
+ }
+ }
+ }
+}
diff --git a/examples_java/src/db/GettingStarted/Inventory.java b/examples_java/src/db/GettingStarted/Inventory.java
new file mode 100644
index 0000000..d10c5d5
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/Inventory.java
@@ -0,0 +1,70 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File: Inventory.java
+
+package db.GettingStarted;
+
+public class Inventory {
+
+ private String sku;
+ private String itemName;
+ private String category;
+ private String vendor;
+ private int vendorInventory;
+ private float vendorPrice;
+
+ public void setSku(String data) {
+ sku = data;
+ }
+
+ public void setItemName(String data) {
+ itemName = data;
+ }
+
+ public void setCategory(String data) {
+ category = data;
+ }
+
+ public void setVendorInventory(int data) {
+ vendorInventory = data;
+ }
+
+ public void setVendor(String data) {
+ vendor = data;
+ }
+
+ public void setVendorPrice(float data) {
+ vendorPrice = data;
+ }
+
+ public String getSku() {
+ return sku;
+ }
+
+ public String getItemName() {
+ return itemName;
+ }
+
+ public String getCategory() {
+ return category;
+ }
+
+ public int getVendorInventory() {
+ return vendorInventory;
+ }
+
+ public String getVendor() {
+ return vendor;
+ }
+
+ public float getVendorPrice() {
+ return vendorPrice;
+ }
+}
+
diff --git a/examples_java/src/db/GettingStarted/InventoryBinding.java b/examples_java/src/db/GettingStarted/InventoryBinding.java
new file mode 100644
index 0000000..5b1f451
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/InventoryBinding.java
@@ -0,0 +1,54 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File: InventoryBinding.java
+
+package db.GettingStarted;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.bind.tuple.TupleInput;
+import com.sleepycat.bind.tuple.TupleOutput;
+
+public class InventoryBinding extends TupleBinding {
+
+ // Implement this abstract method. Used to convert
+ // a DatabaseEntry to an Inventory object.
+ public Object entryToObject(TupleInput ti) {
+
+ String sku = ti.readString();
+ String itemName = ti.readString();
+ String category = ti.readString();
+ String vendor = ti.readString();
+ int vendorInventory = ti.readInt();
+ float vendorPrice = ti.readFloat();
+
+ Inventory inventory = new Inventory();
+ inventory.setSku(sku);
+ inventory.setItemName(itemName);
+ inventory.setCategory(category);
+ inventory.setVendor(vendor);
+ inventory.setVendorInventory(vendorInventory);
+ inventory.setVendorPrice(vendorPrice);
+
+ return inventory;
+ }
+
+ // Implement this abstract method. Used to convert a
+ // Inventory object to a DatabaseEntry object.
+ public void objectToEntry(Object object, TupleOutput to) {
+
+ Inventory inventory = (Inventory)object;
+
+ to.writeString(inventory.getSku());
+ to.writeString(inventory.getItemName());
+ to.writeString(inventory.getCategory());
+ to.writeString(inventory.getVendor());
+ to.writeInt(inventory.getVendorInventory());
+ to.writeFloat(inventory.getVendorPrice());
+ }
+}
diff --git a/examples_java/src/db/GettingStarted/ItemNameKeyCreator.java b/examples_java/src/db/GettingStarted/ItemNameKeyCreator.java
new file mode 100644
index 0000000..f52834d
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/ItemNameKeyCreator.java
@@ -0,0 +1,45 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File: ItemNameKeyCreator.java
+
+package db.GettingStarted;
+
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.db.SecondaryKeyCreator;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.SecondaryDatabase;
+
+public class ItemNameKeyCreator implements SecondaryKeyCreator {
+
+ private TupleBinding theBinding;
+
+ // Use the constructor to set the tuple binding
+ ItemNameKeyCreator(TupleBinding binding) {
+ theBinding = binding;
+ }
+
+ // Abstract method that we must implement
+ public boolean createSecondaryKey(SecondaryDatabase secDb,
+ DatabaseEntry keyEntry, // From the primary
+ DatabaseEntry dataEntry, // From the primary
+ DatabaseEntry resultEntry) // set the key data on this.
+ throws DatabaseException {
+
+ if (dataEntry != null) {
+ // Convert dataEntry to an Inventory object
+ Inventory inventoryItem =
+ (Inventory)theBinding.entryToObject(dataEntry);
+ // Get the item name and use that as the key
+ String theItem = inventoryItem.getItemName();
+ resultEntry.setData(theItem.getBytes());
+ }
+ return true;
+ }
+}
diff --git a/examples_java/src/db/GettingStarted/MyDbs.java b/examples_java/src/db/GettingStarted/MyDbs.java
new file mode 100644
index 0000000..6c35a06
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/MyDbs.java
@@ -0,0 +1,165 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File: MyDbs.java
+
+package db.GettingStarted;
+
+import java.io.FileNotFoundException;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.tuple.TupleBinding;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryDatabase;
+
+
+public class MyDbs {
+
+ // The databases that our application uses
+ private Database vendorDb = null;
+ private Database inventoryDb = null;
+ private Database classCatalogDb = null;
+ private SecondaryDatabase itemNameIndexDb = null;
+
+ private String vendordb = "VendorDB.db";
+ private String inventorydb = "InventoryDB.db";
+ private String classcatalogdb = "ClassCatalogDB.db";
+ private String itemnameindexdb = "ItemNameIndexDB.db";
+
+ // Needed for object serialization
+ private StoredClassCatalog classCatalog;
+
+ // Our constructor does nothing
+ public MyDbs() {}
+
+ // The setup() method opens all our databases
+ // for us.
+ public void setup(String databasesHome)
+ throws DatabaseException {
+
+ DatabaseConfig myDbConfig = new DatabaseConfig();
+ SecondaryConfig mySecConfig = new SecondaryConfig();
+
+ myDbConfig.setErrorStream(System.err);
+ mySecConfig.setErrorStream(System.err);
+ myDbConfig.setErrorPrefix("MyDbs");
+ mySecConfig.setErrorPrefix("MyDbs");
+ myDbConfig.setType(DatabaseType.BTREE);
+ mySecConfig.setType(DatabaseType.BTREE);
+ myDbConfig.setAllowCreate(true);
+ mySecConfig.setAllowCreate(true);
+
+ // Now open, or create and open, our databases
+ // Open the vendors and inventory databases
+ try {
+ vendordb = databasesHome + "/" + vendordb;
+ vendorDb = new Database(vendordb,
+ null,
+ myDbConfig);
+
+ inventorydb = databasesHome + "/" + inventorydb;
+ inventoryDb = new Database(inventorydb,
+ null,
+ myDbConfig);
+
+ // Open the class catalog db. This is used to
+ // optimize class serialization.
+ classcatalogdb = databasesHome + "/" + classcatalogdb;
+ classCatalogDb = new Database(classcatalogdb,
+ null,
+ myDbConfig);
+ } catch(FileNotFoundException fnfe) {
+ System.err.println("MyDbs: " + fnfe.toString());
+ System.exit(-1);
+ }
+
+ // Create our class catalog
+ classCatalog = new StoredClassCatalog(classCatalogDb);
+
+ // Need a tuple binding for the Inventory class.
+ // We use the InventoryBinding class
+ // that we implemented for this purpose.
+ TupleBinding inventoryBinding = new InventoryBinding();
+
+ // Open the secondary database. We use this to create a
+ // secondary index for the inventory database
+
+ // We want to maintain an index for the inventory entries based
+ // on the item name. So, instantiate the appropriate key creator
+ // and open a secondary database.
+ ItemNameKeyCreator keyCreator =
+ new ItemNameKeyCreator(new InventoryBinding());
+
+
+ // Set up additional secondary properties
+ // Need to allow duplicates for our secondary database
+ mySecConfig.setSortedDuplicates(true);
+ mySecConfig.setAllowPopulate(true); // Allow autopopulate
+ mySecConfig.setKeyCreator(keyCreator);
+
+ // Now open it
+ try {
+ itemnameindexdb = databasesHome + "/" + itemnameindexdb;
+ itemNameIndexDb = new SecondaryDatabase(itemnameindexdb,
+ null,
+ inventoryDb,
+ mySecConfig);
+ } catch(FileNotFoundException fnfe) {
+ System.err.println("MyDbs: " + fnfe.toString());
+ System.exit(-1);
+ }
+ }
+
+ // getter methods
+ public Database getVendorDB() {
+ return vendorDb;
+ }
+
+ public Database getInventoryDB() {
+ return inventoryDb;
+ }
+
+ public SecondaryDatabase getNameIndexDB() {
+ return itemNameIndexDb;
+ }
+
+ public StoredClassCatalog getClassCatalog() {
+ return classCatalog;
+ }
+
+ // Close the databases
+ public void close() {
+ try {
+ if (itemNameIndexDb != null) {
+ itemNameIndexDb.close();
+ }
+
+ if (vendorDb != null) {
+ vendorDb.close();
+ }
+
+ if (inventoryDb != null) {
+ inventoryDb.close();
+ }
+
+ if (classCatalogDb != null) {
+ classCatalogDb.close();
+ }
+
+ } catch(DatabaseException dbe) {
+ System.err.println("Error closing MyDbs: " +
+ dbe.toString());
+ System.exit(-1);
+ }
+ }
+}
+
diff --git a/examples_java/src/db/GettingStarted/Vendor.java b/examples_java/src/db/GettingStarted/Vendor.java
new file mode 100644
index 0000000..4e13ddb
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/Vendor.java
@@ -0,0 +1,90 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File: Vendor.java
+package db.GettingStarted;
+
+import java.io.Serializable;
+
+public class Vendor implements Serializable {
+
+ private String repName;
+ private String address;
+ private String city;
+ private String state;
+ private String zipcode;
+ private String bizPhoneNumber;
+ private String repPhoneNumber;
+ private String vendor;
+
+ public void setRepName(String data) {
+ repName = data;
+ }
+
+ public void setAddress(String data) {
+ address = data;
+ }
+
+ public void setCity(String data) {
+ city = data;
+ }
+
+ public void setState(String data) {
+ state = data;
+ }
+
+ public void setZipcode(String data) {
+ zipcode = data;
+ }
+
+ public void setBusinessPhoneNumber(String data) {
+ bizPhoneNumber = data;
+ }
+
+ public void setRepPhoneNumber(String data) {
+ repPhoneNumber = data;
+ }
+
+ public void setVendorName(String data) {
+ vendor = data;
+ }
+
+ public String getRepName() {
+ return repName;
+ }
+
+ public String getAddress() {
+ return address;
+ }
+
+ public String getCity() {
+ return city;
+ }
+
+ public String getState() {
+ return state;
+ }
+
+ public String getZipcode() {
+ return zipcode;
+ }
+
+ public String getBusinessPhoneNumber() {
+ return bizPhoneNumber;
+ }
+
+ public String getRepPhoneNumber() {
+ return repPhoneNumber;
+ }
+
+ public String getVendorName() {
+ return vendor;
+ }
+
+}
+
diff --git a/examples_java/src/db/GettingStarted/inventory.txt b/examples_java/src/db/GettingStarted/inventory.txt
new file mode 100644
index 0000000..d6b6876
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/inventory.txt
@@ -0,0 +1,800 @@
+Oranges#OranfruiRu6Ghr#0.71#451#fruits#TriCounty Produce
+Oranges#OranfruiXRPFn1#0.73#263#fruits#Simply Fresh
+Oranges#OranfruiLEuzQj#0.69#261#fruits#Off the Vine
+Apples#ApplfruiZls4Du#1.20#472#fruits#TriCounty Produce
+Apples#Applfrui8fewZe#1.21#402#fruits#Simply Fresh
+Apples#ApplfruiXoT6xG#1.20#728#fruits#Off the Vine
+Bananas#BanafruipIlluX#0.50#207#fruits#TriCounty Produce
+Bananas#BanafruiEQhWuj#0.50#518#fruits#Simply Fresh
+Bananas#BanafruimpRgPO#0.50#741#fruits#Off the Vine
+Almonds#AlmofruiPPCLz8#0.55#600#fruits#TriCounty Produce
+Almonds#AlmofruidMyKmp#0.54#745#fruits#Simply Fresh
+Almonds#Almofrui7K0xzH#0.53#405#fruits#Off the Vine
+Allspice#AllsfruibJGK4R#0.94#669#fruits#TriCounty Produce
+Allspice#Allsfruilfvoeg#0.94#244#fruits#Simply Fresh
+Allspice#Allsfruio12BOS#0.95#739#fruits#Off the Vine
+Apricot#AprifruijphEpM#0.89#560#fruits#TriCounty Produce
+Apricot#AprifruiU1zIDn#0.91#980#fruits#Simply Fresh
+Apricot#AprifruichcwYS#0.95#668#fruits#Off the Vine
+Avocado#AvocfruiwYYomu#0.99#379#fruits#TriCounty Produce
+Avocado#AvocfruiT6IwWE#1.02#711#fruits#Simply Fresh
+Avocado#AvocfruisbK1h5#0.97#856#fruits#Off the Vine
+Bael Fruit#BaelfruilAU7Hj#0.41#833#fruits#TriCounty Produce
+Bael Fruit#BaelfruiX2KvqV#0.40#770#fruits#Simply Fresh
+Bael Fruit#Baelfruidjne4e#0.39#778#fruits#Off the Vine
+Betel Nut#BetefruiQYdHqQ#0.34#926#fruits#TriCounty Produce
+Betel Nut#Betefrui32BKAz#0.37#523#fruits#Simply Fresh
+Betel Nut#BetefruisaWzY4#0.34#510#fruits#Off the Vine
+Black Walnut#BlacfruiXxIuMU#0.57#923#fruits#TriCounty Produce
+Black Walnut#BlacfruiZXgY9t#0.59#312#fruits#Simply Fresh
+Black Walnut#BlacfruikWO0vz#0.60#877#fruits#Off the Vine
+Blueberry#BluefruiCbxb4t#1.02#276#fruits#TriCounty Produce
+Blueberry#BluefruiBuCfgO#1.03#522#fruits#Simply Fresh
+Blueberry#Bluefruixz8MkE#1.01#278#fruits#Off the Vine
+Boysenberry#BoysfruizxyMuz#1.05#239#fruits#TriCounty Produce
+Boysenberry#Boysfrui3hTRQu#1.09#628#fruits#Simply Fresh
+Boysenberry#BoysfruinpLvr3#1.02#349#fruits#Off the Vine
+Breadnut#Breafrui0kDPs6#0.31#558#fruits#TriCounty Produce
+Breadnut#Breafrui44s3og#0.32#879#fruits#Simply Fresh
+Breadnut#BreafruiwyLKhJ#0.30#407#fruits#Off the Vine
+Cactus#Cactfruiyo2ddH#0.56#601#fruits#TriCounty Produce
+Cactus#CactfruixTOLv5#0.54#477#fruits#Simply Fresh
+Cactus#Cactfrui4ioUav#0.55#896#fruits#Off the Vine
+California Wild Grape#CalifruiZsWAa6#0.78#693#fruits#TriCounty Produce
+California Wild Grape#Califruid84xyt#0.83#293#fruits#Simply Fresh
+California Wild Grape#CalifruiLSJFoJ#0.81#543#fruits#Off the Vine
+Cashew#CashfruihaOFVP#0.37#221#fruits#TriCounty Produce
+Cashew#Cashfruizzcw1E#0.38#825#fruits#Simply Fresh
+Cashew#CashfruiqtMe2Q#0.38#515#fruits#Off the Vine
+Chico Sapote#ChicfruiY534SX#0.47#216#fruits#TriCounty Produce
+Chico Sapote#ChicfruiSqL3Lc#0.45#476#fruits#Simply Fresh
+Chico Sapote#ChicfruiurzIp4#0.47#200#fruits#Off the Vine
+Chinese Jello#ChinfruiyRg75u#0.64#772#fruits#TriCounty Produce
+Chinese Jello#ChinfruiuIUj0X#0.65#624#fruits#Simply Fresh
+Chinese Jello#ChinfruiwXbRrL#0.67#719#fruits#Off the Vine
+Common Guava#Commfruib6znSI#0.80#483#fruits#TriCounty Produce
+Common Guava#Commfrui6eUivL#0.81#688#fruits#Simply Fresh
+Common Guava#CommfruibWKnz3#0.84#581#fruits#Off the Vine
+Crabapple#CrabfruioY2L63#0.94#582#fruits#TriCounty Produce
+Crabapple#Crabfruijxcxyt#0.94#278#fruits#Simply Fresh
+Crabapple#CrabfruibvWd8K#0.95#213#fruits#Off the Vine
+Cranberry#CranfruiJxmKr5#0.83#923#fruits#TriCounty Produce
+Cranberry#CranfruiPlklAF#0.84#434#fruits#Simply Fresh
+Cranberry#Cranfrui3G5XL9#0.84#880#fruits#Off the Vine
+Damson Plum#DamsfruibMRMwe#0.98#782#fruits#TriCounty Produce
+Damson Plum#DamsfruiV6wFLk#1.03#400#fruits#Simply Fresh
+Damson Plum#DamsfruiLhqFrQ#0.98#489#fruits#Off the Vine
+Date Palm#DatefruigS31GU#1.14#315#fruits#TriCounty Produce
+Date Palm#DatefruipKPaJK#1.09#588#fruits#Simply Fresh
+Date Palm#Datefrui5fTyNS#1.14#539#fruits#Off the Vine
+Dragon's Eye#DragfruirGJ3aI#0.28#315#fruits#TriCounty Produce
+Dragon's Eye#DragfruiBotxqt#0.27#705#fruits#Simply Fresh
+Dragon's Eye#DragfruiPsSnV9#0.29#482#fruits#Off the Vine
+East Indian Wine Palm#EastfruiNXFJuG#0.43#992#fruits#TriCounty Produce
+East Indian Wine Palm#Eastfruiq06fRr#0.40#990#fruits#Simply Fresh
+East Indian Wine Palm#Eastfrui4QUwl2#0.43#351#fruits#Off the Vine
+English Walnut#EnglfruiBMtHtW#1.04#787#fruits#TriCounty Produce
+English Walnut#EnglfruiHmVzxV#1.03#779#fruits#Simply Fresh
+English Walnut#Englfrui18Tc9n#1.06#339#fruits#Off the Vine
+False Mangosteen#FalsfruibkmYqH#0.66#971#fruits#TriCounty Produce
+False Mangosteen#FalsfruipBsbcX#0.68#250#fruits#Simply Fresh
+False Mangosteen#FalsfruiPrFfhe#0.70#386#fruits#Off the Vine
+Fried Egg Tree#FriefruiihHUdc#0.29#649#fruits#TriCounty Produce
+Fried Egg Tree#FriefruimdD1rf#0.28#527#fruits#Simply Fresh
+Fried Egg Tree#FriefruivyAzYq#0.29#332#fruits#Off the Vine
+Genipap#GenifruiDtKusQ#0.62#986#fruits#TriCounty Produce
+Genipap#GenifruiXq32eP#0.61#326#fruits#Simply Fresh
+Genipap#Genifruiphwwyq#0.61#794#fruits#Off the Vine
+Ginger#GingfruiQLbRZI#0.28#841#fruits#TriCounty Produce
+Ginger#GingfruiS8kK4p#0.29#432#fruits#Simply Fresh
+Ginger#GingfruioL3Y4S#0.27#928#fruits#Off the Vine
+Grapefruit#Grapfruih86Zxh#1.07#473#fruits#TriCounty Produce
+Grapefruit#GrapfruiwL1v0N#1.08#878#fruits#Simply Fresh
+Grapefruit#GrapfruihmJzWm#1.02#466#fruits#Off the Vine
+Hackberry#HackfruiQjomN7#0.22#938#fruits#TriCounty Produce
+Hackberry#HackfruiWS0eKp#0.20#780#fruits#Simply Fresh
+Hackberry#Hackfrui0MIv6J#0.21#345#fruits#Off the Vine
+Honey Locust#HonefruiebXGRc#1.08#298#fruits#TriCounty Produce
+Honey Locust#HonefruiPSqILB#1.00#427#fruits#Simply Fresh
+Honey Locust#Honefrui6UXtvW#1.03#422#fruits#Off the Vine
+Japanese Plum#JapafruihTmoYR#0.40#658#fruits#TriCounty Produce
+Japanese Plum#JapafruifGqz0l#0.40#700#fruits#Simply Fresh
+Japanese Plum#JapafruiufWkLx#0.39#790#fruits#Off the Vine
+Jojoba#JojofruisE0wTh#0.97#553#fruits#TriCounty Produce
+Jojoba#JojofruiwiYLp2#1.02#969#fruits#Simply Fresh
+Jojoba#JojofruigMD1ej#0.96#899#fruits#Off the Vine
+Jostaberry#JostfruiglsEGV#0.50#300#fruits#TriCounty Produce
+Jostaberry#JostfruiV3oo1h#0.52#423#fruits#Simply Fresh
+Jostaberry#JostfruiUBerur#0.53#562#fruits#Off the Vine
+Kangaroo Apple#KangfruiEQknz8#0.60#661#fruits#TriCounty Produce
+Kangaroo Apple#KangfruiNabdFq#0.60#377#fruits#Simply Fresh
+Kangaroo Apple#Kangfrui7hky1i#0.60#326#fruits#Off the Vine
+Ken's Red#Ken'fruinPUSIm#0.21#337#fruits#TriCounty Produce
+Ken's Red#Ken'fruiAoZlpl#0.21#902#fruits#Simply Fresh
+Ken's Red#Ken'frui5rmbd4#0.22#972#fruits#Off the Vine
+Ketembilla#Ketefrui3yAKxQ#0.31#303#fruits#TriCounty Produce
+Ketembilla#KetefruiROn6F5#0.34#283#fruits#Simply Fresh
+Ketembilla#Ketefrui16Rsts#0.33#887#fruits#Off the Vine
+King Orange#KingfruisOFzWk#0.74#429#fruits#TriCounty Produce
+King Orange#KingfruiBmzRJT#0.74#500#fruits#Simply Fresh
+King Orange#KingfruiGsrgRX#0.78#994#fruits#Off the Vine
+Kola Nut#KolafruiBbtAuw#0.58#991#fruits#TriCounty Produce
+Kola Nut#KolafruirbnLVS#0.62#733#fruits#Simply Fresh
+Kola Nut#Kolafrui1ItXJx#0.58#273#fruits#Off the Vine
+Kuko#Kukofrui6YH5Ds#0.41#647#fruits#TriCounty Produce
+Kuko#Kukofrui7WZaZK#0.39#241#fruits#Simply Fresh
+Kuko#Kukofruig9MQFT#0.40#204#fruits#Off the Vine
+Kumquat#KumqfruiT6WKQL#0.73#388#fruits#TriCounty Produce
+Kumquat#KumqfruidLiFLU#0.70#393#fruits#Simply Fresh
+Kumquat#KumqfruiL6zhQX#0.71#994#fruits#Off the Vine
+Kwai Muk#KwaifruiQK1zOE#1.10#249#fruits#TriCounty Produce
+Kwai Muk#KwaifruifbCRlT#1.14#657#fruits#Simply Fresh
+Kwai Muk#Kwaifruipe7T2m#1.09#617#fruits#Off the Vine
+Lanzone#LanzfruijsPf1v#0.34#835#fruits#TriCounty Produce
+Lanzone#LanzfruibU3QoL#0.34#404#fruits#Simply Fresh
+Lanzone#LanzfruiYgHwv6#0.34#237#fruits#Off the Vine
+Lemon#Lemofrui4Tgsg2#0.46#843#fruits#TriCounty Produce
+Lemon#LemofruivK6qvj#0.43#207#fruits#Simply Fresh
+Lemon#LemofruiXSXqJ0#0.44#910#fruits#Off the Vine
+Lemon Grass#LemofruiVFgVh5#0.40#575#fruits#TriCounty Produce
+Lemon Grass#LemofruiWIelvi#0.41#386#fruits#Simply Fresh
+Lemon Grass#LemofruiGVAow0#0.39#918#fruits#Off the Vine
+Lilly-pilly#LillfruiEQnW1m#1.21#974#fruits#TriCounty Produce
+Lilly-pilly#LillfruiMqVuR5#1.23#303#fruits#Simply Fresh
+Lilly-pilly#LillfruiVGH9p4#1.17#512#fruits#Off the Vine
+Ling Nut#LingfruiGtOf8X#0.85#540#fruits#TriCounty Produce
+Ling Nut#LingfruiuP0Jf9#0.83#200#fruits#Simply Fresh
+Ling Nut#LingfruiuO5qf5#0.81#319#fruits#Off the Vine
+Lipote#LipofruisxD2Qc#0.85#249#fruits#TriCounty Produce
+Lipote#LipofruiHNdIqL#0.85#579#fruits#Simply Fresh
+Lipote#LipofruiSQ2pKK#0.83#472#fruits#Off the Vine
+Litchee#Litcfrui1R6Ydz#0.99#806#fruits#TriCounty Produce
+Litchee#LitcfruiwtDM79#1.01#219#fruits#Simply Fresh
+Litchee#LitcfruilpPZbC#1.05#419#fruits#Off the Vine
+Longan#LongfruiEI0lWF#1.02#573#fruits#TriCounty Produce
+Longan#LongfruiPQxxSF#1.04#227#fruits#Simply Fresh
+Longan#LongfruisdI812#0.99#993#fruits#Off the Vine
+Love-in-a-mist#LovefruiKYPW70#0.69#388#fruits#TriCounty Produce
+Love-in-a-mist#LovefruiHrgjDa#0.67#478#fruits#Simply Fresh
+Love-in-a-mist#LovefruipSOWVz#0.71#748#fruits#Off the Vine
+Lychee#LychfruiicVLnY#0.38#276#fruits#TriCounty Produce
+Lychee#LychfruiGY6yJr#0.38#602#fruits#Simply Fresh
+Lychee#LychfruiTzDCq2#0.40#572#fruits#Off the Vine
+Mabolo#MabofruiSY8RQS#0.97#263#fruits#TriCounty Produce
+Mabolo#MabofruiOWWk0n#0.98#729#fruits#Simply Fresh
+Mabolo#MabofruixQLOTF#0.98#771#fruits#Off the Vine
+Macadamia Nut#MacafruiZppJPw#1.22#888#fruits#TriCounty Produce
+Macadamia Nut#MacafruiI7XFMV#1.24#484#fruits#Simply Fresh
+Macadamia Nut#Macafrui4x8bxV#1.20#536#fruits#Off the Vine
+Madagascar Plum#MadafruiVj5fDf#1.14#596#fruits#TriCounty Produce
+Madagascar Plum#MadafruivJhAFI#1.15#807#fruits#Simply Fresh
+Madagascar Plum#Madafrui7MTe1x#1.17#355#fruits#Off the Vine
+Magnolia Vine#MagnfruiigN4Y1#1.17#321#fruits#TriCounty Produce
+Magnolia Vine#MagnfruicKtiHd#1.15#353#fruits#Simply Fresh
+Magnolia Vine#MagnfruiLPDSCp#1.23#324#fruits#Off the Vine
+Mamey#Mamefrui5rjLF6#0.36#683#fruits#TriCounty Produce
+Mamey#MamefruiM6ndnR#0.38#404#fruits#Simply Fresh
+Mamey#Mamefruiq9KntD#0.36#527#fruits#Off the Vine
+Mandarin Orange#MandfruiRKpmKL#0.42#352#fruits#TriCounty Produce
+Mandarin Orange#Mandfrui1V0KLG#0.42#548#fruits#Simply Fresh
+Mandarin Orange#Mandfruig2o9Fg#0.41#686#fruits#Off the Vine
+Marany Nut#MarafruiqkrwoJ#1.14#273#fruits#TriCounty Produce
+Marany Nut#MarafruiCGKpke#1.12#482#fruits#Simply Fresh
+Marany Nut#MarafruiB1YE5x#1.09#412#fruits#Off the Vine
+Marula#MarufruiXF4biH#0.22#403#fruits#TriCounty Produce
+Marula#MarufruidZiVKZ#0.23#317#fruits#Simply Fresh
+Marula#MarufruiIS8BEp#0.21#454#fruits#Off the Vine
+Mayhaw#MayhfruiCSrm7k#0.24#220#fruits#TriCounty Produce
+Mayhaw#MayhfruiNRDzWs#0.25#710#fruits#Simply Fresh
+Mayhaw#MayhfruiIUCyEg#0.24#818#fruits#Off the Vine
+Meiwa Kumquat#MeiwfruiYhv3AY#0.21#997#fruits#TriCounty Produce
+Meiwa Kumquat#MeiwfruiyzQFNR#0.22#347#fruits#Simply Fresh
+Meiwa Kumquat#Meiwfruict4OUp#0.21#923#fruits#Off the Vine
+Mexican Barberry#Mexifrui2P2dXi#0.28#914#fruits#TriCounty Produce
+Mexican Barberry#MexifruiywUTMI#0.29#782#fruits#Simply Fresh
+Mexican Barberry#MexifruijPHu5X#0.29#367#fruits#Off the Vine
+Meyer Lemon#Meyefruin9901J#0.38#824#fruits#TriCounty Produce
+Meyer Lemon#MeyefruiNeQpjO#0.37#617#fruits#Simply Fresh
+Meyer Lemon#MeyefruiYEVznZ#0.37#741#fruits#Off the Vine
+Mississippi Honeyberry#Missfruipb5iW3#0.95#595#fruits#TriCounty Produce
+Mississippi Honeyberry#MissfruiINiDbB#0.96#551#fruits#Simply Fresh
+Mississippi Honeyberry#MissfruiNUQ82a#0.93#396#fruits#Off the Vine
+Monkey Pot#MonkfruiXlTW4j#0.90#896#fruits#TriCounty Produce
+Monkey Pot#Monkfrui1p7a4h#0.88#344#fruits#Simply Fresh
+Monkey Pot#Monkfrui4eKggb#0.92#917#fruits#Off the Vine
+Monos Plum#Monofrui0Mv9aV#1.11#842#fruits#TriCounty Produce
+Monos Plum#Monofrui6iTGQY#1.14#570#fruits#Simply Fresh
+Monos Plum#MonofruiNu2uGH#1.13#978#fruits#Off the Vine
+Moosewood#MoosfruiMXEGex#0.86#969#fruits#TriCounty Produce
+Moosewood#Moosfrui8805mB#0.86#963#fruits#Simply Fresh
+Moosewood#MoosfruiOsnDFL#0.88#594#fruits#Off the Vine
+Natal Orange#NatafruitB8Kh2#0.42#332#fruits#TriCounty Produce
+Natal Orange#NatafruiOhqRrd#0.42#982#fruits#Simply Fresh
+Natal Orange#NatafruiRObMf6#0.41#268#fruits#Off the Vine
+Nectarine#NectfruilNfeD8#0.36#601#fruits#TriCounty Produce
+Nectarine#NectfruiQfjt6b#0.35#818#fruits#Simply Fresh
+Nectarine#Nectfrui5U7U96#0.37#930#fruits#Off the Vine
+Neem Tree#NeemfruiCruEMF#0.24#222#fruits#TriCounty Produce
+Neem Tree#NeemfruiGv0pv5#0.24#645#fruits#Simply Fresh
+Neem Tree#NeemfruiUFPVfk#0.25#601#fruits#Off the Vine
+New Zealand Spinach#New fruihDIgec#0.87#428#fruits#TriCounty Produce
+New Zealand Spinach#New fruiaoR9TP#0.87#630#fruits#Simply Fresh
+New Zealand Spinach#New fruiy8LBul#0.94#570#fruits#Off the Vine
+Olosapo#OlosfruiGXvaMm#0.76#388#fruits#TriCounty Produce
+Olosapo#OlosfruiESlpB3#0.76#560#fruits#Simply Fresh
+Olosapo#OlosfruiFNEkER#0.76#962#fruits#Off the Vine
+Oregon Grape#OregfruiWxhzrf#1.14#892#fruits#TriCounty Produce
+Oregon Grape#OregfruiMgjHUn#1.20#959#fruits#Simply Fresh
+Oregon Grape#OregfruiC5UCxX#1.17#419#fruits#Off the Vine
+Otaheite Apple#OtahfruilT0iFj#0.21#579#fruits#TriCounty Produce
+Otaheite Apple#Otahfrui92PyMY#0.22#857#fruits#Simply Fresh
+Otaheite Apple#OtahfruiLGD1EH#0.20#807#fruits#Off the Vine
+Oyster Plant#OystfruimGxOsj#0.77#835#fruits#TriCounty Produce
+Oyster Plant#Oystfrui1kudBX#0.81#989#fruits#Simply Fresh
+Oyster Plant#OystfruiaX3uO2#0.80#505#fruits#Off the Vine
+Panama Berry#PanafruiZG0Vp4#1.19#288#fruits#TriCounty Produce
+Panama Berry#PanafruiobvXPE#1.21#541#fruits#Simply Fresh
+Panama Berry#PanafruipaW8F3#1.16#471#fruits#Off the Vine
+Peach Tomato#PeacfruiQpovYH#1.20#475#fruits#TriCounty Produce
+Peach Tomato#PeacfruixYXLTN#1.18#655#fruits#Simply Fresh
+Peach Tomato#PeacfruiILDYAp#1.23#876#fruits#Off the Vine
+Peanut#Peanfruiy8M7pt#0.69#275#fruits#TriCounty Produce
+Peanut#PeanfruiEimbED#0.65#307#fruits#Simply Fresh
+Peanut#Peanfruic452Vc#0.68#937#fruits#Off the Vine
+Peanut Butter Fruit#PeanfruixEDt9Y#0.27#628#fruits#TriCounty Produce
+Peanut Butter Fruit#PeanfruiST0T0R#0.27#910#fruits#Simply Fresh
+Peanut Butter Fruit#Peanfrui7jeRN2#0.27#938#fruits#Off the Vine
+Pear#PearfruiB5YmSJ#0.20#945#fruits#TriCounty Produce
+Pear#PearfruiA93XZx#0.21#333#fruits#Simply Fresh
+Pear#PearfruioNKiIf#0.21#715#fruits#Off the Vine
+Pecan#PecafruiiTIv1Z#0.26#471#fruits#TriCounty Produce
+Pecan#PecafruiMGkqla#0.26#889#fruits#Simply Fresh
+Pecan#Pecafrui1szYz2#0.25#929#fruits#Off the Vine
+Purple Passion Fruit#Purpfrui4mMGkD#1.04#914#fruits#TriCounty Produce
+Purple Passion Fruit#Purpfrui5XOW3K#1.06#423#fruits#Simply Fresh
+Purple Passion Fruit#PurpfruifDTAgW#1.05#549#fruits#Off the Vine
+Red Mulberry#Red fruiVLOXIW#1.24#270#fruits#TriCounty Produce
+Red Mulberry#Red fruiXNXt4a#1.21#836#fruits#Simply Fresh
+Red Mulberry#Red fruiUseWLG#1.21#795#fruits#Off the Vine
+Red Princess#Red fruigJLR4V#0.23#829#fruits#TriCounty Produce
+Red Princess#Red fruinVKps5#0.23#558#fruits#Simply Fresh
+Red Princess#Red frui0jl9mg#0.24#252#fruits#Off the Vine
+Striped Screw Pine#StrifruiUKzjoU#0.60#226#fruits#TriCounty Produce
+Striped Screw Pine#StrifruivWLDzH#0.64#685#fruits#Simply Fresh
+Striped Screw Pine#StrifruiiF7CGH#0.60#983#fruits#Off the Vine
+Tapioca#Tapifruib4LCqt#0.40#955#fruits#TriCounty Produce
+Tapioca#TapifruiwgQLj9#0.41#889#fruits#Simply Fresh
+Tapioca#TapifruiZ6Igg3#0.41#655#fruits#Off the Vine
+Tavola#Tavofrui0k9XOt#1.16#938#fruits#TriCounty Produce
+Tavola#Tavofrui8DuRxL#1.08#979#fruits#Simply Fresh
+Tavola#TavofruiNZEuJZ#1.16#215#fruits#Off the Vine
+Tea#TeafruiL0357s#1.11#516#fruits#TriCounty Produce
+Tea#TeafruiD5soTf#1.13#970#fruits#Simply Fresh
+Tea#TeafruiOWq4oO#1.19#357#fruits#Off the Vine
+Ugli Fruit#UglifruipKNCpf#0.24#501#fruits#TriCounty Produce
+Ugli Fruit#UglifruifbDrzc#0.24#642#fruits#Simply Fresh
+Ugli Fruit#Uglifruiwx8or4#0.24#280#fruits#Off the Vine
+Vegetable Brain#VegefruieXLBoc#0.73#355#fruits#TriCounty Produce
+Vegetable Brain#Vegefruik5FSdl#0.71#498#fruits#Simply Fresh
+Vegetable Brain#VegefruiKBfzN0#0.72#453#fruits#Off the Vine
+White Walnut#Whitfruit3oVHL#0.30#501#fruits#TriCounty Produce
+White Walnut#WhitfruiHygydw#0.30#913#fruits#Simply Fresh
+White Walnut#WhitfruieNtplo#0.30#401#fruits#Off the Vine
+Wood Apple#WoodfruijVPRqA#0.68#501#fruits#TriCounty Produce
+Wood Apple#Woodfrui4Zk69T#0.68#616#fruits#Simply Fresh
+Wood Apple#WoodfruiuSLHZK#0.70#474#fruits#Off the Vine
+Yellow Horn#Yellfrui5igjjf#1.18#729#fruits#TriCounty Produce
+Yellow Horn#Yellfrui0DiPqa#1.13#517#fruits#Simply Fresh
+Yellow Horn#Yellfrui0ljvqC#1.14#853#fruits#Off the Vine
+Yellow Sapote#YellfruilGmCfq#0.93#204#fruits#TriCounty Produce
+Yellow Sapote#Yellfrui4J2mke#0.88#269#fruits#Simply Fresh
+Yellow Sapote#Yellfrui6PuXaL#0.86#575#fruits#Off the Vine
+Ylang-ylang#Ylanfrui3rmByO#0.76#429#fruits#TriCounty Produce
+Ylang-ylang#YlanfruiA80Nkq#0.76#886#fruits#Simply Fresh
+Ylang-ylang#YlanfruinUEm5d#0.72#747#fruits#Off the Vine
+Zapote Blanco#ZapofruisZ5sMA#0.67#428#fruits#TriCounty Produce
+Zapote Blanco#ZapofruilKxl7N#0.65#924#fruits#Simply Fresh
+Zapote Blanco#ZapofruiAe6Eu1#0.68#255#fruits#Off the Vine
+Zulu Nut#Zulufrui469K4k#0.71#445#fruits#TriCounty Produce
+Zulu Nut#ZulufruiWbz6vU#0.71#653#fruits#Simply Fresh
+Zulu Nut#Zulufrui0LJnWK#0.71#858#fruits#Off the Vine
+Artichoke#ArtivegeIuqmS4#0.71#282#vegetables#The Pantry
+Artichoke#Artivegebljjnf#0.69#66#vegetables#TriCounty Produce
+Artichoke#ArtivegeTa2lcF#0.70#618#vegetables#Off the Vine
+Asparagus#AspavegezC0cDl#0.23#70#vegetables#The Pantry
+Asparagus#AspavegeM1q5Kt#0.24#546#vegetables#TriCounty Produce
+Asparagus#AspavegeXWbCb8#0.24#117#vegetables#Off the Vine
+Basil#Basivegev08fzf#0.31#213#vegetables#The Pantry
+Basil#BasivegeF3Uha7#0.29#651#vegetables#TriCounty Produce
+Basil#BasivegeqR8SHC#0.31#606#vegetables#Off the Vine
+Bean#BeanvegegCFUOp#0.27#794#vegetables#The Pantry
+Bean#BeanvegeqMSEVq#0.27#468#vegetables#TriCounty Produce
+Bean#Beanvege4IGUwX#0.27#463#vegetables#Off the Vine
+Beet#BeetvegedEv4Ic#0.35#120#vegetables#The Pantry
+Beet#Beetvegegi1bz1#0.35#540#vegetables#TriCounty Produce
+Beet#BeetvegemztZcN#0.36#386#vegetables#Off the Vine
+Blackeyed Pea#Blacvege3TPldr#0.86#133#vegetables#The Pantry
+Blackeyed Pea#Blacvege3Zqnep#0.88#67#vegetables#TriCounty Produce
+Blackeyed Pea#Blacvege3khffZ#0.90#790#vegetables#Off the Vine
+Cabbage#CabbvegeY0c4Fw#0.82#726#vegetables#The Pantry
+Cabbage#CabbvegeoaK7Co#0.85#439#vegetables#TriCounty Produce
+Cabbage#CabbvegeVvO646#0.82#490#vegetables#Off the Vine
+Carrot#CarrvegeEbI0sw#0.45#717#vegetables#The Pantry
+Carrot#CarrvegeEZndWL#0.49#284#vegetables#TriCounty Produce
+Carrot#CarrvegewUkHao#0.47#122#vegetables#Off the Vine
+Cauliflower#Caulvege1CPeNG#0.68#756#vegetables#The Pantry
+Cauliflower#CaulvegedrPqib#0.66#269#vegetables#TriCounty Produce
+Cauliflower#CaulvegeT6cka8#0.65#728#vegetables#Off the Vine
+Chayote#ChayvegePRReGE#0.14#233#vegetables#The Pantry
+Chayote#Chayvegep058f7#0.14#88#vegetables#TriCounty Produce
+Chayote#ChayvegeoxO40S#0.14#611#vegetables#Off the Vine
+Corn#CornvegeukXkv6#0.72#632#vegetables#The Pantry
+Corn#CornvegePnPREC#0.72#609#vegetables#TriCounty Produce
+Corn#CornvegeO0GwoQ#0.70#664#vegetables#Off the Vine
+Cucumber#CucuvegeEqQeA7#0.94#499#vegetables#The Pantry
+Cucumber#CucuvegewmKbJ1#0.94#738#vegetables#TriCounty Produce
+Cucumber#CucuvegeUW6JaA#0.94#565#vegetables#Off the Vine
+Cantaloupe#CantvegeIHs9vJ#0.66#411#vegetables#The Pantry
+Cantaloupe#CantvegeEaDdST#0.66#638#vegetables#TriCounty Produce
+Cantaloupe#CantvegewWQEa0#0.64#682#vegetables#Off the Vine
+Carraway#CarrvegewuL4Ma#0.32#740#vegetables#The Pantry
+Carraway#CarrvegeyiWfBj#0.32#265#vegetables#TriCounty Produce
+Carraway#CarrvegeMjb1i9#0.31#732#vegetables#Off the Vine
+Celeriac#CelevegeoTBicd#0.74#350#vegetables#The Pantry
+Celeriac#CelevegeCNABoZ#0.70#261#vegetables#TriCounty Produce
+Celeriac#Celevege9LUeww#0.70#298#vegetables#Off the Vine
+Celery#Celevegej40ZCc#0.59#740#vegetables#The Pantry
+Celery#CelevegerYlVRy#0.58#734#vegetables#TriCounty Produce
+Celery#Celevege67eimC#0.58#619#vegetables#Off the Vine
+Chervil#ChervegeuH4Dge#0.09#502#vegetables#The Pantry
+Chervil#Chervegea1OyKO#0.09#299#vegetables#TriCounty Produce
+Chervil#Chervegeq56gMO#0.09#474#vegetables#Off the Vine
+Chicory#Chicvege79qoQ8#0.09#709#vegetables#The Pantry
+Chicory#ChicvegeTSVBQq#0.10#477#vegetables#TriCounty Produce
+Chicory#Chicvege6qpcyi#0.10#282#vegetables#Off the Vine
+Chinese Cabbage#ChinvegeFNsSRn#0.78#408#vegetables#The Pantry
+Chinese Cabbage#Chinvege2ldNr3#0.80#799#vegetables#TriCounty Produce
+Chinese Cabbage#ChinvegeK3R2Td#0.80#180#vegetables#Off the Vine
+Chinese Beans#ChinvegebxbyPy#0.45#654#vegetables#The Pantry
+Chinese Beans#ChinvegewKGwgx#0.45#206#vegetables#TriCounty Produce
+Chinese Beans#ChinvegevVjzC0#0.47#643#vegetables#Off the Vine
+Chines Kale#ChinvegeCfdkss#0.70#239#vegetables#The Pantry
+Chines Kale#Chinvege6V6Dne#0.65#548#vegetables#TriCounty Produce
+Chines Kale#ChinvegeB7vE3x#0.66#380#vegetables#Off the Vine
+Chinese Radish#ChinvegeXcM4eq#0.22#190#vegetables#The Pantry
+Chinese Radish#ChinvegeTdUBqN#0.22#257#vegetables#TriCounty Produce
+Chinese Radish#ChinvegeMXMms8#0.22#402#vegetables#Off the Vine
+Chinese Mustard#ChinvegeRDdpdl#0.33#149#vegetables#The Pantry
+Chinese Mustard#ChinvegeABDhNd#0.32#320#vegetables#TriCounty Produce
+Chinese Mustard#Chinvege8NPwa2#0.34#389#vegetables#Off the Vine
+Cilantro#CilavegeQXBEsW#0.60#674#vegetables#The Pantry
+Cilantro#CilavegeRgjkUG#0.60#355#vegetables#TriCounty Produce
+Cilantro#CilavegelT2msu#0.59#464#vegetables#Off the Vine
+Collard#CollvegesTGGNw#0.32#745#vegetables#The Pantry
+Collard#CollvegeAwdor5#0.32#124#vegetables#TriCounty Produce
+Collard#CollvegeQe900L#0.30#796#vegetables#Off the Vine
+Coriander#CorivegeXxp4xY#0.26#560#vegetables#The Pantry
+Coriander#Corivege9xBAT0#0.27#321#vegetables#TriCounty Produce
+Coriander#CorivegeCfNjBx#0.27#709#vegetables#Off the Vine
+Dandelion#DandvegeJNcnbr#0.11#285#vegetables#The Pantry
+Dandelion#DandvegeGwBkHZ#0.11#733#vegetables#TriCounty Produce
+Dandelion#DandvegeZfwVqn#0.11#57#vegetables#Off the Vine
+Daikon Radish#DaikvegeHHsd7M#0.61#743#vegetables#The Pantry
+Daikon Radish#DaikvegeIu17yC#0.62#459#vegetables#TriCounty Produce
+Daikon Radish#DaikvegePzFjqf#0.63#296#vegetables#Off the Vine
+Eggplant#EggpvegeKJtydN#0.55#200#vegetables#The Pantry
+Eggplant#EggpvegeQMKrNs#0.53#208#vegetables#TriCounty Produce
+Eggplant#EggpvegeN0WnSo#0.51#761#vegetables#Off the Vine
+English Pea#Englvegea1ytIn#0.40#457#vegetables#The Pantry
+English Pea#EnglvegerU9Vty#0.37#263#vegetables#TriCounty Produce
+English Pea#EnglvegeCmkd3y#0.39#430#vegetables#Off the Vine
+Fennel#Fennvegebz2UM7#0.76#545#vegetables#The Pantry
+Fennel#FennvegeQzjtZ3#0.78#795#vegetables#TriCounty Produce
+Fennel#FennvegeXSrW61#0.75#79#vegetables#Off the Vine
+Garlic#GarlvegesR2yel#0.76#478#vegetables#The Pantry
+Garlic#GarlvegeEQvt8W#0.77#349#vegetables#TriCounty Produce
+Garlic#GarlvegedljBdK#0.80#708#vegetables#Off the Vine
+Ginger#GingvegeMNiTc2#0.88#563#vegetables#The Pantry
+Ginger#Gingvegeq366Sn#0.89#738#vegetables#TriCounty Produce
+Ginger#GingvegeznyyVj#0.89#598#vegetables#Off the Vine
+Horseradish#HorsvegemSwISt#0.12#622#vegetables#The Pantry
+Horseradish#HorsvegetCOS0x#0.11#279#vegetables#TriCounty Produce
+Horseradish#Horsvegew6XXaS#0.12#478#vegetables#Off the Vine
+Japanese Eggplant#JapavegeTdKDCL#0.57#539#vegetables#The Pantry
+Japanese Eggplant#JapavegevsJfGa#0.58#782#vegetables#TriCounty Produce
+Japanese Eggplant#JapavegeCIrIxd#0.57#777#vegetables#Off the Vine
+Jerusalem Artichoke#Jeruvege928cr0#0.13#231#vegetables#The Pantry
+Jerusalem Artichoke#JeruvegeC2v086#0.14#123#vegetables#TriCounty Produce
+Jerusalem Artichoke#JeruvegeehCYzi#0.14#196#vegetables#Off the Vine
+Jicama#JicavegeRWYj9n#0.75#79#vegetables#The Pantry
+Jicama#JicavegeGk5LKH#0.71#292#vegetables#TriCounty Produce
+Jicama#JicavegeUjpaX1#0.70#308#vegetables#Off the Vine
+Kale#Kalevegext6RNT#0.55#765#vegetables#The Pantry
+Kale#KalevegeFsp17B#0.53#107#vegetables#TriCounty Produce
+Kale#KalevegeAffBTS#0.57#573#vegetables#Off the Vine
+Kiwifruit#KiwivegeloZBKJ#0.60#769#vegetables#The Pantry
+Kiwifruit#KiwivegenCQAHw#0.59#307#vegetables#TriCounty Produce
+Kiwifruit#Kiwivege0Gi3P2#0.59#235#vegetables#Off the Vine
+Kohlrabi#KohlvegeJFKZDl#0.26#406#vegetables#The Pantry
+Kohlrabi#Kohlvege32UTAj#0.28#613#vegetables#TriCounty Produce
+Kohlrabi#KohlvegejNQC1M#0.28#326#vegetables#Off the Vine
+Leek#Leekvege5iaFtg#0.70#580#vegetables#The Pantry
+Leek#Leekvegei9Wxbz#0.68#188#vegetables#TriCounty Produce
+Leek#LeekvegewY4mAc#0.70#473#vegetables#Off the Vine
+Lettuce#LettvegesK9wDR#0.55#716#vegetables#The Pantry
+Lettuce#LettvegeWzMyCM#0.57#83#vegetables#TriCounty Produce
+Lettuce#LettvegeHgfGG8#0.56#268#vegetables#Off the Vine
+Melons#Melovege6t93WF#0.11#252#vegetables#The Pantry
+Melons#Melovegeq9kz7T#0.12#558#vegetables#TriCounty Produce
+Melons#Melovege9kLTXN#0.12#382#vegetables#Off the Vine
+Mushroom#MushvegeSq53h8#0.59#365#vegetables#The Pantry
+Mushroom#Mushvegedq6lYP#0.59#444#vegetables#TriCounty Produce
+Mushroom#Mushvege8o27D2#0.55#467#vegetables#Off the Vine
+Okra#OkravegeTszQSL#0.55#62#vegetables#The Pantry
+Okra#OkravegeJBWmfh#0.58#165#vegetables#TriCounty Produce
+Okra#OkravegeD6tF9n#0.55#77#vegetables#Off the Vine
+Onion#OniovegejwimQo#0.80#186#vegetables#The Pantry
+Onion#OniovegeUOwwks#0.80#417#vegetables#TriCounty Produce
+Onion#OniovegezcRDrc#0.80#435#vegetables#Off the Vine
+Oregano#OregvegetlU7Ez#0.71#119#vegetables#The Pantry
+Oregano#Oregvege9h9ZKy#0.70#173#vegetables#TriCounty Produce
+Oregano#OregvegebXr0PJ#0.70#773#vegetables#Off the Vine
+Parsley#ParsvegeXFEjjN#0.83#502#vegetables#The Pantry
+Parsley#ParsvegejAg5C4#0.80#454#vegetables#TriCounty Produce
+Parsley#ParsvegehAtH2H#0.84#523#vegetables#Off the Vine
+Parsnip#Parsvegee9Lp6D#0.46#626#vegetables#The Pantry
+Parsnip#ParsvegeSxXHSA#0.47#411#vegetables#TriCounty Produce
+Parsnip#Parsvegea0stPf#0.44#403#vegetables#Off the Vine
+Pea#Peavegecq4SxR#0.18#342#vegetables#The Pantry
+Pea#Peavege46Gdp9#0.18#255#vegetables#TriCounty Produce
+Pea#Peavegeov1gc5#0.18#251#vegetables#Off the Vine
+Pepper#PeppvegeUcBYRp#0.33#52#vegetables#The Pantry
+Pepper#PeppvegeB60btP#0.35#107#vegetables#TriCounty Produce
+Pepper#PeppvegeG4tP3e#0.34#481#vegetables#Off the Vine
+Pigeon Pea#Pigevegec5bAtm#0.94#391#vegetables#The Pantry
+Pigeon Pea#Pigevegeb93eLi#0.91#447#vegetables#TriCounty Produce
+Pigeon Pea#PigevegejEBDRa#0.89#259#vegetables#Off the Vine
+Irish Potato#IrisvegeJNQqby#0.72#355#vegetables#The Pantry
+Irish Potato#Irisvegewq1PLd#0.72#601#vegetables#TriCounty Produce
+Irish Potato#IrisvegeAfFLdO#0.68#740#vegetables#Off the Vine
+Pumpkin#PumpvegeiYsPR8#0.25#776#vegetables#The Pantry
+Pumpkin#PumpvegelqP1Kh#0.25#189#vegetables#TriCounty Produce
+Pumpkin#Pumpvegeb3nQU5#0.26#207#vegetables#Off the Vine
+Radish#RadivegeNwwSBJ#0.16#613#vegetables#The Pantry
+Radish#Radivege0tIBnL#0.16#779#vegetables#TriCounty Produce
+Radish#RadivegeNLqJCf#0.16#731#vegetables#Off the Vine
+Rhubarb#RhubvegeREfOti#0.12#301#vegetables#The Pantry
+Rhubarb#Rhubvege4Jc3b7#0.12#557#vegetables#TriCounty Produce
+Rhubarb#RhubvegeaXqF7H#0.12#378#vegetables#Off the Vine
+Rosemary#Rosevege16QStc#0.73#380#vegetables#The Pantry
+Rosemary#RosevegeNf6Oem#0.75#622#vegetables#TriCounty Produce
+Rosemary#RosevegeFgsOyN#0.74#631#vegetables#Off the Vine
+Rutabaga#RutavegecUYfQ3#0.55#676#vegetables#The Pantry
+Rutabaga#RutavegejOG5DF#0.55#273#vegetables#TriCounty Produce
+Rutabaga#RutavegewEVjzV#0.53#452#vegetables#Off the Vine
+Salsify#SalsvegeViS9HF#0.11#537#vegetables#The Pantry
+Salsify#Salsvegemd3HAL#0.11#753#vegetables#TriCounty Produce
+Salsify#SalsvegeuRCnmq#0.10#787#vegetables#Off the Vine
+Savory#Savovegee4DRWl#0.21#456#vegetables#The Pantry
+Savory#SavovegerZ90Xm#0.21#642#vegetables#TriCounty Produce
+Savory#Savovegeje7yy7#0.22#328#vegetables#Off the Vine
+Sesame#Sesavege4NAWZE#0.84#54#vegetables#The Pantry
+Sesame#SesavegeMTc9IN#0.84#458#vegetables#TriCounty Produce
+Sesame#SesavegegOwAjo#0.83#125#vegetables#Off the Vine
+Shallots#ShalvegeUO2pDO#0.26#599#vegetables#The Pantry
+Shallots#ShalvegeY1sekb#0.27#647#vegetables#TriCounty Produce
+Shallots#ShalvegeSDC8VY#0.27#369#vegetables#Off the Vine
+Sugar Snap Peas#SugavegepUZDTl#0.47#308#vegetables#The Pantry
+Sugar Snap Peas#Sugavege1XyzNH#0.48#205#vegetables#TriCounty Produce
+Sugar Snap Peas#SugavegeJuaG7f#0.46#348#vegetables#Off the Vine
+Soybean#SoybvegeqxSVRL#0.70#639#vegetables#The Pantry
+Soybean#SoybvegezEMjOG#0.68#423#vegetables#TriCounty Produce
+Soybean#SoybvegebanSFq#0.67#268#vegetables#Off the Vine
+Spaghetti Squash#SpagvegeMNO1yC#0.12#753#vegetables#The Pantry
+Spaghetti Squash#SpagvegeilpUaD#0.13#604#vegetables#TriCounty Produce
+Spaghetti Squash#SpagvegeAOoZNX#0.13#431#vegetables#Off the Vine
+Spinach#SpinvegeegXXou#0.10#742#vegetables#The Pantry
+Spinach#SpinvegeVcqXL6#0.11#708#vegetables#TriCounty Produce
+Spinach#SpinvegetZ26DN#0.11#625#vegetables#Off the Vine
+Sweet Potato#SweevegepNDQWb#0.94#720#vegetables#The Pantry
+Sweet Potato#Sweevegepnw7Tm#0.90#377#vegetables#TriCounty Produce
+Sweet Potato#Sweevegeyk0C82#0.89#242#vegetables#Off the Vine
+Swiss Chard#SwisvegeksalTA#0.54#545#vegetables#The Pantry
+Swiss Chard#SwisvegeKm2Kze#0.54#472#vegetables#TriCounty Produce
+Swiss Chard#SwisvegehteuMk#0.56#142#vegetables#Off the Vine
+Taro#Tarovege3fpGV6#0.87#155#vegetables#The Pantry
+Taro#TarovegerZkmof#0.86#371#vegetables#TriCounty Produce
+Taro#TarovegeXKPuzc#0.89#443#vegetables#Off the Vine
+Tarragon#TarrvegeCzVC6U#0.18#491#vegetables#The Pantry
+Tarragon#TarrvegesIkEfS#0.17#65#vegetables#TriCounty Produce
+Tarragon#TarrvegerZsKFP#0.18#180#vegetables#Off the Vine
+Thyme#Thymvege8Rv72c#0.41#442#vegetables#The Pantry
+Thyme#ThymvegeJoUdQS#0.42#237#vegetables#TriCounty Produce
+Thyme#ThymvegeRck5uO#0.43#491#vegetables#Off the Vine
+Tomato#Tomavegey0NHGK#0.31#60#vegetables#The Pantry
+Tomato#TomavegeKAjRUn#0.30#630#vegetables#TriCounty Produce
+Tomato#TomavegePZOHlH#0.30#70#vegetables#Off the Vine
+Turnip#TurnvegeRVQiV5#0.44#580#vegetables#The Pantry
+Turnip#TurnvegeVjIX9D#0.45#743#vegetables#TriCounty Produce
+Turnip#TurnvegelFhvuJ#0.44#219#vegetables#Off the Vine
+Watercress#WatevegelwzPLQ#0.54#230#vegetables#The Pantry
+Watercress#Watevege8oeDCT#0.54#774#vegetables#TriCounty Produce
+Watercress#Watevegexr8L1t#0.55#185#vegetables#Off the Vine
+Watermelon#WatevegeL83MRH#0.19#698#vegetables#The Pantry
+Watermelon#WatevegeR2S4Dq#0.21#488#vegetables#TriCounty Produce
+Watermelon#WatevegepFPXQu#0.21#439#vegetables#Off the Vine
+Kamote#KamovegegdON75#0.13#218#vegetables#The Pantry
+Kamote#KamovegevupDBf#0.13#98#vegetables#TriCounty Produce
+Kamote#KamovegeSQX7IA#0.14#703#vegetables#Off the Vine
+Alogbati#AlogvegeB1WaJU#0.41#775#vegetables#The Pantry
+Alogbati#AlogvegeVr5cPP#0.40#789#vegetables#TriCounty Produce
+Alogbati#AlogvegeyTUQzy#0.40#416#vegetables#Off the Vine
+Ampalaya#AmpavegemR9fSd#0.85#107#vegetables#The Pantry
+Ampalaya#AmpavegeJDu9Im#0.90#676#vegetables#TriCounty Produce
+Ampalaya#AmpavegepL8GH5#0.86#728#vegetables#Off the Vine
+Dahon ng sili#Dahovege6X9grk#0.11#369#vegetables#The Pantry
+Dahon ng sili#DahovegeiHZjQT#0.11#141#vegetables#TriCounty Produce
+Dahon ng sili#DahovegeoCDAH8#0.12#517#vegetables#Off the Vine
+Gabi#GabivegeVm4Xk3#0.44#396#vegetables#The Pantry
+Gabi#Gabivegeu6woqK#0.42#722#vegetables#TriCounty Produce
+Gabi#GabivegezcA7q1#0.42#394#vegetables#Off the Vine
+Kabute#Kabuvege6Tqrif#0.16#123#vegetables#The Pantry
+Kabute#KabuvegeA3uYdG#0.15#183#vegetables#TriCounty Produce
+Kabute#KabuvegeXW6ZiI#0.16#624#vegetables#Off the Vine
+Kamoteng Kahoy#KamovegeAdW37X#0.42#782#vegetables#The Pantry
+Kamoteng Kahoy#KamovegetFlqpC#0.42#515#vegetables#TriCounty Produce
+Kamoteng Kahoy#KamovegeMvxoLn#0.40#166#vegetables#Off the Vine
+Kangkong#KangvegeSFTvEz#0.35#759#vegetables#The Pantry
+Kangkong#KangvegeRLR6gL#0.34#695#vegetables#TriCounty Produce
+Kangkong#Kangvege9BFo14#0.35#783#vegetables#Off the Vine
+Labanos#Labavege3qrWJL#0.94#514#vegetables#The Pantry
+Labanos#LabavegekgVWDH#0.89#210#vegetables#TriCounty Produce
+Labanos#LabavegeiVPgMx#0.89#207#vegetables#Off the Vine
+Labong#LabovegeX3O8yz#0.85#722#vegetables#The Pantry
+Labong#LabovegeI1wSEs#0.87#472#vegetables#TriCounty Produce
+Labong#LabovegeOPiQht#0.85#740#vegetables#Off the Vine
+Malunggay#MaluvegeHkwAFm#0.30#252#vegetables#The Pantry
+Malunggay#Maluvegez6TiSY#0.30#245#vegetables#TriCounty Produce
+Malunggay#MaluvegewzY37D#0.31#405#vegetables#Off the Vine
+Munggo#MungvegeqeuwGw#0.25#362#vegetables#The Pantry
+Munggo#MungvegeNhqWvL#0.26#360#vegetables#TriCounty Produce
+Munggo#MungvegeGxNxQC#0.25#555#vegetables#Off the Vine
+Pechay#PechvegezDeHFZ#0.36#401#vegetables#The Pantry
+Pechay#Pechvegehi4Fcx#0.35#723#vegetables#TriCounty Produce
+Pechay#Pechvege8Pq8Eo#0.36#141#vegetables#Off the Vine
+Sigarilyas#SigavegeMJrtlV#0.88#335#vegetables#The Pantry
+Sigarilyas#SigavegeLhsoOB#0.87#768#vegetables#TriCounty Produce
+Sigarilyas#SigavegeS6RJcA#0.93#356#vegetables#Off the Vine
+Sitaw#Sitavege0hMi9z#0.65#153#vegetables#The Pantry
+Sitaw#Sitavegeez1g6N#0.67#561#vegetables#TriCounty Produce
+Sitaw#Sitavege0BCNeF#0.66#674#vegetables#Off the Vine
+Talong#TalovegevZjVK6#0.10#530#vegetables#The Pantry
+Talong#TalovegexX4MRw#0.09#305#vegetables#TriCounty Produce
+Talong#TalovegeO3U2ze#0.10#126#vegetables#Off the Vine
+Toge#TogevegeYelJUw#0.54#449#vegetables#The Pantry
+Toge#Togevegeilr1xK#0.54#274#vegetables#TriCounty Produce
+Toge#Togevegesvjnyn#0.51#316#vegetables#Off the Vine
+Ube#UbevegeoPnxvb#0.56#397#vegetables#The Pantry
+Ube#Ubevege2CNyve#0.55#450#vegetables#TriCounty Produce
+Ube#UbevegeC43sVj#0.55#263#vegetables#Off the Vine
+Upo#UpovegecOGRqC#0.22#404#vegetables#The Pantry
+Upo#Upovegekjl2wl#0.22#541#vegetables#TriCounty Produce
+Upo#UpovegemTTTwI#0.23#459#vegetables#Off the Vine
+Edamame#EdamvegeVYtk8z#0.79#296#vegetables#The Pantry
+Edamame#Edamvege608vXi#0.78#700#vegetables#TriCounty Produce
+Edamame#Edamvege1jiqGY#0.75#115#vegetables#Off the Vine
+Hairy melon#HairvegeFYFHIw#0.71#789#vegetables#The Pantry
+Hairy melon#HairvegeS7AAqI#0.72#302#vegetables#TriCounty Produce
+Hairy melon#HairvegeO6WJHL#0.72#444#vegetables#Off the Vine
+Burdock#BurdvegeyLstLV#0.56#761#vegetables#The Pantry
+Burdock#BurdvegeZsqAjT#0.56#582#vegetables#TriCounty Produce
+Burdock#BurdvegeycF7mo#0.55#566#vegetables#Off the Vine
+Snake gourd#SnakvegesfHGvt#0.92#626#vegetables#The Pantry
+Snake gourd#SnakvegedlNiBk#0.92#669#vegetables#TriCounty Produce
+Snake gourd#Snakvegec5n1UM#0.92#143#vegetables#Off the Vine
+Wasabi#Wasavege5P5pZp#0.67#751#vegetables#The Pantry
+Wasabi#Wasavege6EEE9r#0.68#559#vegetables#TriCounty Produce
+Wasabi#Wasavege1ve7TY#0.65#61#vegetables#Off the Vine
+Yam#YamvegeRN9ONH#0.57#438#vegetables#The Pantry
+Yam#YamvegeWjdzeA#0.56#564#vegetables#TriCounty Produce
+Yam#YamvegeI1AnyI#0.56#456#vegetables#Off the Vine
+Apple Fritters#AppldessDj96hw#6.12#16#desserts#Mom's Kitchen
+Apple Fritters#AppldessrN1kvM#6.06#7#desserts#The Baking Pan
+Banana Split#Banadess7tpjkJ#10.86#10#desserts#Mom's Kitchen
+Banana Split#Banadessfif758#11.07#14#desserts#The Baking Pan
+Blueberry Boy Bait#BluedesseX2LVU#3.72#16#desserts#Mom's Kitchen
+Blueberry Boy Bait#Bluedess9zLhaH#3.93#9#desserts#The Baking Pan
+Candied Cranberries#CanddessjW92p3#1.77#9#desserts#Mom's Kitchen
+Candied Cranberries#CanddesskhtVoQ#1.72#0#desserts#The Baking Pan
+Daiquiri Souffle#DaiqdessebnYcy#9.54#15#desserts#Mom's Kitchen
+Daiquiri Souffle#DaiqdessfM1DnX#9.72#6#desserts#The Baking Pan
+Bananas Flambe#BanadesscczumD#6.94#12#desserts#Mom's Kitchen
+Bananas Flambe#Banadess8qNfxd#7.07#16#desserts#The Baking Pan
+Pie, Apple#Pie,desshcSHhT#7.88#11#desserts#Mom's Kitchen
+Pie, Apple#Pie,dessTbiwDp#7.88#15#desserts#The Baking Pan
+Pie, Pumpkin#Pie,desswhPBPB#6.00#20#desserts#Mom's Kitchen
+Pie, Pumpkin#Pie,dessDg3NWl#6.24#19#desserts#The Baking Pan
+Pie, Blueberry#Pie,dessw9VdgD#2.14#3#desserts#Mom's Kitchen
+Pie, Blueberry#Pie,dessiSjZKD#2.12#1#desserts#The Baking Pan
+Pie, Pecan#Pie,dess2NqhNR#12.70#20#desserts#Mom's Kitchen
+Pie, Pecan#Pie,dessB1LfcE#12.33#12#desserts#The Baking Pan
+Pie, Cranberry Apple#Pie,dess1mL7IS#10.16#7#desserts#Mom's Kitchen
+Pie, Cranberry Apple#Pie,dessmDhkUA#10.16#11#desserts#The Baking Pan
+Pie, Banana Cream#Pie,dessH80DuG#7.35#6#desserts#Mom's Kitchen
+Pie, Banana Cream#Pie,dessf1YvFb#7.08#11#desserts#The Baking Pan
+Pie, Key Lime#Pie,desshtli5N#4.85#2#desserts#Mom's Kitchen
+Pie, Key Lime#Pie,dessMwQkKm#5.13#1#desserts#The Baking Pan
+Pie, Lemon Meringue#Pie,dess9naVkX#3.74#7#desserts#Mom's Kitchen
+Pie, Lemon Meringue#Pie,dessKYcNML#3.67#5#desserts#The Baking Pan
+Pie, Caramel#Pie,dessSUuiIU#2.27#9#desserts#Mom's Kitchen
+Pie, Caramel#Pie,dessvo8uHh#2.33#4#desserts#The Baking Pan
+Pie, Raspberry#Pie,dessUHhMlS#2.36#0#desserts#Mom's Kitchen
+Pie, Raspberry#Pie,dessJflbf5#2.36#2#desserts#The Baking Pan
+Ice Cream, Chocolate#Ice desseXuyxx#1.44#9#desserts#Mom's Kitchen
+Ice Cream, Chocolate#Ice dessASBohf#1.41#13#desserts#The Baking Pan
+Ice Cream, Vanilla#Ice dessYnzbbt#11.92#19#desserts#Mom's Kitchen
+Ice Cream, Vanilla#Ice dessUBBKp8#11.58#10#desserts#The Baking Pan
+Ice Cream, Strawberry#Ice dessfTwKhD#1.90#14#desserts#Mom's Kitchen
+Ice Cream, Strawberry#Ice dessaO9Fxf#1.99#6#desserts#The Baking Pan
+Ice Cream, Rocky Road#Ice dessyIri3P#13.10#20#desserts#Mom's Kitchen
+Ice Cream, Rocky Road#Ice dessZuLr8F#13.48#13#desserts#The Baking Pan
+Ice Cream, Mint Chocolate Chip#Ice dessV1IGG7#5.75#4#desserts#Mom's Kitchen
+Ice Cream, Mint Chocolate Chip#Ice dessX1gEQ4#5.64#1#desserts#The Baking Pan
+Ice Cream Sundae#Ice dessbhlAXt#5.62#11#desserts#Mom's Kitchen
+Ice Cream Sundae#Ice dessByapxl#5.72#16#desserts#The Baking Pan
+Cobbler, Peach#CobbdessYUGeOB#10.14#20#desserts#Mom's Kitchen
+Cobbler, Peach#CobbdessXfEtUK#10.43#16#desserts#The Baking Pan
+Cobbler, Berry-Pecan#Cobbdessx3htak#5.36#12#desserts#Mom's Kitchen
+Cobbler, Berry-Pecan#Cobbdesse4FUVI#5.41#8#desserts#The Baking Pan
+Cobbler, Blueberry#CobbdessbiI0oF#3.78#11#desserts#Mom's Kitchen
+Cobbler, Blueberry#CobbdessMXxbBN#3.57#2#desserts#The Baking Pan
+Cobbler, Cherry#CobbdessNSa8QW#12.58#0#desserts#Mom's Kitchen
+Cobbler, Cherry#CobbdessA1dADa#12.10#10#desserts#The Baking Pan
+Cobbler, Huckleberry#Cobbdess3t6O8d#3.99#18#desserts#Mom's Kitchen
+Cobbler, Huckleberry#CobbdessGI9euK#3.88#0#desserts#The Baking Pan
+Cobbler, Rhubarb#Cobbdess22X40Z#9.54#0#desserts#Mom's Kitchen
+Cobbler, Rhubarb#CobbdessPfnCT0#9.27#18#desserts#The Baking Pan
+Cobbler, Strawberry#CobbdessI78188#12.43#0#desserts#Mom's Kitchen
+Cobbler, Strawberry#CobbdessH3LdgQ#12.20#3#desserts#The Baking Pan
+Cobbler, Zucchini#Cobbdess5rK4dP#11.24#3#desserts#Mom's Kitchen
+Cobbler, Zucchini#Cobbdess4Ez8kS#10.51#10#desserts#The Baking Pan
+Brownies#BrowdessmogdTl#7.62#9#desserts#Mom's Kitchen
+Brownies#Browdess84Qc1z#7.55#9#desserts#The Baking Pan
+Fudge Bar#Fudgdess8iXSyf#11.72#6#desserts#Mom's Kitchen
+Fudge Bar#FudgdessakU1Id#12.29#5#desserts#The Baking Pan
+Cookies, Oatmeal#Cookdessnq9Oya#2.84#15#desserts#Mom's Kitchen
+Cookies, Oatmeal#CookdessBhgp7p#2.68#10#desserts#The Baking Pan
+Cookies, Chocolate Chip#CookdessRVszsZ#12.73#17#desserts#Mom's Kitchen
+Cookies, Chocolate Chip#CookdessSOoHmT#12.26#19#desserts#The Baking Pan
+Cookies, Peanut Butter#Cookdess2UcMI2#7.82#5#desserts#Mom's Kitchen
+Cookies, Peanut Butter#Cookdess1cILme#7.46#20#desserts#The Baking Pan
+Mousse, Chocolate#MousdessDpN4sQ#6.25#20#desserts#Mom's Kitchen
+Mousse, Chocolate#Mousdess8FyFT8#5.96#1#desserts#The Baking Pan
+Mousse, Blueberry Maple#MousdessacwrkO#7.28#7#desserts#Mom's Kitchen
+Mousse, Blueberry Maple#MousdessbiCMFg#7.21#12#desserts#The Baking Pan
+Mousse, Chocolate Banana#MousdessIeW4qz#5.13#2#desserts#Mom's Kitchen
+Mousse, Chocolate Banana#Mousdess1De9oL#5.08#19#desserts#The Baking Pan
+Mousse, Cherry#Mousdesss1bF8H#13.05#20#desserts#Mom's Kitchen
+Mousse, Cherry#Mousdess0ujevx#12.43#1#desserts#The Baking Pan
+Mousse, Eggnog#MousdessZ38hXj#9.07#10#desserts#Mom's Kitchen
+Mousse, Eggnog#Mousdesshs05ST#8.81#8#desserts#The Baking Pan
+Mousse, Strawberry#MousdessHCDlBK#5.58#3#desserts#Mom's Kitchen
+Mousse, Strawberry#MousdessSZ4PyW#5.36#6#desserts#The Baking Pan
+Sherbet, Cantaloupe#Sherdess3DCxUg#3.11#9#desserts#Mom's Kitchen
+Sherbet, Cantaloupe#Sherdesscp2VIz#2.99#7#desserts#The Baking Pan
+Sherbet, Lemon Milk#Sherdess1JVFOS#7.57#9#desserts#Mom's Kitchen
+Sherbet, Lemon Milk#SherdessC865vu#7.57#0#desserts#The Baking Pan
+Sherbet, Orange Crush#Sherdess8W8Mb9#4.32#18#desserts#Mom's Kitchen
+Sherbet, Orange Crush#SherdessxmVJBF#4.16#10#desserts#The Baking Pan
+Sherbet, Blueberry#SherdessFAgxqp#3.46#9#desserts#Mom's Kitchen
+Sherbet, Blueberry#SherdessMPL87u#3.60#6#desserts#The Baking Pan
+Sherbet, Raspberry#Sherdesse86ugA#6.08#1#desserts#Mom's Kitchen
+Sherbet, Raspberry#Sherdesslc1etR#5.85#12#desserts#The Baking Pan
+Sherbet, Strawberry#SherdessFwv09m#4.63#17#desserts#Mom's Kitchen
+Sherbet, Strawberry#SherdessKB0H7q#4.81#20#desserts#The Baking Pan
+Tart, Apple#TartdessrsTyXA#3.35#18#desserts#Mom's Kitchen
+Tart, Apple#Tartdessp7pyiy#3.13#11#desserts#The Baking Pan
+Tart, Almond#TartdessC7FARL#6.62#10#desserts#Mom's Kitchen
+Tart, Almond#Tartdess1V1A1c#6.68#13#desserts#The Baking Pan
+Tart, Blueberry#TartdesssQZRXX#10.28#10#desserts#Mom's Kitchen
+Tart, Blueberry#TartdessUSJSuc#10.28#9#desserts#The Baking Pan
+Tart, Chocolate-Pear#Tartdess2pdOE4#5.67#17#desserts#Mom's Kitchen
+Tart, Chocolate-Pear#TartdessL3aEDd#5.51#9#desserts#The Baking Pan
+Tart, Lemon Fudge#Tartdess9DhZUT#3.88#3#desserts#Mom's Kitchen
+Tart, Lemon Fudge#TartdesshzLOWt#3.96#13#desserts#The Baking Pan
+Tart, Pecan#TartdessvSbXzd#11.80#3#desserts#Mom's Kitchen
+Tart, Pecan#Tartdess6YXJec#11.04#13#desserts#The Baking Pan
+Tart, Pineapple#TartdesseMfJFe#9.01#18#desserts#Mom's Kitchen
+Tart, Pineapple#TartdessA2Wftr#8.44#13#desserts#The Baking Pan
+Tart, Pear#Tartdess4a1BUc#10.09#2#desserts#Mom's Kitchen
+Tart, Pear#TartdessNw8YPG#10.68#5#desserts#The Baking Pan
+Tart, Raspberry#TartdessAVnpP6#6.18#7#desserts#Mom's Kitchen
+Tart, Raspberry#TartdessfVxZFf#5.95#9#desserts#The Baking Pan
+Tart, Strawberry#Tartdess4IUcZW#4.75#8#desserts#Mom's Kitchen
+Tart, Strawberry#Tartdess2BeEDb#4.61#17#desserts#The Baking Pan
+Tart, Raspberry#TartdesshyBd24#1.85#5#desserts#Mom's Kitchen
+Tart, Raspberry#Tartdess5fqxgy#1.94#20#desserts#The Baking Pan
+Trifle, Berry#TrifdessmEkbU2#12.48#19#desserts#Mom's Kitchen
+Trifle, Berry#TrifdessAV9Ix8#12.60#18#desserts#The Baking Pan
+Trifle, American#TrifdesscsdSCd#4.70#17#desserts#Mom's Kitchen
+Trifle, American#TrifdessTArskm#4.35#11#desserts#The Baking Pan
+Trifle, English#TrifdessX87q8T#8.20#9#desserts#Mom's Kitchen
+Trifle, English#Trifdess52l955#8.12#11#desserts#The Baking Pan
+Trifle, Orange#TrifdesslUwxwe#9.74#15#desserts#Mom's Kitchen
+Trifle, Orange#TrifdessFrfCHP#10.22#1#desserts#The Baking Pan
+Trifle, Pumpkin#TrifdessJKFN96#4.72#7#desserts#Mom's Kitchen
+Trifle, Pumpkin#TrifdessMNw4EV#4.95#16#desserts#The Baking Pan
+Trifle, Scottish#TrifdessFa0JdK#13.63#0#desserts#Mom's Kitchen
+Trifle, Scottish#TrifdessAAUQCN#14.03#6#desserts#The Baking Pan
+Trifle, Sherry#TrifdesscuttJg#4.42#5#desserts#Mom's Kitchen
+Trifle, Sherry#TrifdesspRGpfP#4.21#19#desserts#The Baking Pan
+Trifle, Strawberry#TrifdessAd5TpV#3.58#11#desserts#Mom's Kitchen
+Trifle, Strawberry#Trifdess1rtW0A#3.58#3#desserts#The Baking Pan
+Trifle, Scotch Whiskey#Trifdess2zJsGi#5.44#5#desserts#Mom's Kitchen
+Trifle, Scotch Whiskey#TrifdessL8nuI6#5.18#5#desserts#The Baking Pan
+Cheesecake, Amaretto#CheedessOJBqfD#11.89#5#desserts#Mom's Kitchen
+Cheesecake, Amaretto#CheedessVnDf14#11.89#9#desserts#The Baking Pan
+Cheesecake, Apple#Cheedessuks1YK#11.22#15#desserts#Mom's Kitchen
+Cheesecake, Apple#CheedessMYKaKK#11.01#14#desserts#The Baking Pan
+Cheesecake, Apricot#CheedessKUxTYY#12.34#16#desserts#Mom's Kitchen
+Cheesecake, Apricot#CheedessMvB1pr#11.88#18#desserts#The Baking Pan
+Cheesecake, Australian#CheedessQ9WAIn#2.70#9#desserts#Mom's Kitchen
+Cheesecake, Australian#CheedessE6Jyjc#2.53#14#desserts#The Baking Pan
+Cheesecake, Arkansas#CheedessTbqzmw#6.98#9#desserts#Mom's Kitchen
+Cheesecake, Arkansas#CheedesstWJZfC#6.66#5#desserts#The Baking Pan
+Cheesecake, Blueberry#Cheedessyo51KL#8.07#11#desserts#Mom's Kitchen
+Cheesecake, Blueberry#Cheedess4Hz7P4#8.62#5#desserts#The Baking Pan
+Cheesecake, Cherry#CheedessEahRkC#4.40#14#desserts#Mom's Kitchen
+Cheesecake, Cherry#Cheedess3Nx4jZ#4.65#3#desserts#The Baking Pan
+Cheesecake, Cran-Raspberry#CheedessrJsr9i#13.47#20#desserts#Mom's Kitchen
+Cheesecake, Cran-Raspberry#CheedesshcuXCy#14.00#6#desserts#The Baking Pan
+Cheesecake, German Chocolate#CheedesswayvJL#12.03#16#desserts#Mom's Kitchen
+Cheesecake, German Chocolate#CheedessebTAeB#11.58#0#desserts#The Baking Pan
+Cheesecake, Turtle#CheedessLqgeIA#12.19#6#desserts#Mom's Kitchen
+Cheesecake, Turtle#CheedessvyNohA#12.07#19#desserts#The Baking Pan
+Brownies, Apple#BrowdessIDW1Cc#5.44#12#desserts#Mom's Kitchen
+Brownies, Apple#BrowdessyRMrAH#5.14#12#desserts#The Baking Pan
+Brownies, Fudge#BrowdessmIHIFJ#5.19#8#desserts#Mom's Kitchen
+Brownies, Fudge#BrowdessqewJ38#5.10#17#desserts#The Baking Pan
+Brownies, Almond Macaroon#BrowdessniK7QI#10.57#3#desserts#Mom's Kitchen
+Brownies, Almond Macaroon#BrowdessgkXURH#10.36#17#desserts#The Baking Pan
+Brownies, Butterscotch#BrowdesslpA06E#7.16#13#desserts#Mom's Kitchen
+Brownies, Butterscotch#BrowdessK5hofE#7.30#6#desserts#The Baking Pan
+Brownies, Caramel#BrowdessVGfoA8#3.07#3#desserts#Mom's Kitchen
+Brownies, Caramel#Browdess5jvVMM#3.13#11#desserts#The Baking Pan
+Brownies, Cherry#Browdessyoa66A#3.39#17#desserts#Mom's Kitchen
+Brownies, Cherry#BrowdessIg2JuF#3.39#11#desserts#The Baking Pan
+Brownies, Chocolate Chip#Browdessb9dc59#6.18#10#desserts#Mom's Kitchen
+Brownies, Chocolate Chip#BrowdessvW4nOx#6.43#14#desserts#The Baking Pan
+Brownies, Coconut#BrowdessWPHrVR#3.06#15#desserts#Mom's Kitchen
+Brownies, Coconut#BrowdessGVBlML#2.86#11#desserts#The Baking Pan
+Brownies, Cream Cheese#Browdess1OyRay#12.74#4#desserts#Mom's Kitchen
+Brownies, Cream Cheese#Browdess2fRsNv#12.61#19#desserts#The Baking Pan
+Brownies, Fudge Mint#Browdessl7DP7k#11.45#14#desserts#Mom's Kitchen
+Brownies, Fudge Mint#Browdessv70VKQ#11.34#16#desserts#The Baking Pan
+Brownies, Mint Chip#BrowdessDDMvF7#1.81#15#desserts#Mom's Kitchen
+Brownies, Mint Chip#Browdess0j9PBD#1.84#9#desserts#The Baking Pan
+Cake, Angel Food#CakedessEaqGaE#11.18#3#desserts#Mom's Kitchen
+Cake, Angel Food#CakedessJyAyFe#11.18#14#desserts#The Baking Pan
+Cake, Chocolate#CakedessKLXFbn#10.11#7#desserts#Mom's Kitchen
+Cake, Chocolate#CakedessfNP5Hg#9.91#14#desserts#The Baking Pan
+Cake, Carrot#CakedessUTgMoV#4.20#13#desserts#Mom's Kitchen
+Cake, Carrot#CakedessQdkaYg#4.00#3#desserts#The Baking Pan
+Cake, Lemon Blueberry#CakedessepkeEW#11.73#16#desserts#Mom's Kitchen
+Cake, Lemon Blueberry#CakedessHTKyQs#12.42#16#desserts#The Baking Pan
+Cake Triple Fudge#CakedessiZ75lR#7.92#7#desserts#Mom's Kitchen
+Cake Triple Fudge#CakedessWRrSXP#8.00#15#desserts#The Baking Pan
+Cake, Walnut#CakedessveYVXZ#10.83#17#desserts#Mom's Kitchen
+Cake, Walnut#Cakedesse22rT5#11.04#7#desserts#The Baking Pan
+Cake, French Apple#CakedessjA2Kxv#1.95#0#desserts#Mom's Kitchen
+Cake, French Apple#CakedessNBHCk0#1.86#20#desserts#The Baking Pan
+Cake, Fig#CakedessOncX4y#6.82#3#desserts#Mom's Kitchen
+Cake, Fig#CakedessTJtffn#7.08#10#desserts#The Baking Pan
+Cake, Maple#CakedessnoGPRF#3.04#11#desserts#Mom's Kitchen
+Cake, Maple#CakedessfVattM#3.22#4#desserts#The Baking Pan
+Cake, Devil's Food#CakedessiXcDCt#4.73#7#desserts#Mom's Kitchen
+Cake, Devil's Food#CakedessnBZk45#4.82#6#desserts#The Baking Pan
+Cake, Double-Lemon#CakedesskeS0Vd#3.46#9#desserts#Mom's Kitchen
+Cake, Double-Lemon#Cakedess50vx53#3.60#6#desserts#The Baking Pan
+Sorbet, Blackberry#SorbdessQoa0CE#9.88#15#desserts#Mom's Kitchen
+Sorbet, Blackberry#SorbdessqoOYzv#9.78#9#desserts#The Baking Pan
diff --git a/examples_java/src/db/GettingStarted/vendors.txt b/examples_java/src/db/GettingStarted/vendors.txt
new file mode 100644
index 0000000..528e1b1
--- /dev/null
+++ b/examples_java/src/db/GettingStarted/vendors.txt
@@ -0,0 +1,6 @@
+TriCounty Produce#309 S. Main Street#Middle Town#MN#55432#763 555 5761#Mort Dufresne#763 555 5765
+Simply Fresh#15612 Bogart Lane#Harrigan#WI#53704#420 333 3912#Cheryl Swedberg#420 333 3952
+Off the Vine#133 American Ct.#Centennial#IA#52002#563 121 3800#Bob King#563 121 3800 x54
+The Pantry#1206 N. Creek Way#Middle Town#MN#55432#763 555 3391#Sully Beckstrom#763 555 3391
+Mom's Kitchen#53 Yerman Ct.#Middle Town#MN#55432#763 554 9200#Maggie Kultgen#763 554 9200 x12
+The Baking Pan#1415 53rd Ave.#Dutchin#MN#56304#320 442 2277#Mike Roan#320 442 6879
diff --git a/examples_java/src/db/LockExample.java b/examples_java/src/db/LockExample.java
new file mode 100644
index 0000000..7b20838
--- /dev/null
+++ b/examples_java/src/db/LockExample.java
@@ -0,0 +1,226 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db;
+
+import com.sleepycat.db.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.PrintStream;
+import java.util.Vector;
+
+//
+// An example of a program using Lock and related classes.
+//
+class LockExample {
+ private static final String progname = "LockExample";
+ private static final File LOCK_HOME = new File("TESTDIR");
+ Environment dbenv;
+
+ public LockExample(File home, int maxlocks, boolean do_unlink)
+ throws DatabaseException, FileNotFoundException {
+
+ if (do_unlink) {
+ Environment.remove(home, true, null);
+ }
+
+ EnvironmentConfig config = new EnvironmentConfig();
+ config.setErrorStream(System.err);
+ config.setErrorPrefix("LockExample");
+ config.setMaxLocks(maxlocks);
+ config.setAllowCreate(true);
+ config.setInitializeLocking(true);
+ dbenv = new Environment(home, config);
+ }
+
+ public void close() throws DatabaseException {
+ dbenv.close();
+ }
+
+ // Prompts for a line, returning the default answer if a blank
+ // line is entered.
+ //
+ public static String askForLine(InputStreamReader reader,
+ PrintStream out, String prompt,
+ String defaultAnswer) {
+ String result;
+ do {
+ if (defaultAnswer != null)
+ out.print(prompt + " [" + defaultAnswer + "] >");
+ else
+ out.print(prompt);
+ out.flush();
+ result = getLine(reader);
+ if (result == null || result.length() == 0)
+ result = defaultAnswer;
+ } while (result == null);
+ return result;
+ }
+
+ // Not terribly efficient, but does the job.
+ // Works for reading a line from stdin or a file.
+ // Returns null on EOF. If EOF appears in the middle
+ // of a line, returns that line, then null on next call.
+ //
+ public static String getLine(InputStreamReader reader) {
+ StringBuffer b = new StringBuffer();
+ int c;
+ try {
+ while ((c = reader.read()) != -1 && c != '\n')
+ if (c != '\r')
+ b.append((char)c);
+ } catch (IOException ioe) {
+ c = -1;
+ }
+
+ if (c == -1 && b.length() == 0)
+ return null;
+ else
+ return b.toString();
+ }
+
+ public void run() throws DatabaseException {
+ long held;
+ int len = 0, locker;
+ int ret;
+ boolean did_get = false;
+ int lockid = 0;
+ InputStreamReader in = new InputStreamReader(System.in);
+ Vector locks = new Vector();
+
+ //
+ // Accept lock requests.
+ //
+ locker = dbenv.createLockerID();
+ for (held = 0;;) {
+ String opbuf = askForLine(in, System.out,
+ "Operation get/release", "get");
+ if (opbuf == null)
+ break;
+
+ try {
+ if (opbuf.equals("get") || opbuf.equals("vget")) {
+ // Acquire a lock.
+ String objbuf = askForLine(in, System.out,
+ "input object (text string) to lock> ", null);
+ if (objbuf == null)
+ break;
+
+ String lockbuf;
+ do {
+ lockbuf = askForLine(in, System.out,
+ "lock type read/write", "read");
+ if (lockbuf == null)
+ break;
+ len = lockbuf.length();
+ } while (len >= 1 &&
+ !lockbuf.equals("read") &&
+ !lockbuf.equals("write"));
+
+ LockRequestMode lock_type;
+ if (len <= 1 || lockbuf.equals("read"))
+ lock_type = LockRequestMode.READ;
+ else
+ lock_type = LockRequestMode.WRITE;
+
+ DatabaseEntry entry = new DatabaseEntry(objbuf.getBytes());
+
+ Lock lock;
+ if (opbuf.equals("get")) {
+ did_get = true;
+ lock = dbenv.getLock(locker, true, entry, lock_type);
+ } else {
+ LockRequest req = new LockRequest(
+ LockOperation.GET, lock_type, entry, null);
+ LockRequest[] reqs = { req };
+ dbenv.lockVector(locker, true, reqs);
+ lock = req.getLock();
+ System.out.println("Got lock: " + lock);
+ }
+ lockid = locks.size();
+ locks.addElement(lock);
+ } else {
+ // Release a lock.
+ String objbuf;
+ objbuf = askForLine(in, System.out,
+ "input lock to release> ", null);
+ if (objbuf == null)
+ break;
+
+ lockid = Integer.parseInt(objbuf, 16);
+ if (lockid < 0 || lockid >= locks.size()) {
+ System.out.println("Lock #" + lockid + " out of range");
+ continue;
+ }
+ did_get = false;
+ Lock lock = (Lock)locks.elementAt(lockid);
+ dbenv.putLock(lock);
+ }
+ System.out.println("Lock #" + lockid + " " +
+ (did_get ? "granted" : "released"));
+ held += did_get ? 1 : -1;
+ } catch (LockNotGrantedException lnge) {
+ System.err.println("Lock not granted");
+ } catch (DeadlockException de) {
+ System.err.println("LockExample: lock_" +
+ (did_get ? "get" : "put") +
+ ": returned DEADLOCK");
+ } catch (DatabaseException dbe) {
+ System.err.println("LockExample: lock_get: " + dbe.toString());
+ }
+ }
+ System.out.println();
+ System.out.println("Closing lock region " + String.valueOf(held) +
+ " locks held");
+ }
+
+ private static void usage() {
+ System.err.println("usage: LockExample [-u] [-h home] [-m maxlocks]");
+ System.exit(1);
+ }
+
+ public static void main(String[] argv) {
+ File home = LOCK_HOME;
+ boolean do_unlink = false;
+ int maxlocks = 0;
+
+ for (int i = 0; i < argv.length; ++i) {
+ if (argv[i].equals("-h")) {
+ if (++i >= argv.length)
+ usage();
+ home = new File(argv[i]);
+ } else if (argv[i].equals("-m")) {
+ if (++i >= argv.length)
+ usage();
+
+ try {
+ maxlocks = Integer.parseInt(argv[i]);
+ } catch (NumberFormatException nfe) {
+ usage();
+ }
+ } else if (argv[i].equals("-u")) {
+ do_unlink = true;
+ } else {
+ usage();
+ }
+ }
+
+ try {
+ LockExample app = new LockExample(home, maxlocks, do_unlink);
+ app.run();
+ app.close();
+ } catch (DatabaseException dbe) {
+ System.err.println(progname + ": " + dbe.toString());
+ } catch (Throwable t) {
+ System.err.println(progname + ": " + t.toString());
+ }
+ System.out.println("LockExample completed");
+ }
+}
diff --git a/examples_java/src/db/SequenceExample.java b/examples_java/src/db/SequenceExample.java
new file mode 100644
index 0000000..92f0984
--- /dev/null
+++ b/examples_java/src/db/SequenceExample.java
@@ -0,0 +1,92 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db;
+
+import com.sleepycat.db.*;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.PrintStream;
+
+class SequenceExample {
+ private static final int EXIT_SUCCESS = 0;
+ private static final int EXIT_FAILURE = 1;
+
+ public SequenceExample() {
+ }
+
+ public static void usage() {
+ System.out.println("usage: java " +
+ "db.SequenceExample [-r] [database]\n");
+ System.exit(EXIT_FAILURE);
+ }
+
+ public static void main(String[] argv) {
+ boolean removeExistingDatabase = false;
+ String databaseName = "access.db";
+
+ for (int i = 0; i < argv.length; i++) {
+ if (argv[i].equals("-r"))
+ removeExistingDatabase = true;
+ else if (argv[i].equals("-?"))
+ usage();
+ else if (argv[i].startsWith("-"))
+ usage();
+ else {
+ if ((argv.length - i) != 1)
+ usage();
+ databaseName = argv[i];
+ break;
+ }
+ }
+
+ try {
+ SequenceExample app = new SequenceExample();
+ app.run(removeExistingDatabase, databaseName);
+ } catch (DatabaseException dbe) {
+ System.err.println("SequenceExample: " + dbe.toString());
+ System.exit(EXIT_FAILURE);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("SequenceExample: " + fnfe.toString());
+ System.exit(EXIT_FAILURE);
+ }
+ System.exit(EXIT_SUCCESS);
+ }
+
+ public void run(boolean removeExistingDatabase, String databaseName)
+ throws DatabaseException, FileNotFoundException {
+
+ // Remove the previous database.
+ if (removeExistingDatabase)
+ new File(databaseName).delete();
+
+ // Create the database object.
+ // There is no environment for this simple example.
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setErrorStream(System.err);
+ dbConfig.setErrorPrefix("SequenceExample");
+ dbConfig.setType(DatabaseType.BTREE);
+ dbConfig.setAllowCreate(true);
+ Database table = new Database(databaseName, null, dbConfig);
+
+ SequenceConfig config = new SequenceConfig();
+ config.setAllowCreate(true);
+ DatabaseEntry key =
+ new DatabaseEntry("my_sequence".getBytes());
+ Sequence sequence = table.openSequence(null, key, config);
+
+ for (int i = 0; i < 10; i++) {
+ long seqnum = sequence.get(null, 1);
+ System.out.println("Got sequence number: " + seqnum);
+ }
+
+ sequence.close();
+ table.close();
+ }
+}
diff --git a/examples_java/src/db/TpcbExample.java b/examples_java/src/db/TpcbExample.java
new file mode 100644
index 0000000..883dca4
--- /dev/null
+++ b/examples_java/src/db/TpcbExample.java
@@ -0,0 +1,789 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db;
+
+import com.sleepycat.db.*;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.math.BigDecimal;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.Random;
+import java.util.GregorianCalendar;
+
+//
+// This program implements a basic TPC/B driver program. To create the
+// TPC/B database, run with the -i (init) flag. The number of records
+// with which to populate the account, history, branch, and teller tables
+// is specified by the a, s, b, and t flags respectively. To run a TPC/B
+// test, use the n flag to indicate a number of transactions to run in
+// each thread and -T to specify the number of threads.
+//
+class TpcbExample {
+ public static final int TELLERS_PER_BRANCH = 10;
+ public static final int ACCOUNTS_PER_TELLER = 10000;
+ public static final int HISTORY_PER_BRANCH = 2592000;
+
+ //
+ // The default configuration that adheres to TPCB scaling rules requires
+ // nearly 3 GB of space. To avoid requiring that much space for testing,
+ // we set the parameters much lower. If you want to run a valid 10 TPS
+ // configuration, uncomment the VALID_SCALING configuration
+ //
+
+ // VALID_SCALING configuration
+ /*
+ public static final int ACCOUNTS = 1000000;
+ public static final int BRANCHES = 10;
+ public static final int TELLERS = 100;
+ public static final int HISTORY = 25920000;
+ */
+
+ // TINY configuration
+ /*
+ public static final int ACCOUNTS = 1000;
+ public static final int BRANCHES = 10;
+ public static final int TELLERS = 100;
+ public static final int HISTORY = 10000;
+ */
+
+ // Default configuration
+ public static final int ACCOUNTS = 100000;
+ public static final int BRANCHES = 10;
+ public static final int TELLERS = 100;
+ public static final int HISTORY = 259200;
+
+ public static final int HISTORY_LEN = 100;
+ public static final int RECLEN = 100;
+ public static final int BEGID = 1000000;
+
+ // used by random_id()
+ public static final int ACCOUNT = 0;
+ public static final int BRANCH = 1;
+ public static final int TELLER = 2;
+
+ public static boolean verbose = false;
+ public static final String progname = "TpcbExample"; // Program name.
+
+ Environment dbenv;
+ int accounts, branches, tellers, history;
+
+ public TpcbExample(File home,
+ int accounts, int branches, int tellers, int history,
+ int cachesize, boolean noSync)
+ throws DatabaseException, FileNotFoundException {
+
+ this.accounts = accounts;
+ this.branches = branches;
+ this.tellers = tellers;
+ this.history = history;
+
+ EnvironmentConfig config = new EnvironmentConfig();
+ config.setErrorStream(System.err);
+ config.setErrorPrefix(progname);
+ config.setLockDetectMode(LockDetectMode.DEFAULT);
+ config.setCacheSize(cachesize == 0 ? 4 * 1024 * 1024 : cachesize);
+ config.setTxnNoSync(noSync);
+ config.setLockDetectMode(LockDetectMode.DEFAULT);
+ config.setAllowCreate(true);
+
+ config.setInitializeCache(true);
+ config.setTransactional(true);
+ config.setInitializeLocking(true);
+ config.setInitializeLogging(true);
+
+ dbenv = new Environment(home, config);
+ }
+
+ public void close()
+ throws DatabaseException {
+
+ try {
+ if (dbenv != null)
+ dbenv.close();
+ } finally {
+ dbenv = null;
+ }
+ }
+
+ //
+ // Initialize the database to the number of accounts, branches,
+ // history records, and tellers given to the constructor.
+ //
+ public void populate() {
+ Database dbp = null;
+
+ int err;
+ int balance, idnum;
+ int end_anum, end_bnum, end_tnum;
+ int start_anum, start_bnum, start_tnum;
+ int h_nelem;
+
+ idnum = BEGID;
+ balance = 500000;
+
+ h_nelem = accounts;
+
+ try {
+ DatabaseConfig config = new DatabaseConfig();
+ config.setType(DatabaseType.HASH);
+ config.setHashNumElements(h_nelem);
+ config.setAllowCreate(true);
+ dbp = dbenv.openDatabase(null, "account", null, config);
+ } catch (Exception e1) {
+ // can be DatabaseException or FileNotFoundException
+ errExit(e1, "Open of account file failed");
+ }
+
+ start_anum = idnum;
+ populateTable(dbp, idnum, balance, h_nelem, "account");
+ idnum += h_nelem;
+ end_anum = idnum - 1;
+ try {
+ dbp.close();
+ } catch (DatabaseException e2) {
+ errExit(e2, "Account file close failed");
+ }
+
+ if (verbose)
+ System.out.println("Populated accounts: " +
+ String.valueOf(start_anum) + " - " +
+ String.valueOf(end_anum));
+
+ //
+ // Since the number of branches is very small, we want to use very
+ // small pages and only 1 key per page. This is the poor-man's way
+ // of getting key locking instead of page locking.
+ //
+ h_nelem = (int)branches;
+
+ try {
+ DatabaseConfig config = new DatabaseConfig();
+ config.setType(DatabaseType.HASH);
+ config.setHashNumElements(h_nelem);
+ config.setHashFillFactor(1);
+ config.setPageSize(512);
+ config.setAllowCreate(true);
+ dbp = dbenv.openDatabase(null, "branch", null, config);
+ } catch (Exception e3) {
+ // can be DatabaseException or FileNotFoundException
+ errExit(e3, "Branch file create failed");
+ }
+
+ start_bnum = idnum;
+ populateTable(dbp, idnum, balance, h_nelem, "branch");
+ idnum += h_nelem;
+ end_bnum = idnum - 1;
+
+ try {
+ dbp.close();
+ } catch (DatabaseException dbe4) {
+ errExit(dbe4, "Close of branch file failed");
+ }
+
+ if (verbose)
+ System.out.println("Populated branches: " +
+ String.valueOf(start_bnum) + " - " +
+ String.valueOf(end_bnum));
+
+ //
+ // In the case of tellers, we also want small pages, but we'll let
+ // the fill factor dynamically adjust itself.
+ //
+ h_nelem = (int)tellers;
+
+ try {
+ DatabaseConfig config = new DatabaseConfig();
+ config.setType(DatabaseType.HASH);
+ config.setHashNumElements(h_nelem);
+ config.setHashFillFactor(0);
+ config.setPageSize(512);
+ config.setAllowCreate(true);
+ dbp = dbenv.openDatabase(null, "teller", null, config);
+ } catch (Exception e5) {
+ // can be DatabaseException or FileNotFoundException
+ errExit(e5, "Teller file create failed");
+ }
+
+ start_tnum = idnum;
+ populateTable(dbp, idnum, balance, h_nelem, "teller");
+ idnum += h_nelem;
+ end_tnum = idnum - 1;
+
+ try {
+ dbp.close();
+ } catch (DatabaseException e6) {
+ errExit(e6, "Close of teller file failed");
+ }
+
+ if (verbose)
+ System.out.println("Populated tellers: " +
+ String.valueOf(start_tnum) + " - " +
+ String.valueOf(end_tnum));
+
+ try {
+ DatabaseConfig config = new DatabaseConfig();
+ config.setType(DatabaseType.RECNO);
+ config.setRecordLength(HISTORY_LEN);
+ config.setAllowCreate(true);
+ dbp = dbenv.openDatabase(null, "history", null, config);
+ } catch (Exception e7) {
+ // can be DatabaseException or FileNotFoundException
+ errExit(e7, "Create of history file failed");
+ }
+
+ populateHistory(dbp);
+
+ try {
+ dbp.close();
+ } catch (DatabaseException e8) {
+ errExit(e8, "Close of history file failed");
+ }
+ }
+
+ public void populateTable(Database dbp,
+ int start_id, int balance, int nrecs, String msg) {
+ Defrec drec = new Defrec();
+
+ DatabaseEntry kdbt = new DatabaseEntry(drec.data);
+ kdbt.setSize(4); // sizeof(int)
+ DatabaseEntry ddbt = new DatabaseEntry(drec.data);
+ ddbt.setSize(drec.data.length); // uses whole array
+
+ try {
+ for (int i = 0; i < nrecs; i++) {
+ kdbt.setRecordNumber(start_id + (int)i);
+ drec.set_balance(balance);
+ dbp.putNoOverwrite(null, kdbt, ddbt);
+ }
+ } catch (DatabaseException dbe) {
+ System.err.println("Failure initializing " + msg + " file: " +
+ dbe.toString());
+ System.exit(1);
+ }
+ }
+
+ public void populateHistory(Database dbp) {
+ Histrec hrec = new Histrec();
+ hrec.set_amount(10);
+
+ byte[] arr = new byte[4]; // sizeof(int)
+ int i;
+ DatabaseEntry kdbt = new DatabaseEntry(arr);
+ kdbt.setSize(arr.length);
+ DatabaseEntry ddbt = new DatabaseEntry(hrec.data);
+ ddbt.setSize(hrec.data.length);
+
+ try {
+ for (i = 1; i <= history; i++) {
+ kdbt.setRecordNumber(i);
+
+ hrec.set_aid(random_id(ACCOUNT));
+ hrec.set_bid(random_id(BRANCH));
+ hrec.set_tid(random_id(TELLER));
+
+ dbp.append(null, kdbt, ddbt);
+ }
+ } catch (DatabaseException dbe) {
+ errExit(dbe, "Failure initializing history file");
+ }
+ }
+
+ static Random rand = new Random();
+ public static int random_int(int lo, int hi) {
+ int t = rand.nextInt();
+ if (t < 0)
+ t = -t;
+ int ret = (int)(((double)t / ((double)(Integer.MAX_VALUE) + 1)) *
+ (hi - lo + 1));
+ ret += lo;
+ return (ret);
+ }
+
+ public int random_id(int type) {
+ int min, max, num;
+
+ max = min = BEGID;
+ num = accounts;
+ switch(type) {
+ case TELLER:
+ min += branches;
+ num = tellers;
+ // fallthrough
+ case BRANCH:
+ if (type == BRANCH)
+ num = branches;
+ min += accounts;
+ // fallthrough
+ case ACCOUNT:
+ max = min + num - 1;
+ }
+ return (random_int(min, max));
+ }
+
+ // The byte order is our choice.
+ //
+ static long get_int_in_array(byte[] array, int offset) {
+ return
+ ((0xff & array[offset + 0]) << 0) |
+ ((0xff & array[offset + 1]) << 8) |
+ ((0xff & array[offset + 2]) << 16) |
+ ((0xff & array[offset + 3]) << 24);
+ }
+
+ // Note: Value needs to be long to avoid sign extension
+ static void set_int_in_array(byte[] array, int offset, long value) {
+ array[offset + 0] = (byte)((value >> 0) & 0xff);
+ array[offset + 1] = (byte)((value >> 8) & 0xff);
+ array[offset + 2] = (byte)((value >> 16) & 0xff);
+ array[offset + 3] = (byte)((value >> 24) & 0xff);
+ }
+
+ // round 'd' to 'scale' digits, and return result as string
+ static String showRounded(double d, int scale) {
+ return new BigDecimal(d).
+ setScale(scale, BigDecimal.ROUND_HALF_DOWN).toString();
+ }
+
+ public void run(int ntxns, int threads) {
+ double gtps;
+ int txns, failed;
+ long curtime, starttime;
+ TxnThread[] txnList = new TxnThread[threads];
+ for (int i = 0; i < threads; i++)
+ txnList[i] = new TxnThread("Thread " + String.valueOf(i), ntxns);
+
+ starttime = (new Date()).getTime();
+ for (int i = 0; i < threads; i++)
+ txnList[i].start();
+ for (int i = 0; i < threads; i++)
+ try {
+ txnList[i].join();
+ } catch (Exception e1) {
+ errExit(e1, "join failed");
+ }
+
+ curtime = (new Date()).getTime();
+ txns = failed = 0;
+ for (int i = 0; i < threads; i++) {
+ txns += txnList[i].txns;
+ failed += txnList[i].failed;
+ }
+ gtps = (double)(txns - failed) /
+ ((curtime - starttime) / 1000.0);
+ System.out.print("\nTotal: " +
+ String.valueOf(txns) + " txns " +
+ String.valueOf(failed) + " failed ");
+ System.out.println(showRounded(gtps, 2) + " TPS");
+ }
+
+ class TxnThread extends Thread {
+ private int ntxns; /* Number of txns we were asked to run. */
+ public int txns, failed; /* Number that succeeded / failed. */
+ private Database adb, bdb, hdb, tdb;
+
+ public TxnThread(String name, int ntxns) {
+ super(name);
+ this.ntxns = ntxns;
+ }
+
+ public void run() {
+ double gtps, itps;
+ int n, ret;
+ long start_time, end_time;
+
+ //
+ // Open the database files.
+ //
+ int err;
+ try {
+ DatabaseConfig config = new DatabaseConfig();
+ config.setTransactional(true);
+ adb = dbenv.openDatabase(null, "account", null, config);
+ bdb = dbenv.openDatabase(null, "branch", null, config);
+ tdb = dbenv.openDatabase(null, "teller", null, config);
+ hdb = dbenv.openDatabase(null, "history", null, config);
+ } catch (DatabaseException dbe) {
+ TpcbExample.errExit(dbe, "Open of db files failed");
+ } catch (FileNotFoundException fnfe) {
+ TpcbExample.errExit(fnfe, "Open of db files failed, missing file");
+ }
+
+ start_time = (new Date()).getTime();
+ for (txns = n = ntxns, failed = 0; n-- > 0;)
+ if ((ret = txn()) != 0)
+ failed++;
+ end_time = (new Date()).getTime();
+ if (end_time == start_time)
+ end_time++;
+
+ System.out.println(getName() + ": " + (long)txns + " txns: " +
+ failed + " failed, " + TpcbExample.showRounded(
+ (txns - failed) / (double)(end_time - start_time), 2) + " TPS");
+
+ try {
+ adb.close();
+ bdb.close();
+ tdb.close();
+ hdb.close();
+ } catch (DatabaseException dbe2) {
+ TpcbExample.errExit(dbe2, "Close of db files failed");
+ }
+ }
+
+ //
+ // XXX Figure out the appropriate way to pick out IDs.
+ //
+ int txn() {
+ Cursor acurs = null;
+ Cursor bcurs = null;
+ Cursor hcurs = null;
+ Cursor tcurs = null;
+ Transaction t = null;
+
+ Defrec rec = new Defrec();
+ Histrec hrec = new Histrec();
+ int account, branch, teller;
+
+ DatabaseEntry d_dbt = new DatabaseEntry();
+ DatabaseEntry d_histdbt = new DatabaseEntry();
+ DatabaseEntry k_dbt = new DatabaseEntry();
+ DatabaseEntry k_histdbt = new DatabaseEntry();
+
+ account = TpcbExample.this.random_id(TpcbExample.ACCOUNT);
+ branch = TpcbExample.this.random_id(TpcbExample.BRANCH);
+ teller = TpcbExample.this.random_id(TpcbExample.TELLER);
+
+ // The history key will not actually be retrieved,
+ // but it does need to be set to something.
+ byte[] hist_key = new byte[4];
+ k_histdbt.setData(hist_key);
+ k_histdbt.setSize(4 /* == sizeof(int)*/);
+
+ byte[] key_bytes = new byte[4];
+ k_dbt.setData(key_bytes);
+ k_dbt.setSize(4 /* == sizeof(int)*/);
+
+ d_dbt.setData(rec.data);
+ d_dbt.setUserBuffer(rec.length(), true);
+
+ hrec.set_aid(account);
+ hrec.set_bid(branch);
+ hrec.set_tid(teller);
+ hrec.set_amount(10);
+ // Request 0 bytes since we're just positioning.
+ d_histdbt.setPartial(0, 0, true);
+
+ // START PER-TRANSACTION TIMING.
+ //
+ // Technically, TPCB requires a limit on response time, you only
+ // get to count transactions that complete within 2 seconds.
+ // That's not an issue for this sample application -- regardless,
+ // here's where the transaction begins.
+ try {
+ t = dbenv.beginTransaction(null, null);
+
+ acurs = adb.openCursor(t, null);
+ bcurs = bdb.openCursor(t, null);
+ tcurs = tdb.openCursor(t, null);
+ hcurs = hdb.openCursor(t, null);
+
+ // Account record
+ k_dbt.setRecordNumber(account);
+ if (acurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
+ throw new Exception("acurs get failed");
+ rec.set_balance(rec.get_balance() + 10);
+ acurs.putCurrent(d_dbt);
+
+ // Branch record
+ k_dbt.setRecordNumber(branch);
+ if (bcurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
+ throw new Exception("bcurs get failed");
+ rec.set_balance(rec.get_balance() + 10);
+ bcurs.putCurrent(d_dbt);
+
+ // Teller record
+ k_dbt.setRecordNumber(teller);
+ if (tcurs.getSearchKey(k_dbt, d_dbt, null) != OperationStatus.SUCCESS)
+ throw new Exception("ccurs get failed");
+ rec.set_balance(rec.get_balance() + 10);
+ tcurs.putCurrent(d_dbt);
+
+ // History record
+ d_histdbt.setPartial(0, 0, false);
+ d_histdbt.setData(hrec.data);
+ d_histdbt.setUserBuffer(hrec.length(), true);
+ if (hdb.append(t, k_histdbt, d_histdbt) != OperationStatus.SUCCESS)
+ throw new DatabaseException("put failed");
+
+ acurs.close();
+ acurs = null;
+ bcurs.close();
+ bcurs = null;
+ tcurs.close();
+ tcurs = null;
+ hcurs.close();
+ hcurs = null;
+
+ // null out t in advance; if the commit fails,
+ // we don't want to abort it in the catch clause.
+ Transaction tmptxn = t;
+ t = null;
+ tmptxn.commit();
+
+ // END TIMING
+ return (0);
+ } catch (Exception e) {
+ try {
+ if (acurs != null)
+ acurs.close();
+ if (bcurs != null)
+ bcurs.close();
+ if (tcurs != null)
+ tcurs.close();
+ if (hcurs != null)
+ hcurs.close();
+ if (t != null)
+ t.abort();
+ } catch (DatabaseException dbe) {
+ // not much we can do here.
+ }
+
+ if (TpcbExample.this.verbose) {
+ System.out.println("Transaction A=" + String.valueOf(account) +
+ " B=" + String.valueOf(branch) +
+ " T=" + String.valueOf(teller) +
+ " failed");
+ System.out.println("Reason: " + e.toString());
+ }
+ return (-1);
+ }
+ }
+ }
+
+ private static void usage() {
+ System.err.println(
+ "usage: TpcbExample [-fiv] [-a accounts] [-b branches]\n" +
+ " [-c cachesize] [-h home] [-n transactions]\n" +
+ " [-T threads] [-S seed] [-s history] [-t tellers]");
+ System.exit(1);
+ }
+
+ private static void invarg(String str) {
+ System.err.println("TpcbExample: invalid argument: " + str);
+ System.exit(1);
+ }
+
+ public static void errExit(Exception err, String s) {
+ System.err.print(progname + ": ");
+ if (s != null) {
+ System.err.print(s + ": ");
+ }
+ System.err.println(err.toString());
+ System.exit(1);
+ }
+
+ public static void main(String[] argv) throws java.io.IOException {
+ File home = new File("TESTDIR");
+ int accounts = ACCOUNTS;
+ int branches = BRANCHES;
+ int tellers = TELLERS;
+ int history = HISTORY;
+ int threads = 1;
+ int mpool = 0;
+ int ntxns = 0;
+ boolean iflag = false;
+ boolean txn_no_sync = false;
+ long seed = (new GregorianCalendar()).get(Calendar.SECOND);
+
+ for (int i = 0; i < argv.length; ++i) {
+ if (argv[i].equals("-a")) {
+ // Number of account records
+ if ((accounts = Integer.parseInt(argv[++i])) <= 0)
+ invarg(argv[i]);
+ } else if (argv[i].equals("-b")) {
+ // Number of branch records
+ if ((branches = Integer.parseInt(argv[++i])) <= 0)
+ invarg(argv[i]);
+ } else if (argv[i].equals("-c")) {
+ // Cachesize in bytes
+ if ((mpool = Integer.parseInt(argv[++i])) <= 0)
+ invarg(argv[i]);
+ } else if (argv[i].equals("-f")) {
+ // Fast mode: no txn sync.
+ txn_no_sync = true;
+ } else if (argv[i].equals("-h")) {
+ // DB home.
+ home = new File(argv[++i]);
+ } else if (argv[i].equals("-i")) {
+ // Initialize the test.
+ iflag = true;
+ } else if (argv[i].equals("-n")) {
+ // Number of transactions
+ if ((ntxns = Integer.parseInt(argv[++i])) <= 0)
+ invarg(argv[i]);
+ } else if (argv[i].equals("-S")) {
+ // Random number seed.
+ seed = Long.parseLong(argv[++i]);
+ if (seed <= 0)
+ invarg(argv[i]);
+ } else if (argv[i].equals("-s")) {
+ // Number of history records
+ if ((history = Integer.parseInt(argv[++i])) <= 0)
+ invarg(argv[i]);
+ } else if (argv[i].equals("-T")) {
+ // Number of threads
+ if ((threads = Integer.parseInt(argv[++i])) <= 0)
+ invarg(argv[i]);
+ } else if (argv[i].equals("-t")) {
+ // Number of teller records
+ if ((tellers = Integer.parseInt(argv[++i])) <= 0)
+ invarg(argv[i]);
+ } else if (argv[i].equals("-v")) {
+ // Verbose option.
+ verbose = true;
+ } else {
+ usage();
+ }
+ }
+
+ rand.setSeed((int)seed);
+
+ // Initialize the database environment.
+ // Must be done in within a try block.
+ //
+ TpcbExample app = null;
+ try {
+ app = new TpcbExample(home, accounts, branches, tellers, history,
+ mpool, iflag || txn_no_sync);
+ } catch (Exception e1) {
+ errExit(e1, "initializing environment failed");
+ }
+
+ if (verbose)
+ System.out.println((long)accounts + " Accounts, " +
+ String.valueOf(branches) + " Branches, " +
+ String.valueOf(tellers) + " Tellers, " +
+ String.valueOf(history) + " History");
+
+ if (iflag) {
+ if (ntxns != 0)
+ usage();
+ app.populate();
+ } else {
+ if (ntxns == 0)
+ usage();
+ app.run(ntxns, threads);
+ }
+
+ // Shut down the application.
+
+ try {
+ app.close();
+ } catch (DatabaseException dbe2) {
+ errExit(dbe2, "appexit failed");
+ }
+
+ System.exit(0);
+ }
+};
+
+// Simulate the following C struct:
+// struct Defrec {
+// u_int32_t id;
+// u_int32_t balance;
+// u_int8_t pad[RECLEN - sizeof(int) - sizeof(int)];
+// };
+
+class Defrec {
+ public Defrec() {
+ data = new byte[TpcbExample.RECLEN];
+ }
+
+ public int length() {
+ return TpcbExample.RECLEN;
+ }
+
+ public long get_id() {
+ return TpcbExample.get_int_in_array(data, 0);
+ }
+
+ public void set_id(long value) {
+ TpcbExample.set_int_in_array(data, 0, value);
+ }
+
+ public long get_balance() {
+ return TpcbExample.get_int_in_array(data, 4);
+ }
+
+ public void set_balance(long value) {
+ TpcbExample.set_int_in_array(data, 4, value);
+ }
+
+ static {
+ Defrec d = new Defrec();
+ d.set_balance(500000);
+ }
+
+ public byte[] data;
+}
+
+// Simulate the following C struct:
+// struct Histrec {
+// u_int32_t aid;
+// u_int32_t bid;
+// u_int32_t tid;
+// u_int32_t amount;
+// u_int8_t pad[RECLEN - 4 * sizeof(u_int32_t)];
+// };
+
+class Histrec {
+ public Histrec() {
+ data = new byte[TpcbExample.RECLEN];
+ }
+
+ public int length() {
+ return TpcbExample.RECLEN;
+ }
+
+ public long get_aid() {
+ return TpcbExample.get_int_in_array(data, 0);
+ }
+
+ public void set_aid(long value) {
+ TpcbExample.set_int_in_array(data, 0, value);
+ }
+
+ public long get_bid() {
+ return TpcbExample.get_int_in_array(data, 4);
+ }
+
+ public void set_bid(long value) {
+ TpcbExample.set_int_in_array(data, 4, value);
+ }
+
+ public long get_tid() {
+ return TpcbExample.get_int_in_array(data, 8);
+ }
+
+ public void set_tid(long value) {
+ TpcbExample.set_int_in_array(data, 8, value);
+ }
+
+ public long get_amount() {
+ return TpcbExample.get_int_in_array(data, 12);
+ }
+
+ public void set_amount(long value) {
+ TpcbExample.set_int_in_array(data, 12, value);
+ }
+
+ public byte[] data;
+}
diff --git a/examples_java/src/db/repquote/RepConfig.java b/examples_java/src/db/repquote/RepConfig.java
new file mode 100644
index 0000000..6f29d43
--- /dev/null
+++ b/examples_java/src/db/repquote/RepConfig.java
@@ -0,0 +1,111 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.repquote;
+
+import java.util.Vector;
+
+import com.sleepycat.db.ReplicationHostAddress;
+import com.sleepycat.db.ReplicationManagerAckPolicy;
+import com.sleepycat.db.ReplicationManagerStartPolicy;
+import com.sleepycat.db.ReplicationManagerSiteInfo;
+
+public class RepConfig
+{
+ /* Constant values used in the RepQuote application. */
+ public static final String progname = "RepQuoteExample";
+ public static final int CACHESIZE = 10 * 1024 * 1024;
+ public static final int SLEEPTIME = 5000;
+
+ /* Member variables containing configuration information. */
+ public ReplicationManagerAckPolicy ackPolicy;
+ public boolean bulk; /* Whether bulk transfer should be performed. */
+ public String home; /* The home directory for rep files. */
+ public Vector otherHosts; /* Stores an optional set of "other" hosts. */
+ public int priority; /* Priority within the replication group. */
+ public ReplicationManagerStartPolicy startPolicy;
+ public ReplicationHostAddress thisHost; /* Host address to listen to. */
+ /* Optional value specifying the # of sites in the replication group. */
+ public int totalSites;
+ public boolean verbose;
+
+ /* Member variables used internally. */
+ private int currOtherHost;
+ private boolean gotListenAddress;
+
+ public RepConfig()
+ {
+ startPolicy = ReplicationManagerStartPolicy.REP_ELECTION;
+ home = "";
+ gotListenAddress = false;
+ totalSites = 0;
+ priority = 100;
+ verbose = false;
+ currOtherHost = 0;
+ thisHost = new ReplicationHostAddress();
+ otherHosts = new Vector();
+ ackPolicy = ReplicationManagerAckPolicy.QUORUM;
+ bulk = false;
+ }
+
+ public java.io.File getHome()
+ {
+ return new java.io.File(home);
+ }
+
+ public void setThisHost(String host, int port)
+ {
+ gotListenAddress = true;
+ thisHost.port = port;
+ thisHost.host = host;
+ }
+
+ public ReplicationHostAddress getThisHost()
+ {
+ if (!gotListenAddress)
+ System.err.println("Warning: no host specified, returning default.");
+ return thisHost;
+ }
+
+ public boolean gotListenAddress() {
+ return gotListenAddress;
+ }
+
+ public void addOtherHost(String host, int port, boolean peer)
+ {
+ ReplicationHostAddress newInfo =
+ new ReplicationHostAddress(host, port);
+ RepRemoteHost newHost = new RepRemoteHost(newInfo, peer);
+ otherHosts.add(newHost);
+ }
+
+ public RepRemoteHost getFirstOtherHost()
+ {
+ currOtherHost = 0;
+ if (otherHosts.size() == 0)
+ return null;
+ return (RepRemoteHost)otherHosts.get(currOtherHost);
+ }
+
+ public RepRemoteHost getNextOtherHost()
+ {
+ currOtherHost++;
+ if (currOtherHost >= otherHosts.size())
+ return null;
+ return (RepRemoteHost)otherHosts.get(currOtherHost);
+ }
+
+ public RepRemoteHost getOtherHost(int i)
+ {
+ if (i >= otherHosts.size())
+ return null;
+ return (RepRemoteHost)otherHosts.get(i);
+ }
+
+}
+
diff --git a/examples_java/src/db/repquote/RepQuoteEnvironment.java b/examples_java/src/db/repquote/RepQuoteEnvironment.java
new file mode 100644
index 0000000..af475b6
--- /dev/null
+++ b/examples_java/src/db/repquote/RepQuoteEnvironment.java
@@ -0,0 +1,59 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.repquote;
+
+import com.sleepycat.db.*;
+
+/*
+ * A simple wrapper class, that facilitates storing some
+ * custom information with an Environment object.
+ * The information is used by the Replication callback (handleEvent).
+ */
+public class RepQuoteEnvironment extends Environment
+{
+ private boolean appFinished;
+ private boolean inClientSync;
+ private boolean isMaster;
+
+ public RepQuoteEnvironment(final java.io.File host,
+ EnvironmentConfig config)
+ throws DatabaseException, java.io.FileNotFoundException
+ {
+ super(host, config);
+ appFinished = false;
+ inClientSync = false;
+ isMaster = false;
+ }
+
+ boolean getAppFinished()
+ {
+ return appFinished;
+ }
+ public void setAppFinished(boolean appFinished)
+ {
+ this.appFinished = appFinished;
+ }
+ boolean getInClientSync()
+ {
+ return inClientSync;
+ }
+ public void setInClientSync(boolean inClientSync)
+ {
+ this.inClientSync = inClientSync;
+ }
+ boolean getIsMaster()
+ {
+ return isMaster;
+ }
+ public void setIsMaster(boolean isMaster)
+ {
+ this.isMaster = isMaster;
+ }
+}
+
diff --git a/examples_java/src/db/repquote/RepQuoteExample.java b/examples_java/src/db/repquote/RepQuoteExample.java
new file mode 100644
index 0000000..2c828c9
--- /dev/null
+++ b/examples_java/src/db/repquote/RepQuoteExample.java
@@ -0,0 +1,669 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.repquote;
+
+import java.io.FileNotFoundException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.lang.Thread;
+import java.lang.InterruptedException;
+
+import com.sleepycat.db.*;
+import db.repquote.RepConfig;
+
+/**
+ * RepQuoteExample is a simple but complete demonstration of a replicated
+ * application. The application is a mock stock ticker. The master accepts a
+ * stock symbol and an numerical value as input, and stores this information
+ * into a replicated database; either master or clients can display the
+ * contents of the database.
+ * <p>
+ * The options to start a given replication node are:
+ * <pre>
+ * -h home (required; h stands for home directory)
+ * -l host:port (required; l stands for local)
+ * -C or M (optional; start up as client or master)
+ * -r host:port (optional; r stands for remote; any number of these may
+ * be specified)
+ * -R host:port (optional; R stands for remote peer; only one of these may
+ * be specified)
+ * -a all|quorum (optional; a stands for ack policy)
+ * -b (optional; b stands for bulk)
+ * -n nsites (optional; number of sites in replication group; defaults to 0
+ * to try to dynamically compute nsites)
+ * -p priority (optional; defaults to 100)
+ * -v (optional; v stands for verbose)
+ * </pre>
+ * <p>
+ * A typical session begins with a command such as the following to start a
+ * master:
+ *
+ * <pre>
+ * java db.repquote.RepQuoteExample -M -h dir1 -l localhost:6000
+ * </pre>
+ *
+ * and several clients:
+ *
+ * <pre>
+ * java db.repquote.RepQuoteExample -C -h dir2
+ * -l localhost:6001 -r localhost:6000
+ * java db.repquote.RepQuoteExample -C -h dir3
+ * -l localhost:6002 -r localhost:6000
+ * java db.repquote.RepQuoteExample -C -h dir4
+ * -l localhost:6003 -r localhost:6000
+ * </pre>
+ *
+ * <p>
+ * Each process is a member of a DB replication group. The sample application
+ * expects the following commands to stdin:
+ * <ul>
+ * <li>NEWLINE -- print all the stocks held in the database</li>
+ * <li>quit -- shutdown this node</li>
+ * <li>exit -- shutdown this node</li>
+ * <li>stock_symbol number -- enter this stock and number into the
+ * database</li>
+ * </ul>
+ */
+
+public class RepQuoteExample
+{
+ private RepConfig appConfig;
+ private RepQuoteEnvironment dbenv;
+ private CheckpointThread ckpThr;
+ private LogArchiveThread lgaThr;
+
+ public static void usage()
+ {
+ System.err.println("usage: " + RepConfig.progname +
+ " -h home -l host:port [-CM][-r host:port][-R host:port]\n" +
+ " [-a all|quorum][-b][-n nsites][-p priority][-v]");
+
+ System.err.println(
+ "\t -h home (required; h stands for home directory)\n" +
+ "\t -l host:port (required; l stands for local)\n" +
+ "\t -C or -M (optional; start up as client or master)\n" +
+ "\t -r host:port (optional; r stands for remote; any number " +
+ "of these\n" +
+ "\t may be specified)\n" +
+ "\t -R host:port (optional; R stands for remote peer; only " +
+ "one of\n" +
+ "\t these may be specified)\n" +
+ "\t -a all|quorum (optional; a stands for ack policy)\n" +
+ "\t -b (optional; b stands for bulk)\n" +
+ "\t -n nsites (optional; number of sites in replication " +
+ "group; defaults\n" +
+ "\t to 0 to try to dynamically compute nsites)\n" +
+ "\t -p priority (optional; defaults to 100)\n" +
+ "\t -v (optional; v stands for verbose)\n");
+
+ System.exit(1);
+ }
+
+ public static void main(String[] argv)
+ throws Exception
+ {
+ RepConfig config = new RepConfig();
+ boolean isPeer;
+ String tmpHost;
+ int tmpPort = 0;
+
+ /* Extract the command line parameters. */
+ for (int i = 0; i < argv.length; i++)
+ {
+ isPeer = false;
+ if (argv[i].compareTo("-a") == 0) {
+ if (i == argv.length - 1)
+ usage();
+ i++;
+ if (argv[i].equals("all"))
+ config.ackPolicy = ReplicationManagerAckPolicy.ALL;
+ else if (!argv[i].equals("quorum"))
+ usage();
+ } else if (argv[i].compareTo("-b") == 0)
+ config.bulk = true;
+ else if (argv[i].compareTo("-C") == 0) {
+ config.startPolicy = ReplicationManagerStartPolicy.REP_CLIENT;
+ } else if (argv[i].compareTo("-h") == 0) {
+ if (i == argv.length - 1)
+ usage();
+ /* home - a string arg. */
+ i++;
+ config.home = argv[i];
+ } else if (argv[i].compareTo("-l") == 0) {
+ if (i == argv.length - 1)
+ usage();
+ /* "local" should be host:port. */
+ i++;
+ String[] words = argv[i].split(":");
+ if (words.length != 2) {
+ System.err.println(
+ "Invalid host specification host:port needed.");
+ usage();
+ }
+ try {
+ tmpPort = Integer.parseInt(words[1]);
+ } catch (NumberFormatException nfe) {
+ System.err.println("Invalid host specification, " +
+ "could not parse port number.");
+ usage();
+ }
+ config.setThisHost(words[0], tmpPort);
+ } else if (argv[i].compareTo("-M") == 0) {
+ config.startPolicy = ReplicationManagerStartPolicy.REP_MASTER;
+ } else if (argv[i].compareTo("-n") == 0) {
+ if (i == argv.length - 1)
+ usage();
+ i++;
+ config.totalSites = Integer.parseInt(argv[i]);
+ } else if (argv[i].compareTo("-p") == 0) {
+ if (i == argv.length - 1)
+ usage();
+ i++;
+ config.priority = Integer.parseInt(argv[i]);
+ } else if (argv[i].compareTo("-R") == 0 ||
+ argv[i].compareTo("-r") == 0) {
+ if (i == argv.length - 1)
+ usage();
+ if (argv[i].equals("-R"))
+ isPeer = true;
+ i++;
+ String[] words = argv[i].split(":");
+ if (words.length != 2) {
+ System.err.println(
+ "Invalid host specification host:port needed.");
+ usage();
+ }
+ try {
+ tmpPort = Integer.parseInt(words[1]);
+ } catch (NumberFormatException nfe) {
+ System.err.println("Invalid host specification, " +
+ "could not parse port number.");
+ usage();
+ }
+ config.addOtherHost(words[0], tmpPort, isPeer);
+ } else if (argv[i].compareTo("-v") == 0) {
+ config.verbose = true;
+ } else {
+ System.err.println("Unrecognized option: " + argv[i]);
+ usage();
+ }
+
+ }
+
+ /* Error check command line. */
+ if ((!config.gotListenAddress()) || config.home.length() == 0)
+ usage();
+
+ RepQuoteExample runner = null;
+ try {
+ runner = new RepQuoteExample();
+ runner.init(config);
+
+ /* Sleep to give ourselves time to find a master. */
+ //try {
+ // Thread.sleep(5000);
+ //} catch (InterruptedException e) {}
+
+ runner.doloop();
+ runner.terminate();
+ } catch (DatabaseException dbe) {
+ System.err.println("Caught an exception during " +
+ "initialization or processing: " + dbe);
+ if (runner != null)
+ runner.terminate();
+ }
+ } /* End main. */
+
+ public RepQuoteExample()
+ throws DatabaseException
+ {
+ appConfig = null;
+ dbenv = null;
+ }
+
+ public int init(RepConfig config)
+ throws DatabaseException
+ {
+ int ret = 0;
+ appConfig = config;
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setErrorStream(System.err);
+ envConfig.setErrorPrefix(RepConfig.progname);
+
+ envConfig.setReplicationManagerLocalSite(appConfig.getThisHost());
+ for (RepRemoteHost host = appConfig.getFirstOtherHost();
+ host != null; host = appConfig.getNextOtherHost()){
+ envConfig.replicationManagerAddRemoteSite(
+ host.getAddress(), host.isPeer());
+ }
+ if (appConfig.totalSites > 0)
+ envConfig.setReplicationNumSites(appConfig.totalSites);
+
+ /*
+ * Set replication group election priority for this environment.
+ * An election first selects the site with the most recent log
+ * records as the new master. If multiple sites have the most
+ * recent log records, the site with the highest priority value
+ * is selected as master.
+ */
+ envConfig.setReplicationPriority(appConfig.priority);
+
+ envConfig.setCacheSize(RepConfig.CACHESIZE);
+ envConfig.setTxnNoSync(true);
+
+ envConfig.setEventHandler(new RepQuoteEventHandler());
+
+ /*
+ * Set the policy that determines how master and client sites
+ * handle acknowledgement of replication messages needed for
+ * permanent records. The default policy of "quorum" requires only
+ * a quorum of electable peers sufficient to ensure a permanent
+ * record remains durable if an election is held. The "all" option
+ * requires all clients to acknowledge a permanent replication
+ * message instead.
+ */
+ envConfig.setReplicationManagerAckPolicy(appConfig.ackPolicy);
+
+ /*
+ * Set the threshold for the minimum and maximum time the client
+ * waits before requesting retransmission of a missing message.
+ * Base these values on the performance and load characteristics
+ * of the master and client host platforms as well as the round
+ * trip message time.
+ */
+ envConfig.setReplicationRequestMin(20000);
+ envConfig.setReplicationRequestMax(500000);
+
+ /*
+ * Configure deadlock detection to ensure that any deadlocks
+ * are broken by having one of the conflicting lock requests
+ * rejected. DB_LOCK_DEFAULT uses the lock policy specified
+ * at environment creation time or DB_LOCK_RANDOM if none was
+ * specified.
+ */
+ envConfig.setLockDetectMode(LockDetectMode.DEFAULT);
+
+ envConfig.setAllowCreate(true);
+ envConfig.setRunRecovery(true);
+ envConfig.setThreaded(true);
+ envConfig.setInitializeReplication(true);
+ envConfig.setInitializeLocking(true);
+ envConfig.setInitializeLogging(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setTransactional(true);
+ envConfig.setVerboseReplication(appConfig.verbose);
+ try {
+ dbenv = new RepQuoteEnvironment(appConfig.getHome(), envConfig);
+ } catch(FileNotFoundException e) {
+ System.err.println("FileNotFound exception: " + e);
+ System.err.println(
+ "Ensure that the environment directory is pre-created.");
+ ret = 1;
+ }
+
+ if (appConfig.bulk)
+ dbenv.setReplicationConfig(ReplicationConfig.BULK, true);
+
+ /*
+ * Configure heartbeat timeouts so that repmgr monitors the
+ * health of the TCP connection. Master sites broadcast a heartbeat
+ * at the frequency specified by the DB_REP_HEARTBEAT_SEND timeout.
+ * Client sites wait for message activity the length of the
+ * DB_REP_HEARTBEAT_MONITOR timeout before concluding that the
+ * connection to the master is lost. The DB_REP_HEARTBEAT_MONITOR
+ * timeout should be longer than the DB_REP_HEARTBEAT_SEND timeout.
+ */
+ dbenv.setReplicationTimeout(ReplicationTimeoutType.HEARTBEAT_SEND,
+ 5000000);
+ dbenv.setReplicationTimeout(ReplicationTimeoutType.HEARTBEAT_MONITOR,
+ 10000000);
+
+ /* The following base replication features may also be useful to your
+ * application. See Berkeley DB documentation for more details.
+ * - Master leases: Provide stricter consistency for data reads
+ * on a master site.
+ * - Timeouts: Customize the amount of time Berkeley DB waits
+ * for such things as an election to be concluded or a master
+ * lease to be granted.
+ * - Delayed client synchronization: Manage the master site's
+ * resources by spreading out resource-intensive client
+ * synchronizations.
+ * - Blocked client operations: Return immediately with an error
+ * instead of waiting indefinitely if a client operation is
+ * blocked by an ongoing client synchronization.
+ *
+ * The following repmgr features may also be useful to your
+ * application. See Berkeley DB documentation for more details.
+ * - Two-site strict majority rule - In a two-site replication
+ * group, require both sites to be available to elect a new
+ * master.
+ * - Timeouts - Customize the amount of time repmgr waits
+ * for such things as waiting for acknowledgements or attempting
+ * to reconnect to other sites.
+ * - Site list - return a list of sites currently known to repmgr.
+ */
+
+ /* Start checkpoint and log archive support threads. */
+ ckpThr = new CheckpointThread(dbenv);
+ ckpThr.start();
+ lgaThr = new LogArchiveThread(dbenv, envConfig);
+ lgaThr.start();
+
+ /* Start replication manager. */
+ dbenv.replicationManagerStart(3, appConfig.startPolicy);
+
+ return ret;
+ }
+
+ public int doloop()
+ throws DatabaseException
+ {
+ Database db = null;
+
+ for (;;)
+ {
+ if (db == null) {
+ DatabaseConfig dbconf = new DatabaseConfig();
+ dbconf.setType(DatabaseType.BTREE);
+ if (dbenv.getIsMaster()) {
+ /*
+ * Open database allowing create only if this is a master
+ * database. A client database uses polling to attempt
+ * to open the database without allowing create until
+ * it is successful.
+ *
+ * This polling logic for allowing create can be
+ * simplified under some circumstances. For example, if
+ * the application can be sure a database is already
+ * there, it would never need to open it allowing create.
+ */
+ dbconf.setAllowCreate(true);
+ }
+ dbconf.setTransactional(true);
+
+ try {
+ db = dbenv.openDatabase
+ (null, RepConfig.progname, null, dbconf);
+ } catch (java.io.FileNotFoundException e) {
+ System.err.println("no stock database available yet.");
+ if (db != null) {
+ db.close(true);
+ db = null;
+ }
+ try {
+ Thread.sleep(RepConfig.SLEEPTIME);
+ } catch (InterruptedException ie) {}
+ continue;
+ }
+ }
+
+ BufferedReader stdin =
+ new BufferedReader(new InputStreamReader(System.in));
+
+ /* Listen for input, and add it to the database. */
+ System.out.print("QUOTESERVER");
+ if (!dbenv.getIsMaster())
+ System.out.print("(read-only)");
+ System.out.print("> ");
+ System.out.flush();
+ String nextline = null;
+ try {
+ nextline = stdin.readLine();
+ } catch (IOException ioe) {
+ System.err.println("Unable to get data from stdin");
+ break;
+ }
+ String[] words = nextline.split("\\s");
+
+ /* A blank line causes the DB to be dumped to stdout. */
+ if (words.length == 0 ||
+ (words.length == 1 && words[0].length() == 0)) {
+ try {
+ if (dbenv.getInClientSync())
+ System.err.println(
+ "Cannot read data during client initialization - please try again.");
+ else
+ printStocks(db);
+ } catch (DeadlockException de) {
+ continue;
+ } catch (DatabaseException e) {
+ /*
+ * This could be DB_REP_HANDLE_DEAD, which
+ * should close the database and continue.
+ */
+ System.err.println("Got db exception reading replication" +
+ "DB: " + e);
+ System.err.println("Expected if it was due to a dead " +
+ "replication handle, otherwise an unexpected error.");
+ db.close(true); /* Close no sync. */
+ db = null;
+ continue;
+ }
+ continue;
+ }
+
+ if (words.length == 1 &&
+ (words[0].compareToIgnoreCase("quit") == 0 ||
+ words[0].compareToIgnoreCase("exit") == 0)) {
+ dbenv.setAppFinished(true);
+ break;
+ } else if (words.length != 2) {
+ System.err.println("Format: TICKER VALUE");
+ continue;
+ }
+
+ if (!dbenv.getIsMaster()) {
+ System.err.println("Can't update client.");
+ continue;
+ }
+
+ DatabaseEntry key = new DatabaseEntry(words[0].getBytes());
+ DatabaseEntry data = new DatabaseEntry(words[1].getBytes());
+
+ db.put(null, key, data);
+ }
+ if (db != null)
+ db.close(true);
+ return 0;
+ }
+
+ public void terminate()
+ throws DatabaseException
+ {
+ /* Wait for checkpoint and log archive threads to finish. */
+ try {
+ lgaThr.join();
+ ckpThr.join();
+ } catch (Exception e1) {
+ System.err.println("Support thread join failed.");
+ }
+
+ /*
+ * We have used the DB_TXN_NOSYNC environment flag for improved
+ * performance without the usual sacrifice of transactional durability,
+ * as discussed in the "Transactional guarantees" page of the Reference
+ * Guide: if one replication site crashes, we can expect the data to
+ * exist at another site. However, in case we shut down all sites
+ * gracefully, we push out the end of the log here so that the most
+ * recent transactions don't mysteriously disappear.
+ */
+ dbenv.logFlush(null);
+
+ dbenv.close();
+ }
+
+ /*
+ * void return type since error conditions are propogated
+ * via exceptions.
+ */
+ private void printStocks(Database db)
+ throws DeadlockException, DatabaseException
+ {
+ Cursor dbc = db.openCursor(null, null);
+
+ System.out.println("\tSymbol\tPrice");
+ System.out.println("\t======\t=====");
+
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry data = new DatabaseEntry();
+ OperationStatus ret;
+ for (ret = dbc.getFirst(key, data, LockMode.DEFAULT);
+ ret == OperationStatus.SUCCESS;
+ ret = dbc.getNext(key, data, LockMode.DEFAULT)) {
+ String keystr = new String
+ (key.getData(), key.getOffset(), key.getSize());
+ String datastr = new String
+ (data.getData(), data.getOffset(), data.getSize());
+ System.out.println("\t"+keystr+"\t"+datastr);
+
+ }
+ dbc.close();
+ }
+
+ /*
+ * Implemention of EventHandler interface to handle the Berkeley DB events
+ * we are interested in receiving.
+ */
+ private /* internal */
+ class RepQuoteEventHandler extends EventHandlerAdapter {
+ public void handleRepClientEvent()
+ {
+ dbenv.setIsMaster(false);
+ dbenv.setInClientSync(true);
+ }
+ public void handleRepMasterEvent()
+ {
+ dbenv.setIsMaster(true);
+ dbenv.setInClientSync(false);
+ }
+ public void handleRepNewMasterEvent()
+ {
+ dbenv.setInClientSync(true);
+ }
+ public void handleRepPermFailedEvent()
+ {
+ /*
+ * Did not get enough acks to guarantee transaction
+ * durability based on the configured ack policy. This
+ * transaction will be flushed to the master site's
+ * local disk storage for durability.
+ */
+ System.err.println(
+ "Insufficient acknowledgements to guarantee transaction durability.");
+ }
+ public void handleRepStartupDoneEvent()
+ {
+ dbenv.setInClientSync(false);
+ }
+ }
+} /* End RepQuoteEventHandler class. */
+
+/*
+ * This is a very simple thread that performs checkpoints at a fixed
+ * time interval. For a master site, the time interval is one minute
+ * plus the duration of the checkpoint_delay timeout (30 seconds by
+ * default.) For a client site, the time interval is one minute.
+ */
+class CheckpointThread extends Thread {
+ private RepQuoteEnvironment myEnv = null;
+
+ public CheckpointThread(RepQuoteEnvironment env) {
+ myEnv = env;
+ }
+
+ public void run() {
+ for (;;) {
+ /*
+ * Wait for one minute, polling once per second to see if
+ * application has finished. When application has finished,
+ * terminate this thread.
+ */
+ for (int i = 0; i < 60; i++) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {}
+ if (myEnv.getAppFinished())
+ return;
+ }
+
+ /* Perform a checkpoint. */
+ try {
+ myEnv.checkpoint(null);
+ } catch (DatabaseException de) {
+ System.err.println("Could not perform checkpoint.");
+ }
+ }
+ }
+}
+
+/*
+ * This is a simple log archive thread. Once per minute, it removes all but
+ * the most recent 3 logs that are safe to remove according to a call to
+ * DBENV->log_archive().
+ *
+ * Log cleanup is needed to conserve disk space, but aggressive log cleanup
+ * can cause more frequent client initializations if a client lags too far
+ * behind the current master. This can happen in the event of a slow client,
+ * a network partition, or a new master that has not kept as many logs as the
+ * previous master.
+ *
+ * The approach in this routine balances the need to mitigate against a
+ * lagging client by keeping a few more of the most recent unneeded logs
+ * with the need to conserve disk space by regularly cleaning up log files.
+ * Use of automatic log removal (DBENV->log_set_config() DB_LOG_AUTO_REMOVE
+ * flag) is not recommended for replication due to the risk of frequent
+ * client initializations.
+ */
+class LogArchiveThread extends Thread {
+ private RepQuoteEnvironment myEnv = null;
+ private EnvironmentConfig myEnvConfig = null;
+
+ public LogArchiveThread(RepQuoteEnvironment env,
+ EnvironmentConfig envConfig) {
+ myEnv = env;
+ myEnvConfig = envConfig;
+ }
+
+ public void run() {
+ java.io.File[] logFileList;
+ int logs_to_keep = 3;
+ int minlog;
+
+ for (;;) {
+ /*
+ * Wait for one minute, polling once per second to see if
+ * application has finished. When application has finished,
+ * terminate this thread.
+ */
+ for (int i = 0; i < 60; i++) {
+ try {
+ Thread.sleep(1000);
+ } catch (InterruptedException ie) {}
+ if (myEnv.getAppFinished())
+ return;
+ }
+
+ try {
+ /* Get the list of unneeded log files. */
+ logFileList = myEnv.getArchiveLogFiles(false);
+ /*
+ * Remove all but the logs_to_keep most recent unneeded
+ * log files.
+ */
+ minlog = logFileList.length - logs_to_keep;
+ for (int i = 0; i < minlog; i++) {
+ logFileList[i].delete();
+ }
+ } catch (DatabaseException de) {
+ System.err.println("Problem deleting log archive files.");
+ }
+ }
+ }
+}
diff --git a/examples_java/src/db/repquote/RepRemoteHost.java b/examples_java/src/db/repquote/RepRemoteHost.java
new file mode 100644
index 0000000..06623ba
--- /dev/null
+++ b/examples_java/src/db/repquote/RepRemoteHost.java
@@ -0,0 +1,29 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 1997-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.repquote;
+
+import com.sleepycat.db.ReplicationHostAddress;
+
+public class RepRemoteHost{
+ private ReplicationHostAddress addr;
+ private boolean isPeer;
+
+ public RepRemoteHost(ReplicationHostAddress remoteAddr, boolean hostIsPeer){
+ addr = remoteAddr;
+ isPeer = hostIsPeer;
+ }
+
+ public ReplicationHostAddress getAddress(){
+ return addr;
+ }
+
+ public boolean isPeer(){
+ return isPeer;
+ }
+}
diff --git a/examples_java/src/db/repquote_gsg/RepConfig.java b/examples_java/src/db/repquote_gsg/RepConfig.java
new file mode 100644
index 0000000..1dbf24c
--- /dev/null
+++ b/examples_java/src/db/repquote_gsg/RepConfig.java
@@ -0,0 +1,105 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.repquote_gsg;
+
+import java.util.Vector;
+
+import com.sleepycat.db.ReplicationHostAddress;
+import com.sleepycat.db.ReplicationManagerStartPolicy;
+
+public class RepConfig
+{
+ // Constant values used in the RepQuote application.
+ public static final String progname = "RepQuoteExampleGSG";
+ public static final int CACHESIZE = 10 * 1024 * 1024;
+ public static final int SLEEPTIME = 5000;
+
+ // Member variables containing configuration information.
+ // String specifying the home directory for rep files.
+ public String home;
+ // Stores an optional set of "other" hosts.
+ public Vector<ReplicationHostAddress> otherHosts;
+ // Priority within the replication group.
+ public int priority;
+ public ReplicationManagerStartPolicy startPolicy;
+ // The host address to listen to.
+ public ReplicationHostAddress thisHost;
+ // Optional parameter specifying the # of sites in the
+ // replication group.
+ public int totalSites;
+
+ // Member variables used internally.
+ private int currOtherHost;
+ private boolean gotListenAddress;
+
+ public RepConfig()
+ {
+ startPolicy = ReplicationManagerStartPolicy.REP_ELECTION;
+ home = "";
+ gotListenAddress = false;
+ totalSites = 0;
+ priority = 100;
+ currOtherHost = 0;
+ thisHost = new ReplicationHostAddress();
+ otherHosts = new Vector<ReplicationHostAddress>();
+ }
+
+ public java.io.File getHome()
+ {
+ return new java.io.File(home);
+ }
+
+ public void setThisHost(String host, int port)
+ {
+ gotListenAddress = true;
+ thisHost.port = port;
+ thisHost.host = host;
+ }
+
+ public ReplicationHostAddress getThisHost()
+ {
+ if (!gotListenAddress)
+ System.err.println("Warning: no host specified, returning default.");
+ return thisHost;
+ }
+
+ public boolean gotListenAddress() {
+ return gotListenAddress;
+ }
+
+ public void addOtherHost(String host, int port)
+ {
+ ReplicationHostAddress newInfo = new ReplicationHostAddress(host, port);
+ otherHosts.add(newInfo);
+ }
+
+ public ReplicationHostAddress getFirstOtherHost()
+ {
+ currOtherHost = 0;
+ if (otherHosts.size() == 0)
+ return null;
+ return (ReplicationHostAddress)otherHosts.get(currOtherHost);
+ }
+
+ public ReplicationHostAddress getNextOtherHost()
+ {
+ currOtherHost++;
+ if (currOtherHost >= otherHosts.size())
+ return null;
+ return (ReplicationHostAddress)otherHosts.get(currOtherHost);
+ }
+
+ public ReplicationHostAddress getOtherHost(int i)
+ {
+ if (i >= otherHosts.size())
+ return null;
+ return (ReplicationHostAddress)otherHosts.get(i);
+ }
+}
+
diff --git a/examples_java/src/db/repquote_gsg/RepQuoteEnvironment.java b/examples_java/src/db/repquote_gsg/RepQuoteEnvironment.java
new file mode 100644
index 0000000..2a830fb
--- /dev/null
+++ b/examples_java/src/db/repquote_gsg/RepQuoteEnvironment.java
@@ -0,0 +1,40 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.repquote_gsg;
+
+import com.sleepycat.db.*;
+
+/*
+ * A simple wrapper class, that facilitates storing some
+ * custom information with an Environment object.
+ * The information is used by the Replication callback (handleEvent).
+ */
+public class RepQuoteEnvironment extends Environment
+{
+ private boolean isMaster;
+
+ public RepQuoteEnvironment(final java.io.File host,
+ EnvironmentConfig config)
+ throws DatabaseException, java.io.FileNotFoundException
+ {
+ super(host, config);
+ isMaster = false;
+ }
+
+ boolean getIsMaster()
+ {
+ return isMaster;
+ }
+
+ public void setIsMaster(boolean isMaster)
+ {
+ this.isMaster = isMaster;
+ }
+}
+
diff --git a/examples_java/src/db/repquote_gsg/RepQuoteExampleGSG.java b/examples_java/src/db/repquote_gsg/RepQuoteExampleGSG.java
new file mode 100644
index 0000000..79740d7
--- /dev/null
+++ b/examples_java/src/db/repquote_gsg/RepQuoteExampleGSG.java
@@ -0,0 +1,383 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// NOTE: This example is a simplified version of the RepQuoteExample.java
+// example that can be found in the db/examples_java/src/db/repquote directory.
+//
+// This example is intended only as an aid in learning Replication Manager
+// concepts. It is not complete in that many features are not exercised
+// in it, nor are many error conditions properly handled.
+
+package db.repquote_gsg;
+
+import java.io.FileNotFoundException;
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+import java.lang.Thread;
+import java.lang.InterruptedException;
+
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.EventHandler;
+import com.sleepycat.db.LockMode;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.ReplicationHandleDeadException;
+import com.sleepycat.db.ReplicationHostAddress;
+import com.sleepycat.db.ReplicationManagerStartPolicy;
+import com.sleepycat.db.ReplicationManagerAckPolicy;
+import db.repquote_gsg.RepConfig;
+
+public class RepQuoteExampleGSG implements EventHandler
+{
+ private RepConfig repConfig;
+ private RepQuoteEnvironment dbenv;
+
+ public static void usage()
+ {
+ System.err.println("usage: " + RepConfig.progname);
+ System.err.println("-h home -l host:port [-r host:port]" +
+ "[-n nsites][-p priority]");
+
+ System.err.println("\t -h home directory (required)\n" +
+ "\t -l host:port (required; l stands for local)\n" +
+ "\t -r host:port (optional; r stands for remote; any " +
+ "number of these\n" +
+ "\t may be specified)\n" +
+ "\t -n nsites (optional; number of sites in replication " +
+ "group; defaults\n" +
+ "\t to 0 to try to dynamically compute nsites)\n" +
+ "\t -p priority (optional; defaults to 100)\n");
+
+ System.exit(1);
+ }
+
+ public static void main(String[] argv)
+ throws Exception
+ {
+ RepConfig config = new RepConfig();
+ String tmpHost;
+ int tmpPort = 0;
+ // Extract the command line parameters.
+ for (int i = 0; i < argv.length; i++)
+ {
+ if (argv[i].compareTo("-h") == 0) {
+ // home is a string arg.
+ i++;
+ config.home = argv[i];
+ } else if (argv[i].compareTo("-l") == 0) {
+ // "local" should be host:port.
+ i++;
+ String[] words = argv[i].split(":");
+ if (words.length != 2) {
+ System.err.println(
+ "Invalid host specification host:port needed.");
+ usage();
+ }
+ try {
+ tmpPort = Integer.parseInt(words[1]);
+ } catch (NumberFormatException nfe) {
+ System.err.println("Invalid host specification, " +
+ "could not parse port number.");
+ usage();
+ }
+ config.setThisHost(words[0], tmpPort);
+ } else if (argv[i].compareTo("-n") == 0) {
+ i++;
+ config.totalSites = Integer.parseInt(argv[i]);
+ } else if (argv[i].compareTo("-p") == 0) {
+ i++;
+ config.priority = Integer.parseInt(argv[i]);
+ } else if (argv[i].compareTo("-r") == 0) {
+ i++;
+ String[] words = argv[i].split(":");
+ if (words.length != 2) {
+ System.err.println(
+ "Invalid host specification host:port needed.");
+ usage();
+ }
+ try {
+ tmpPort = Integer.parseInt(words[1]);
+ } catch (NumberFormatException nfe) {
+ System.err.println("Invalid host specification, " +
+ "could not parse port number.");
+ usage();
+ }
+ config.addOtherHost(words[0], tmpPort);
+ } else {
+ System.err.println("Unrecognized option: " + argv[i]);
+ usage();
+ }
+
+ }
+
+ // Error check command line.
+ if ((!config.gotListenAddress()) || config.home.length() == 0)
+ usage();
+
+ RepQuoteExampleGSG runner = null;
+ try {
+ runner = new RepQuoteExampleGSG();
+ runner.init(config);
+
+ runner.doloop();
+ runner.terminate();
+ } catch (DatabaseException dbe) {
+ System.err.println("Caught an exception during " +
+ "initialization or processing: " + dbe.toString());
+ if (runner != null)
+ runner.terminate();
+ }
+ System.exit(0);
+ } // end main
+
+ public RepQuoteExampleGSG()
+ throws DatabaseException
+ {
+ repConfig = null;
+ dbenv = null;
+ }
+
+ public int init(RepConfig config)
+ throws DatabaseException
+ {
+ int ret = 0;
+ repConfig = config;
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setErrorStream(System.err);
+ envConfig.setErrorPrefix(RepConfig.progname);
+
+ envConfig.setReplicationManagerLocalSite(repConfig.getThisHost());
+ for (ReplicationHostAddress host = repConfig.getFirstOtherHost();
+ host != null; host = repConfig.getNextOtherHost())
+ envConfig.replicationManagerAddRemoteSite(host, false);
+
+ if (repConfig.totalSites > 0)
+ envConfig.setReplicationNumSites(repConfig.totalSites);
+ envConfig.setReplicationPriority(repConfig.priority);
+
+ envConfig.setReplicationManagerAckPolicy(
+ ReplicationManagerAckPolicy.ALL);
+ envConfig.setCacheSize(RepConfig.CACHESIZE);
+ envConfig.setTxnNoSync(true);
+
+ envConfig.setEventHandler(this);
+
+ envConfig.setAllowCreate(true);
+ envConfig.setRunRecovery(true);
+ envConfig.setThreaded(true);
+ envConfig.setInitializeReplication(true);
+ envConfig.setInitializeLocking(true);
+ envConfig.setInitializeLogging(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setTransactional(true);
+ try {
+ dbenv = new RepQuoteEnvironment(repConfig.getHome(), envConfig);
+ } catch(FileNotFoundException e) {
+ System.err.println("FileNotFound exception: " + e.toString());
+ System.err.println(
+ "Ensure that the environment directory is pre-created.");
+ ret = 1;
+ }
+
+ // Start Replication Manager.
+ dbenv.replicationManagerStart(3, repConfig.startPolicy);
+ return ret;
+ }
+
+ // Provides the main data processing function for our application.
+ // This function provides a command line prompt to which the user
+ // can provide a ticker string and a stock price. Once a value is
+ // entered to the application, the application writes the value to
+ // the database and then displays the entire database.
+ public int doloop()
+ throws DatabaseException
+ {
+ Database db = null;
+
+ for (;;)
+ {
+ if (db == null) {
+ DatabaseConfig dbconf = new DatabaseConfig();
+ dbconf.setType(DatabaseType.BTREE);
+ if (dbenv.getIsMaster()) {
+ dbconf.setAllowCreate(true);
+ }
+ dbconf.setTransactional(true);
+
+ try {
+ db = dbenv.openDatabase
+ (null, RepConfig.progname, null, dbconf);
+ } catch (java.io.FileNotFoundException e) {
+ System.err.println("No stock database available yet.");
+ if (db != null) {
+ db.close(true);
+ db = null;
+ }
+ try {
+ Thread.sleep(RepConfig.SLEEPTIME);
+ } catch (InterruptedException ie) {}
+ continue;
+ }
+ }
+
+ BufferedReader stdin =
+ new BufferedReader(new InputStreamReader(System.in));
+
+ // Listen for input, and add it to the database.
+ System.out.print("QUOTESERVER");
+ if (!dbenv.getIsMaster())
+ System.out.print("(read-only)");
+ System.out.print("> ");
+ System.out.flush();
+ String nextline = null;
+ try {
+ nextline = stdin.readLine();
+ } catch (IOException ioe) {
+ System.err.println("Unable to get data from stdin");
+ break;
+ }
+ String[] words = nextline.split("\\s");
+
+ // A blank line causes the DB to be dumped to stdout.
+ if (words.length == 0 ||
+ (words.length == 1 && words[0].length() == 0)) {
+ try {
+ printStocks(db);
+ } catch (DeadlockException de) {
+ continue;
+ // Dead replication handles are cased by an election
+ // resulting in a previously committing read becoming
+ // invalid. Close the db handle and reopen.
+ } catch (ReplicationHandleDeadException rhde) {
+ db.close(true); // close no sync.
+ db = null;
+ continue;
+ } catch (DatabaseException e) {
+ System.err.println("Got db exception reading replication" +
+ "DB: " + e.toString());
+ break;
+ }
+ continue;
+ }
+
+ if (words.length == 1 &&
+ (words[0].compareToIgnoreCase("quit") == 0 ||
+ words[0].compareToIgnoreCase("exit") == 0)) {
+ break;
+ } else if (words.length != 2) {
+ System.err.println("Format: TICKER VALUE");
+ continue;
+ }
+
+ if (!dbenv.getIsMaster()) {
+ System.err.println("Can't update client.");
+ continue;
+ }
+
+ DatabaseEntry key = new DatabaseEntry(words[0].getBytes());
+ DatabaseEntry data = new DatabaseEntry(words[1].getBytes());
+
+ db.put(null, key, data);
+ }
+ if (db != null)
+ db.close(true);
+ return 0;
+ }
+
+ public void terminate()
+ throws DatabaseException
+ {
+ dbenv.close();
+ }
+
+ public void handleRepClientEvent()
+ {
+ dbenv.setIsMaster(false);
+ }
+
+ public void handleRepMasterEvent()
+ {
+ dbenv.setIsMaster(true);
+ }
+
+ public void handleRepNewMasterEvent(int envId)
+ {
+ // Ignored for now.
+ }
+
+ public void handleWriteFailedEvent(int errorCode)
+ {
+ System.err.println("Write to stable storage failed!" +
+ "Operating system error code:" + errorCode);
+ System.err.println("Continuing....");
+ }
+
+ public void handleRepStartupDoneEvent()
+ {
+ // Ignored for now.
+ }
+
+ public void handleRepPermFailedEvent()
+ {
+ // Ignored for now.
+ }
+
+ public void handleRepElectedEvent()
+ {
+ // Safely ignored for Replication Manager applications.
+ }
+
+ public void handlePanicEvent()
+ {
+ System.err.println("Panic encountered!");
+ System.err.println("Shutting down.");
+ System.err.println("You should restart, running recovery.");
+ try {
+ terminate();
+ } catch (DatabaseException dbe) {
+ System.err.println("Caught an exception during " +
+ "termination in handlePanicEvent: " + dbe.toString());
+ }
+ System.exit(-1);
+ }
+
+ // Display all the stock quote information in the database.
+ // Return type is void because error conditions are propagated
+ // via exceptions.
+ private void printStocks(Database db)
+ throws DeadlockException, DatabaseException
+ {
+ Cursor dbc = db.openCursor(null, null);
+
+ System.out.println("\tSymbol\tPrice");
+ System.out.println("\t======\t=====");
+
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry data = new DatabaseEntry();
+ OperationStatus ret;
+ for (ret = dbc.getFirst(key, data, LockMode.DEFAULT);
+ ret == OperationStatus.SUCCESS;
+ ret = dbc.getNext(key, data, LockMode.DEFAULT)) {
+ String keystr = new String
+ (key.getData(), key.getOffset(), key.getSize());
+ String datastr = new String
+ (data.getData(), data.getOffset(), data.getSize());
+ System.out.println("\t"+keystr+"\t"+datastr);
+ }
+ dbc.close();
+ }
+} // end class
+
diff --git a/examples_java/src/db/repquote_gsg/SimpleConfig.java b/examples_java/src/db/repquote_gsg/SimpleConfig.java
new file mode 100644
index 0000000..7807289
--- /dev/null
+++ b/examples_java/src/db/repquote_gsg/SimpleConfig.java
@@ -0,0 +1,32 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.repquote_gsg;
+
+public class SimpleConfig
+{
+ // Constant values used in the RepQuote application.
+ public static final String progname = "SimpleTxn";
+ public static final int CACHESIZE = 10 * 1024 * 1024;
+
+ // Member variables containing configuration information.
+ public String home; // String specifying the home directory for
+ // rep files.
+
+ public SimpleConfig()
+ {
+ home = "";
+ }
+
+ public java.io.File getHome()
+ {
+ return new java.io.File(home);
+ }
+
+}
+
diff --git a/examples_java/src/db/repquote_gsg/SimpleTxn.java b/examples_java/src/db/repquote_gsg/SimpleTxn.java
new file mode 100644
index 0000000..5b95a3a
--- /dev/null
+++ b/examples_java/src/db/repquote_gsg/SimpleTxn.java
@@ -0,0 +1,229 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2001-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.repquote_gsg;
+
+import java.io.FileNotFoundException;
+// BufferedReader and InputStreamReader needed for our command line
+// prompt.
+import java.io.BufferedReader;
+import java.io.InputStreamReader;
+import java.io.IOException;
+import java.io.UnsupportedEncodingException;
+
+import com.sleepycat.db.Cursor;
+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.LockMode;
+import com.sleepycat.db.OperationStatus;
+import db.repquote_gsg.SimpleConfig;
+
+public class SimpleTxn
+{
+ private SimpleConfig simpleConfig;
+ private Environment dbenv;
+
+ public SimpleTxn()
+ throws DatabaseException
+ {
+ simpleConfig = null;
+ dbenv = null;
+ }
+
+ public static void usage()
+ {
+ System.err.println("usage: " + SimpleConfig.progname + " -h home");
+ System.exit(1);
+ }
+
+ public static void main(String[] argv)
+ throws Exception
+ {
+ SimpleConfig config = new SimpleConfig();
+ // Extract the command line parameters.
+ for (int i = 0; i < argv.length; i++)
+ {
+ if (argv[i].compareTo("-h") == 0) {
+ // home - a string arg.
+ i++;
+ config.home = argv[i];
+ } else {
+ System.err.println("Unrecognized option: " + argv[i]);
+ usage();
+ }
+ }
+
+ // Error check command line.
+ if (config.home.length() == 0)
+ usage();
+
+ SimpleTxn runner = null;
+ try {
+ runner = new SimpleTxn();
+ runner.init(config);
+
+ runner.doloop();
+ runner.terminate();
+ } catch (DatabaseException dbe) {
+ System.err.println("Caught an exception during " +
+ "initialization or processing: " + dbe.toString());
+ if (runner != null)
+ runner.terminate();
+ }
+ System.exit(0);
+ } // end main
+
+ public int init(SimpleConfig config)
+ throws DatabaseException
+ {
+ int ret = 0;
+ simpleConfig = config;
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setErrorStream(System.err);
+ envConfig.setErrorPrefix(SimpleConfig.progname);
+
+ envConfig.setCacheSize(SimpleConfig.CACHESIZE);
+ envConfig.setTxnNoSync(true);
+
+ envConfig.setAllowCreate(true);
+ envConfig.setRunRecovery(true);
+ envConfig.setInitializeLocking(true);
+ envConfig.setInitializeLogging(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setTransactional(true);
+ try {
+ dbenv = new Environment(simpleConfig.getHome(), envConfig);
+ } catch(FileNotFoundException e) {
+ System.err.println("FileNotFound exception: " + e.toString());
+ System.err.println(
+ "Ensure that the environment directory is pre-created.");
+ ret = 1;
+ }
+
+ return ret;
+ }
+
+ // Provides the main data processing function for our application.
+ // This function provides a command line prompt to which the user
+ // can provide a ticker string and a stock price. Once a value is
+ // entered to the application, the application writes the value to
+ // the database and then displays the entire database.
+ public int doloop()
+ throws DatabaseException, UnsupportedEncodingException
+ {
+ Database db = null;
+
+ for (;;)
+ {
+ if (db == null) {
+ DatabaseConfig dbconf = new DatabaseConfig();
+ dbconf.setType(DatabaseType.BTREE);
+ dbconf.setAllowCreate(true);
+ dbconf.setTransactional(true);
+
+ try {
+ db = dbenv.openDatabase(null, // txn handle
+ SimpleConfig.progname, // db filename
+ null, // db name
+ dbconf);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("File not found exception" +
+ fnfe.toString());
+ // Get here only if the environment home directory
+ // somehow does not exist.
+ }
+ }
+
+ BufferedReader stdin =
+ new BufferedReader(new InputStreamReader(System.in));
+
+ // Listen for input, and add it to the database.
+ System.out.print("QUOTESERVER> ");
+ System.out.flush();
+ String nextline = null;
+ try {
+ nextline = stdin.readLine();
+ } catch (IOException ioe) {
+ System.err.println("Unable to get data from stdin");
+ break;
+ }
+ String[] words = nextline.split("\\s");
+
+ // A blank line causes the DB to be dumped to stdout.
+ if (words.length == 0 ||
+ (words.length == 1 && words[0].length() == 0)) {
+ try {
+ printStocks(db);
+ } catch (DatabaseException e) {
+ System.err.println("Got db exception reading " +
+ "DB: " + e.toString());
+ break;
+ }
+ continue;
+ }
+
+ if (words.length == 1 &&
+ (words[0].compareToIgnoreCase("quit") == 0 ||
+ words[0].compareToIgnoreCase("exit") == 0)) {
+ break;
+ } else if (words.length != 2) {
+ System.err.println("Format: TICKER VALUE");
+ continue;
+ }
+
+ DatabaseEntry key =
+ new DatabaseEntry(words[0].getBytes("UTF-8"));
+ DatabaseEntry data =
+ new DatabaseEntry(words[1].getBytes("UTF-8"));
+
+ db.put(null, key, data);
+ }
+ if (db != null)
+ db.close(true);
+ return 0;
+ }
+
+ public void terminate()
+ throws DatabaseException
+ {
+ dbenv.close();
+ }
+
+ // Display all the stock quote information in the database.
+ // Return type is void because error conditions are propagated
+ // via exceptions.
+ private void printStocks(Database db)
+ throws DatabaseException
+ {
+ Cursor dbc = db.openCursor(null, null);
+
+ System.out.println("\tSymbol\tPrice");
+ System.out.println("\t======\t=====");
+
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry data = new DatabaseEntry();
+ OperationStatus ret;
+ for (ret = dbc.getFirst(key, data, LockMode.DEFAULT);
+ ret == OperationStatus.SUCCESS;
+ ret = dbc.getNext(key, data, LockMode.DEFAULT)) {
+ String keystr = new String
+ (key.getData(), key.getOffset(), key.getSize());
+ String datastr = new String
+ (data.getData(), data.getOffset(), data.getSize());
+ System.out.println("\t"+keystr+"\t"+datastr);
+
+ }
+ dbc.close();
+ }
+} // end class
+
diff --git a/examples_java/src/db/txn/DBWriter.java b/examples_java/src/db/txn/DBWriter.java
new file mode 100644
index 0000000..bb60fa4
--- /dev/null
+++ b/examples_java/src/db/txn/DBWriter.java
@@ -0,0 +1,213 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.txn;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.tuple.StringBinding;
+
+import com.sleepycat.db.Cursor;
+import com.sleepycat.db.CursorConfig;
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseEntry;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.LockMode;
+import com.sleepycat.db.OperationStatus;
+import com.sleepycat.db.Transaction;
+
+import java.io.UnsupportedEncodingException;
+import java.util.Random;
+
+public class DBWriter extends Thread
+{
+ private Database myDb = null;
+ private Environment myEnv = null;
+ private EntryBinding dataBinding = null;
+ private Random generator = new Random();
+ private boolean passTxn = false;
+
+
+ private static final int MAX_RETRY = 20;
+
+ private static String[] keys = {"key 1", "key 2", "key 3",
+ "key 4", "key 5", "key 6",
+ "key 7", "key 8", "key 9",
+ "key 10"};
+
+
+ // Constructor. Get our DB handles from here
+ // This consturctor allows us to indicate whether the
+ // txn handle should be handed to countRecords()
+ DBWriter(Environment env, Database db, StoredClassCatalog scc,
+ boolean passtxn)
+
+ throws DatabaseException {
+ myDb = db;
+ myEnv = env;
+ dataBinding = new SerialBinding(scc, PayloadData.class);
+
+ passTxn = passtxn;
+ }
+
+ // Constructor. Get our DB handles from here
+ DBWriter(Environment env, Database db, StoredClassCatalog scc)
+
+ throws DatabaseException {
+ myDb = db;
+ myEnv = env;
+ dataBinding = new SerialBinding(scc, PayloadData.class);
+ }
+
+ // Thread method that writes a series of records
+ // to the database using transaction protection.
+ // Deadlock handling is demonstrated here.
+ public void run () {
+ Transaction txn = null;
+
+ // Perform 50 transactions
+ for (int i=0; i<50; i++) {
+
+ boolean retry = true;
+ int retry_count = 0;
+ // while loop is used for deadlock retries
+ while (retry) {
+ // try block used for deadlock detection and
+ // general db exception handling
+ try {
+
+ // Get a transaction
+ txn = myEnv.beginTransaction(null, null);
+
+ // Write 10 records to the db
+ // for each transaction
+ for (int j = 0; j < 10; j++) {
+ // Get the key
+ DatabaseEntry key = new DatabaseEntry();
+ StringBinding.stringToEntry(keys[j], key);
+
+ // Get the data
+ PayloadData pd = new PayloadData(i+j, getName(),
+ generator.nextDouble());
+ DatabaseEntry data = new DatabaseEntry();
+ dataBinding.objectToEntry(pd, data);
+
+ // Do the put
+ myDb.put(txn, key, data);
+ }
+
+ // commit
+ System.out.println(getName() + " : committing txn : " + i);
+
+ // This code block allows us to decide if txn handle is
+ // passed to countRecords()
+ //
+ // TxnGuideInMemory requires a txn handle be handed to
+ // countRecords(). The code self deadlocks if you don't.
+ // TxnGuide has no such requirement because it supports
+ // uncommitted reads.
+ Transaction txnHandle = null;
+ if (passTxn) { txnHandle = txn; }
+
+ System.out.println(getName() + " : Found " +
+ countRecords(txnHandle) + " records in the database.");
+ try {
+ txn.commit();
+ txn = null;
+ } catch (DatabaseException e) {
+ System.err.println("Error on txn commit: " +
+ e.toString());
+ }
+ retry = false;
+
+ } catch (DeadlockException de) {
+ System.out.println("################# " + getName() +
+ " : caught deadlock");
+ // retry if necessary
+ if (retry_count < MAX_RETRY) {
+ System.err.println(getName() +
+ " : Retrying operation.");
+ retry = true;
+ retry_count++;
+ } else {
+ System.err.println(getName() +
+ " : out of retries. Giving up.");
+ retry = false;
+ }
+ } catch (DatabaseException e) {
+ // abort and don't retry
+ retry = false;
+ System.err.println(getName() +
+ " : caught exception: " + e.toString());
+ System.err.println(getName() +
+ " : errno: " + e.getErrno());
+ e.printStackTrace();
+ } finally {
+ if (txn != null) {
+ try {
+ txn.abort();
+ } catch (Exception e) {
+ System.err.println("Error aborting transaction: " +
+ e.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // This simply counts the number of records contained in the
+ // database and returns the result. You can use this method
+ // in three ways:
+ //
+ // First call it with an active txn handle.
+ //
+ // Secondly, configure the cursor for dirty reads
+ //
+ // Third, call countRecords AFTER the writer has committed
+ // its transaction.
+ //
+ // If you do none of these things, the writer thread will
+ // self-deadlock.
+ //
+ // Note that this method exists only for illustrative purposes.
+ // A more straight-forward way to count the number of records in
+ // a database is to use the Database.getStats() method.
+ private int countRecords(Transaction txn) throws DatabaseException {
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry data = new DatabaseEntry();
+ int count = 0;
+ Cursor cursor = null;
+
+ try {
+ // Get the cursor
+ CursorConfig cc = new CursorConfig();
+ // setReadUncommitted is ignored if the database was not
+ // opened for uncommitted read support. TxnGuide opens
+ // its database in this way, TxnGuideInMemory does not.
+ cc.setReadUncommitted(true);
+ cursor = myDb.openCursor(txn, cc);
+ while (cursor.getNext(key, data, LockMode.DEFAULT) ==
+ OperationStatus.SUCCESS) {
+
+ count++;
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return count;
+
+ }
+}
diff --git a/examples_java/src/db/txn/PayloadData.java b/examples_java/src/db/txn/PayloadData.java
new file mode 100644
index 0000000..9151dd1
--- /dev/null
+++ b/examples_java/src/db/txn/PayloadData.java
@@ -0,0 +1,27 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package db.txn;
+
+import java.io.Serializable;
+
+public class PayloadData implements Serializable {
+ private int oID;
+ private String threadName;
+ private double doubleData;
+
+ PayloadData(int id, String name, double data) {
+ oID = id;
+ threadName = name;
+ doubleData = data;
+ }
+
+ public double getDoubleData() { return doubleData; }
+ public int getID() { return oID; }
+ public String getThreadName() { return threadName; }
+}
diff --git a/examples_java/src/db/txn/TxnGuide.java b/examples_java/src/db/txn/TxnGuide.java
new file mode 100644
index 0000000..73d172f
--- /dev/null
+++ b/examples_java/src/db/txn/TxnGuide.java
@@ -0,0 +1,181 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File TxnGuide.java
+
+package db.txn;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.LockDetectMode;
+
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+public class TxnGuide {
+
+ private static String myEnvPath = "./";
+ private static String dbName = "mydb.db";
+ private static String cdbName = "myclassdb.db";
+
+ // DB handles
+ private static Database myDb = null;
+ private static Database myClassDb = null;
+ private static Environment myEnv = null;
+
+ private static final int NUMTHREADS = 5;
+
+ private static void usage() {
+ System.out.println("TxnGuide [-h <env directory>]");
+ System.exit(-1);
+ }
+
+ public static void main(String args[]) {
+ try {
+ // Parse the arguments list
+ parseArgs(args);
+ // Open the environment and databases
+ openEnv();
+ // Get our class catalog (used to serialize objects)
+ StoredClassCatalog classCatalog =
+ new StoredClassCatalog(myClassDb);
+
+ // Start the threads
+ DBWriter[] threadArray;
+ threadArray = new DBWriter[NUMTHREADS];
+ for (int i = 0; i < NUMTHREADS; i++) {
+ threadArray[i] = new DBWriter(myEnv, myDb, classCatalog);
+ threadArray[i].start();
+ }
+
+ for (int i = 0; i < NUMTHREADS; i++) {
+ threadArray[i].join();
+ }
+ } catch (Exception e) {
+ System.err.println("TxnGuide: " + e.toString());
+ e.printStackTrace();
+ } finally {
+ closeEnv();
+ }
+ System.out.println("All done.");
+ }
+
+
+ private static void openEnv() throws DatabaseException {
+ System.out.println("opening env");
+
+ // Set up the environment.
+ EnvironmentConfig myEnvConfig = new EnvironmentConfig();
+ myEnvConfig.setAllowCreate(true);
+ myEnvConfig.setInitializeCache(true);
+ myEnvConfig.setInitializeLocking(true);
+ myEnvConfig.setInitializeLogging(true);
+ myEnvConfig.setRunRecovery(true);
+ myEnvConfig.setTransactional(true);
+ // EnvironmentConfig.setThreaded(true) is the default behavior
+ // in Java, so we do not have to do anything to cause the
+ // environment handle to be free-threaded.
+
+ // Indicate that we want db to internally perform deadlock
+ // detection. Also indicate that the transaction that has
+ // performed the least amount of write activity to
+ // receive the deadlock notification, if any.
+ myEnvConfig.setLockDetectMode(LockDetectMode.MINWRITE);
+
+ // Set up the database
+ DatabaseConfig myDbConfig = new DatabaseConfig();
+ myDbConfig.setType(DatabaseType.BTREE);
+ myDbConfig.setAllowCreate(true);
+ myDbConfig.setTransactional(true);
+ myDbConfig.setSortedDuplicates(true);
+ myDbConfig.setReadUncommitted(true);
+ // no DatabaseConfig.setThreaded() method available.
+ // db handles in java are free-threaded so long as the
+ // env is also free-threaded.
+
+ try {
+ // Open the environment
+ myEnv = new Environment(new File(myEnvPath), // Env home
+ myEnvConfig);
+
+ // Open the database. Do not provide a txn handle. This open
+ // is autocommitted because DatabaseConfig.setTransactional()
+ // is true.
+ myDb = myEnv.openDatabase(null, // txn handle
+ dbName, // Database file name
+ null, // Database name
+ myDbConfig);
+
+ // Used by the bind API for serializing objects
+ // Class database must not support duplicates
+ myDbConfig.setSortedDuplicates(false);
+ myClassDb = myEnv.openDatabase(null, // txn handle
+ cdbName, // Database file name
+ null, // Database name,
+ myDbConfig);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("openEnv: " + fnfe.toString());
+ System.exit(-1);
+ }
+ }
+
+ private static void closeEnv() {
+ System.out.println("Closing env and databases");
+ if (myDb != null ) {
+ try {
+ myDb.close();
+ } catch (DatabaseException e) {
+ System.err.println("closeEnv: myDb: " +
+ e.toString());
+ e.printStackTrace();
+ }
+ }
+
+ if (myClassDb != null ) {
+ try {
+ myClassDb.close();
+ } catch (DatabaseException e) {
+ System.err.println("closeEnv: myClassDb: " +
+ e.toString());
+ e.printStackTrace();
+ }
+ }
+
+ if (myEnv != null ) {
+ try {
+ myEnv.close();
+ } catch (DatabaseException e) {
+ System.err.println("closeEnv: " + e.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private TxnGuide() {}
+
+ private static void parseArgs(String args[]) {
+ for(int i = 0; i < args.length; ++i) {
+ if (args[i].startsWith("-")) {
+ switch(args[i].charAt(1)) {
+ case 'h':
+ myEnvPath = new String(args[++i]);
+ break;
+ default:
+ usage();
+ }
+ }
+ }
+ }
+}
diff --git a/examples_java/src/db/txn/TxnGuideInMemory.java b/examples_java/src/db/txn/TxnGuideInMemory.java
new file mode 100644
index 0000000..1955a8f
--- /dev/null
+++ b/examples_java/src/db/txn/TxnGuideInMemory.java
@@ -0,0 +1,172 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2005-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File TxnGuideInMemory.java
+
+package db.txn;
+
+import com.sleepycat.bind.serial.StoredClassCatalog;
+
+import com.sleepycat.db.Database;
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.LockDetectMode;
+
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+public class TxnGuideInMemory {
+
+ // DB handles
+ private static Database myDb = null;
+ private static Database myClassDb = null;
+ private static Environment myEnv = null;
+
+ private static int NUMTHREADS = 5;
+
+ public static void main(String args[]) {
+ try {
+ // Open the environment and databases
+ openEnv();
+
+ // Get our class catalog (used to serialize objects)
+ StoredClassCatalog classCatalog =
+ new StoredClassCatalog(myClassDb);
+
+ // Start the threads
+ DBWriter[] threadArray;
+ threadArray = new DBWriter[NUMTHREADS];
+ for (int i = 0; i < NUMTHREADS; i++) {
+ threadArray[i] = new DBWriter(myEnv, myDb, classCatalog, true);
+ threadArray[i].start();
+ }
+
+ System.out.println("Threads started.\n");
+
+ for (int i = 0; i < NUMTHREADS; i++) {
+ threadArray[i].join();
+ }
+ } catch (Exception e) {
+ System.err.println("TxnGuideInMemory: " + e.toString());
+ e.printStackTrace();
+ } finally {
+ closeEnv();
+ }
+ System.out.println("All done.");
+ }
+
+ private static void openEnv() throws DatabaseException {
+ System.out.println("opening env");
+
+ // Set up the environment.
+ EnvironmentConfig myEnvConfig = new EnvironmentConfig();
+
+ // Region files are not backed by the filesystem, they are
+ // backed by heap memory.
+ myEnvConfig.setPrivate(true);
+ myEnvConfig.setAllowCreate(true);
+ myEnvConfig.setInitializeCache(true);
+ myEnvConfig.setInitializeLocking(true);
+ myEnvConfig.setInitializeLogging(true);
+ myEnvConfig.setThreaded(true);
+
+ myEnvConfig.setTransactional(true);
+ // EnvironmentConfig.setThreaded(true) is the default behavior
+ // in Java, so we do not have to do anything to cause the
+ // environment handle to be free-threaded.
+
+ // Indicate that we want db to internally perform deadlock
+ // detection. Also indicate that the transaction that has
+ // performed the least amount of write activity to
+ // receive the deadlock notification, if any.
+ myEnvConfig.setLockDetectMode(LockDetectMode.MINWRITE);
+
+ // Specify in-memory logging
+ myEnvConfig.setLogInMemory(true);
+ // Specify the size of the in-memory log buffer
+ // Must be large enough to handle the log data created by
+ // the largest transaction.
+ myEnvConfig.setLogBufferSize(10 * 1024 * 1024);
+ // Specify the size of the in-memory cache
+ // Set it large enough so that it won't page.
+ myEnvConfig.setCacheSize(10 * 1024 * 1024);
+
+ // Set up the database
+ DatabaseConfig myDbConfig = new DatabaseConfig();
+ myDbConfig.setType(DatabaseType.BTREE);
+ myDbConfig.setAllowCreate(true);
+ myDbConfig.setTransactional(true);
+ myDbConfig.setSortedDuplicates(true);
+ // no DatabaseConfig.setThreaded() method available.
+ // db handles in java are free-threaded so long as the
+ // env is also free-threaded.
+
+ try {
+ // Open the environment
+ myEnv = new Environment(null, // Env home
+ myEnvConfig);
+
+ // Open the database. Do not provide a txn handle. This open
+ // is autocommitted because DatabaseConfig.setTransactional()
+ // is true.
+ myDb = myEnv.openDatabase(null, // txn handle
+ null, // Database file name
+ null, // Database name
+ myDbConfig);
+
+ // Used by the bind API for serializing objects
+ // Class database must not support duplicates
+ myDbConfig.setSortedDuplicates(false);
+ myClassDb = myEnv.openDatabase(null, // txn handle
+ null, // Database file name
+ null, // Database name,
+ myDbConfig);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("openEnv: " + fnfe.toString());
+ System.exit(-1);
+ }
+ }
+
+ private static void closeEnv() {
+ System.out.println("Closing env");
+ if (myDb != null ) {
+ try {
+ myDb.close();
+ } catch (DatabaseException e) {
+ System.err.println("closeEnv: myDb: " +
+ e.toString());
+ e.printStackTrace();
+ }
+ }
+
+ if (myClassDb != null ) {
+ try {
+ myClassDb.close();
+ } catch (DatabaseException e) {
+ System.err.println("closeEnv: myClassDb: " +
+ e.toString());
+ e.printStackTrace();
+ }
+ }
+
+ if (myEnv != null ) {
+ try {
+ myEnv.close();
+ } catch (DatabaseException e) {
+ System.err.println("closeEnv: " + e.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private TxnGuideInMemory() {}
+}
diff --git a/examples_java/src/persist/CustomKeyOrderExample.java b/examples_java/src/persist/CustomKeyOrderExample.java
new file mode 100644
index 0000000..aee8c8b
--- /dev/null
+++ b/examples_java/src/persist/CustomKeyOrderExample.java
@@ -0,0 +1,126 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.persist.EntityCursor;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.KeyField;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PrimaryKey;
+
+public class CustomKeyOrderExample {
+
+ @Entity
+ static class Person {
+
+ @PrimaryKey
+ ReverseOrder name;
+
+ Person(String name) {
+ this.name = new ReverseOrder(name);
+ }
+
+ private Person() {} // For deserialization
+
+ @Override
+ public String toString() {
+ return name.value;
+ }
+ }
+
+ @Persistent
+ static class ReverseOrder implements Comparable<ReverseOrder> {
+
+ @KeyField(1)
+ String value;
+
+ ReverseOrder(String value) {
+ this.value = value;
+ }
+
+ private ReverseOrder() {} // For deserialization
+
+ public int compareTo(ReverseOrder o) {
+ return o.value.compareTo(value);
+ }
+ }
+
+ public static void main(String[] args)
+ throws DatabaseException, FileNotFoundException {
+
+ if (args.length != 2 || !"-h".equals(args[0])) {
+ System.err.println
+ ("Usage: java " + CustomKeyOrderExample.class.getName() +
+ " -h <envHome>");
+ System.exit(2);
+ }
+ CustomKeyOrderExample example =
+ new CustomKeyOrderExample(new File(args[1]));
+ example.run();
+ example.close();
+ }
+
+ private Environment env;
+ private EntityStore store;
+
+ private CustomKeyOrderExample(File envHome)
+ throws DatabaseException, FileNotFoundException {
+
+ /* Open a transactional Berkeley DB engine environment. */
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ envConfig.setTransactional(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(envHome, envConfig);
+
+ /* Open a transactional entity store. */
+ StoreConfig storeConfig = new StoreConfig();
+ storeConfig.setAllowCreate(true);
+ storeConfig.setTransactional(true);
+ store = new EntityStore(env, "TestStore", storeConfig);
+ }
+
+ private void run()
+ throws DatabaseException {
+
+ PrimaryIndex<ReverseOrder,Person> index =
+ store.getPrimaryIndex(ReverseOrder.class, Person.class);
+
+ index.put(new Person("Andy"));
+ index.put(new Person("Lisa"));
+ index.put(new Person("Zola"));
+
+ /* Print the entities in key order. */
+ EntityCursor<Person> people = index.entities();
+ try {
+ for (Person person : people) {
+ System.out.println(person);
+ }
+ } finally {
+ people.close();
+ }
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ env.close();
+ }
+}
diff --git a/examples_java/src/persist/DplDump.java b/examples_java/src/persist/DplDump.java
new file mode 100644
index 0000000..f677bc6
--- /dev/null
+++ b/examples_java/src/persist/DplDump.java
@@ -0,0 +1,148 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.persist.EntityCursor;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.EntityMetadata;
+import com.sleepycat.persist.model.EntityModel;
+import com.sleepycat.persist.raw.RawObject;
+import com.sleepycat.persist.raw.RawStore;
+import com.sleepycat.persist.raw.RawType;
+
+/**
+ * Dumps a store or all stores to standard output in raw XML format. This
+ * sample is intended to be modifed to dump in application specific ways.
+ * @see #usage
+ */
+public class DplDump {
+
+ private File envHome;
+ private String storeName;
+ private boolean dumpMetadata;
+ private Environment env;
+
+ public static void main(String[] args) {
+ try {
+ DplDump dump = new DplDump(args);
+ dump.open();
+ dump.dump();
+ dump.close();
+ } catch (Throwable e) {
+ e.printStackTrace();
+ System.exit(1);
+ }
+ }
+
+ private DplDump(String[] args) {
+
+ for (int i = 0; i < args.length; i += 1) {
+ String name = args[i];
+ String val = null;
+ if (i < args.length - 1 && !args[i + 1].startsWith("-")) {
+ i += 1;
+ val = args[i];
+ }
+ if (name.equals("-h")) {
+ if (val == null) {
+ usage("No value after -h");
+ }
+ envHome = new File(val);
+ } else if (name.equals("-s")) {
+ if (val == null) {
+ usage("No value after -s");
+ }
+ storeName = val;
+ } else if (name.equals("-meta")) {
+ dumpMetadata = true;
+ } else {
+ usage("Unknown arg: " + name);
+ }
+ }
+
+ if (storeName == null) {
+ usage("-s not specified");
+ }
+ if (envHome == null) {
+ usage("-h not specified");
+ }
+ }
+
+ private void usage(String msg) {
+
+ if (msg != null) {
+ System.out.println(msg);
+ }
+
+ System.out.println
+ ("usage:" +
+ "\njava " + DplDump.class.getName() +
+ "\n -h <envHome>" +
+ "\n # Environment home directory" +
+ "\n [-meta]" +
+ "\n # Dump metadata; default: false" +
+ "\n -s <storeName>" +
+ "\n # Store to dump");
+
+ System.exit(2);
+ }
+
+ private void open()
+ throws DatabaseException, FileNotFoundException {
+
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(envHome, envConfig);
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ env.close();
+ }
+
+ private void dump()
+ throws DatabaseException {
+
+ StoreConfig storeConfig = new StoreConfig();
+ storeConfig.setReadOnly(true);
+ RawStore store = new RawStore(env, storeName, storeConfig);
+
+ EntityModel model = store.getModel();
+ for (String clsName : model.getKnownClasses()) {
+ EntityMetadata meta = model.getEntityMetadata(clsName);
+ if (meta != null) {
+ if (dumpMetadata) {
+ for (RawType type : model.getAllRawTypeVersions(clsName)) {
+ System.out.println(type);
+ }
+ } else {
+ PrimaryIndex<Object,RawObject> index =
+ store.getPrimaryIndex(clsName);
+ EntityCursor<RawObject> entities = index.entities();
+ for (RawObject entity : entities) {
+ System.out.println(entity);
+ }
+ entities.close();
+ }
+ }
+ }
+
+ store.close();
+ }
+}
diff --git a/examples_java/src/persist/EventExample.java b/examples_java/src/persist/EventExample.java
new file mode 100644
index 0000000..5a3ee44
--- /dev/null
+++ b/examples_java/src/persist/EventExample.java
@@ -0,0 +1,424 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.Serializable;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+import com.sleepycat.bind.EntryBinding;
+import com.sleepycat.bind.serial.SerialBinding;
+import com.sleepycat.bind.serial.StoredClassCatalog;
+import com.sleepycat.bind.tuple.IntegerBinding;
+import com.sleepycat.bind.tuple.LongBinding;
+import com.sleepycat.db.Cursor;
+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.OperationStatus;
+import com.sleepycat.db.SecondaryConfig;
+import com.sleepycat.db.SecondaryCursor;
+import com.sleepycat.db.SecondaryDatabase;
+import com.sleepycat.db.SecondaryKeyCreator;
+import com.sleepycat.db.Transaction;
+
+/**
+ * EventExample is a trivial example which stores Java objects that represent
+ * an event. Events are primarily indexed by a timestamp, but have other
+ * attributes, such as price, account reps, customer name and quantity.
+ * Some of those other attributes are indexed.
+ * <p>
+ * The example simply shows the creation of a BDB environment and database,
+ * inserting some events, and retrieving the events.
+ * <p>
+ * This example is meant to be paired with its twin, EventExampleDPL.java.
+ * EventExample.java and EventExampleDPL.java perform the same functionality,
+ * but use the Base API and the Direct Persistence Layer API, respectively.
+ * This may be a useful way to compare the two APIs.
+ * <p>
+ * To run the example:
+ * <pre>
+ * cd jehome/examples
+ * javac je/EventExample.java
+ * java je.EventExample -h <environmentDirectory>
+ * </pre>
+ */
+public class EventExample {
+
+ /*
+ * The Event class embodies our example event and is the application
+ * data. BDB data records are represented at key/data tuples. In this
+ * example, the key portion of the record is the event time, and the data
+ * portion is the Event instance.
+ */
+ static class Event implements Serializable {
+
+ /* This example will add secondary indices on price and accountReps. */
+ private int price;
+ private Set<String> accountReps;
+
+ private String customerName;
+ private int quantity;
+
+ Event(int price,
+ String customerName) {
+
+ this.price = price;
+ this.customerName = customerName;
+ this.accountReps = new HashSet<String>();
+ }
+
+ void addRep(String rep) {
+ accountReps.add(rep);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(" price=").append(price);
+ sb.append(" customerName=").append(customerName);
+ sb.append(" reps=");
+ if (accountReps.size() == 0) {
+ sb.append("none");
+ } else {
+ for (String rep: accountReps) {
+ sb.append(rep).append(" ");
+ }
+ }
+ return sb.toString();
+ }
+
+ int getPrice() {
+ return price;
+ }
+ }
+
+ /* A BDB environment is roughly equivalent to a relational database. */
+ private Environment env;
+
+ /*
+ * A BDB table is roughly equivalent to a relational table with a
+ * primary index.
+ */
+ private Database eventDb;
+
+ /* A secondary database indexes an additional field of the data record */
+ private SecondaryDatabase eventByPriceDb;
+
+ /*
+ * The catalogs and bindings are used to convert Java objects to the byte
+ * array format used by BDB key/data in the base API. The Direct
+ * Persistence Layer API supports Java objects as arguments directly.
+ */
+ private Database catalogDb;
+ private EntryBinding eventBinding;
+
+ /* Used for generating example data. */
+ private Calendar cal;
+
+
+ /*
+ * First manually make a directory to house the BDB environment.
+ * Usage: java EventExample -h <envHome>
+ * All BDB on-disk storage is held within envHome.
+ */
+ public static void main(String[] args)
+ throws DatabaseException, FileNotFoundException {
+
+ if (args.length != 2 || !"-h".equals(args[0])) {
+ System.err.println
+ ("Usage: java " + EventExample.class.getName() +
+ " -h <envHome>");
+ System.exit(2);
+ }
+ EventExample example = new EventExample(new File(args[1]));
+ example.run();
+ example.close();
+ }
+
+ private EventExample(File envHome)
+ throws DatabaseException, FileNotFoundException {
+
+ /* Open a transactional Berkeley DB engine environment. */
+ System.out.println("-> Creating a BDB environment");
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ envConfig.setTransactional(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(envHome, envConfig);
+
+ init();
+ cal = Calendar.getInstance();
+ }
+
+ /**
+ * Create all primary and secondary indices.
+ */
+ private void init()
+ throws DatabaseException, FileNotFoundException {
+
+ System.out.println("-> Creating a BDB database");
+ DatabaseConfig dbConfig = new DatabaseConfig();
+ dbConfig.setTransactional(true);
+ dbConfig.setAllowCreate(true);
+ dbConfig.setType(DatabaseType.BTREE);
+ eventDb = env.openDatabase(null, // use auto-commit txn
+ "eventDb", // file name
+ null, // database name
+ dbConfig);
+
+
+ /*
+ * In our example, the database record is composed of a key portion
+ * which represents the event timestamp, and a data portion holds an
+ * instance of the Event class.
+ *
+ * BDB's base API accepts and returns key and data as byte arrays, so
+ * we need some support for marshaling between objects and byte arrays.
+ * We call this binding, and supply a package of helper classes to
+ * support this. It's entirely possible to do all binding on your own.
+ *
+ * A class catalog database is needed for storing class descriptions
+ * for the serial binding used below. This avoids storing class
+ * descriptions redundantly in each record.
+ */
+ DatabaseConfig catalogConfig = new DatabaseConfig();
+ catalogConfig.setTransactional(true);
+ catalogConfig.setAllowCreate(true);
+ catalogConfig.setType(DatabaseType.BTREE);
+ catalogDb = env.openDatabase(null, "catalogDb", null, catalogConfig);
+ StoredClassCatalog catalog = new StoredClassCatalog(catalogDb);
+
+ /*
+ * Create a serial binding for Event data objects. Serial
+ * bindings can be used to store any Serializable object.
+ * We can use some pre-defined binding classes to convert
+ * primitives like the long key value to the a byte array.
+ */
+ eventBinding = new SerialBinding(catalog, Event.class);
+
+ /*
+ * Open a secondary database to allow accessing the primary
+ * database a secondary key value. In this case, access events
+ * by price.
+ */
+ SecondaryConfig secConfig = new SecondaryConfig();
+ secConfig.setTransactional(true);
+ secConfig.setAllowCreate(true);
+ secConfig.setType(DatabaseType.BTREE);
+ secConfig.setSortedDuplicates(true);
+ secConfig.setKeyCreator(new PriceKeyCreator(eventBinding));
+ eventByPriceDb = env.openSecondaryDatabase(null,
+ "priceDb",
+ null,
+ eventDb,
+ secConfig);
+
+ }
+
+ private void run()
+ throws DatabaseException {
+
+ Random rand = new Random();
+
+ /* DatabaseEntry represents the key and data of each record */
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry data = new DatabaseEntry();
+
+ /*
+ * Create a set of events. Each insertion is a separate, auto-commit
+ * transaction.
+ */
+ System.out.println("-> Inserting 4 events");
+ LongBinding.longToEntry(makeDate(1), key);
+ eventBinding.objectToEntry(new Event(100, "Company_A"),
+ data);
+ eventDb.put(null, key, data);
+
+ LongBinding.longToEntry(makeDate(2), key);
+ eventBinding.objectToEntry(new Event(2, "Company_B"),
+ data);
+ eventDb.put(null, key, data);
+
+ LongBinding.longToEntry(makeDate(3), key);
+ eventBinding.objectToEntry(new Event(20, "Company_C"),
+ data);
+ eventDb.put(null, key, data);
+
+ LongBinding.longToEntry(makeDate(4), key);
+ eventBinding.objectToEntry(new Event(40, "CompanyD"),
+ data);
+ eventDb.put(null, key, data);
+
+ /* Load a whole set of events transactionally. */
+ Transaction txn = env.beginTransaction(null, null);
+ int maxPrice = 50;
+ System.out.println("-> Inserting some randomly generated events");
+ for (int i = 0; i < 25; i++) {
+ long time = makeDate(rand.nextInt(365));
+ Event e = new Event(rand.nextInt(maxPrice),"Company_X");
+ if ((i%2) ==0) {
+ e.addRep("Jane");
+ e.addRep("Nikunj");
+ } else {
+ e.addRep("Yongmin");
+ }
+ LongBinding.longToEntry(time, key);
+ eventBinding.objectToEntry(e, data);
+ eventDb.put(txn, key, data);
+ }
+ txn.commitWriteNoSync();
+
+ /*
+ * Windows of events - display the events between June 1 and Aug 31
+ */
+ System.out.println("\n-> Display the events between June 1 and Aug 31");
+ long endDate = makeDate(Calendar.AUGUST, 31);
+
+ /* Position the cursor and print the first event. */
+ Cursor eventWindow = eventDb.openCursor(null, null);
+ LongBinding.longToEntry(makeDate(Calendar.JUNE, 1), key);
+
+ if ((eventWindow.getSearchKeyRange(key, data, null)) !=
+ OperationStatus.SUCCESS) {
+ System.out.println("No events found!");
+ eventWindow.close();
+ return;
+ }
+ try {
+ printEvents(key, data, eventWindow, endDate);
+ } finally {
+ eventWindow.close();
+ }
+
+ /*
+ * Display all events, ordered by a secondary index on price.
+ */
+ System.out.println("\n-> Display all events, ordered by price");
+ SecondaryCursor priceCursor =
+ eventByPriceDb.openSecondaryCursor(null, null);
+ try {
+ printEvents(priceCursor);
+ } finally {
+ priceCursor.close();
+ }
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ eventByPriceDb.close();
+ eventDb.close();
+ catalogDb.close();
+ env.close();
+ }
+
+ /**
+ * Print all events covered by this cursor up to the end date. We know
+ * that the cursor operates on long keys and Event data items, but there's
+ * no type-safe way of expressing that within the BDB base API.
+ */
+ private void printEvents(DatabaseEntry firstKey,
+ DatabaseEntry firstData,
+ Cursor cursor,
+ long endDate)
+ throws DatabaseException {
+
+ System.out.println("time=" +
+ new Date(LongBinding.entryToLong(firstKey)) +
+ eventBinding.entryToObject(firstData));
+ DatabaseEntry key = new DatabaseEntry();
+ DatabaseEntry data = new DatabaseEntry();
+
+ while (cursor.getNext(key, data, null) ==
+ OperationStatus.SUCCESS) {
+ if (LongBinding.entryToLong(key) > endDate) {
+ break;
+ }
+ System.out.println("time=" +
+ new Date(LongBinding.entryToLong(key)) +
+ eventBinding.entryToObject(data));
+ }
+ }
+
+ private void printEvents(SecondaryCursor cursor)
+ throws DatabaseException {
+ DatabaseEntry timeKey = new DatabaseEntry();
+ DatabaseEntry priceKey = new DatabaseEntry();
+ DatabaseEntry eventData = new DatabaseEntry();
+
+ while (cursor.getNext(priceKey, timeKey, eventData, null) ==
+ OperationStatus.SUCCESS) {
+ System.out.println("time=" +
+ new Date(LongBinding.entryToLong(timeKey)) +
+ eventBinding.entryToObject(eventData));
+ }
+ }
+
+ /**
+ * Little utility for making up java.util.Dates for different days, just
+ * to generate test data.
+ */
+ private long makeDate(int day) {
+
+ cal.set((Calendar.DAY_OF_YEAR), day);
+ return cal.getTime().getTime();
+ }
+ /**
+ * Little utility for making up java.util.Dates for different days, just
+ * to make the test data easier to read.
+ */
+ private long makeDate(int month, int day) {
+
+ cal.set((Calendar.MONTH), month);
+ cal.set((Calendar.DAY_OF_MONTH), day);
+ return cal.getTime().getTime();
+ }
+
+ /**
+ * A key creator that knows how to extract the secondary key from the data
+ * entry of the primary database. To do so, it uses both the dataBinding
+ * of the primary database and the secKeyBinding.
+ */
+ private static class PriceKeyCreator implements SecondaryKeyCreator {
+
+ private EntryBinding dataBinding;
+
+ PriceKeyCreator(EntryBinding eventBinding) {
+ this.dataBinding = eventBinding;
+ }
+
+ public boolean createSecondaryKey(SecondaryDatabase secondaryDb,
+ DatabaseEntry keyEntry,
+ DatabaseEntry dataEntry,
+ DatabaseEntry resultEntry)
+ throws DatabaseException {
+
+ /*
+ * Convert the data entry to an Event object, extract the secondary
+ * key value from it, and then convert it to the resulting
+ * secondary key entry.
+ */
+ Event e = (Event) dataBinding.entryToObject(dataEntry);
+ int price = e.getPrice();
+ IntegerBinding.intToEntry(price, resultEntry);
+ return true;
+ }
+ }
+}
diff --git a/examples_java/src/persist/EventExampleDPL.java b/examples_java/src/persist/EventExampleDPL.java
new file mode 100644
index 0000000..283b121
--- /dev/null
+++ b/examples_java/src/persist/EventExampleDPL.java
@@ -0,0 +1,273 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2004-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist;
+
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.Calendar;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Random;
+import java.util.Set;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.db.Transaction;
+import com.sleepycat.persist.EntityCursor;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+
+/**
+ * EventExampleDPL is a trivial example which stores Java objects that
+ * represent an event. Events are primarily indexed by a timestamp, but have
+ * other attributes, such as price, account reps, customer name and
+ * quantity. Some of those other attributes are indexed.
+ * <p>
+ * The example simply shows the creation of a BDB environment and database,
+ * inserting some events, and retrieving the events using the Direct
+ * Persistence layer.
+ * <p>
+ * This example is meant to be paired with its twin, EventExample.java.
+ * EventExample.java and EventExampleDPL.java perform the same functionality,
+ * but use the Base API and the Direct Persistence Layer API, respectively.
+ * This may be a useful way to compare the two APIs.
+ * <p>
+ * To run the example:
+ * <pre>
+ * javac EventExampleDPL.java
+ * java EventExampleDPL -h <environmentDirectory>
+ * </pre>
+ */
+public class EventExampleDPL {
+
+ /*
+ * The Event class embodies our example event and is the application
+ * data. The @Entity annotation indicates that this class defines the
+ * objects stored in a BDB database.
+ */
+ @Entity
+ static class Event {
+
+ @PrimaryKey
+ private Date time;
+
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private int price;
+
+ private Set<String> accountReps;
+
+ private String customerName;
+ private int quantity;
+
+ Event(Date time,
+ int price,
+ String customerName) {
+
+ this.time = time;
+ this.price = price;
+ this.customerName = customerName;
+ this.accountReps = new HashSet<String>();
+ }
+
+ private Event() {} // For deserialization
+
+ void addRep(String rep) {
+ accountReps.add(rep);
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append("time=").append(time);
+ sb.append(" price=").append(price);
+ sb.append(" customerName=").append(customerName);
+ sb.append(" reps=");
+ if (accountReps.size() == 0) {
+ sb.append("none");
+ } else {
+ for (String rep: accountReps) {
+ sb.append(rep).append(" ");
+ }
+ }
+ return sb.toString();
+ }
+ }
+
+ /* A BDB environment is roughly equivalent to a relational database. */
+ private Environment env;
+ private EntityStore store;
+
+ /*
+ * Event accessors let us access events by the primary index (time)
+ * as well as by the rep and price fields
+ */
+ PrimaryIndex<Date,Event> eventByTime;
+ SecondaryIndex<Integer,Date,Event> eventByPrice;
+
+ /* Used for generating example data. */
+ private Calendar cal;
+
+ /*
+ * First manually make a directory to house the BDB environment.
+ * Usage: java EventExampleDPL -h <envHome>
+ * All BDB on-disk storage is held within envHome.
+ */
+ public static void main(String[] args)
+ throws DatabaseException, FileNotFoundException {
+
+ if (args.length != 2 || !"-h".equals(args[0])) {
+ System.err.println
+ ("Usage: java " + EventExampleDPL.class.getName() +
+ " -h <envHome>");
+ System.exit(2);
+ }
+ EventExampleDPL example = new EventExampleDPL(new File(args[1]));
+ example.run();
+ example.close();
+ }
+
+ private EventExampleDPL(File envHome)
+ throws DatabaseException, FileNotFoundException {
+
+ /* Open a transactional Berkeley DB engine environment. */
+ System.out.println("-> Creating a BDB environment");
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ envConfig.setTransactional(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(envHome, envConfig);
+
+ /* Initialize the data access object. */
+ init();
+ cal = Calendar.getInstance();
+ }
+
+ /**
+ * Create all primary and secondary indices.
+ */
+ private void init()
+ throws DatabaseException {
+
+ /* Open a transactional entity store. */
+ System.out.println("-> Creating a BDB database");
+ StoreConfig storeConfig = new StoreConfig();
+ storeConfig.setAllowCreate(true);
+ storeConfig.setTransactional(true);
+ store = new EntityStore(env, "ExampleStore", storeConfig);
+
+ eventByTime = store.getPrimaryIndex(Date.class, Event.class);
+ eventByPrice = store.getSecondaryIndex(eventByTime,
+ Integer.class,
+ "price");
+ }
+
+ private void run()
+ throws DatabaseException {
+
+ Random rand = new Random();
+
+ /*
+ * Create a set of events. Each insertion is a separate, auto-commit
+ * transaction.
+ */
+ System.out.println("-> Inserting 4 events");
+ eventByTime.put(new Event(makeDate(1), 100, "Company_A"));
+ eventByTime.put(new Event(makeDate(2), 2, "Company_B"));
+ eventByTime.put(new Event(makeDate(3), 20, "Company_C"));
+ eventByTime.put(new Event(makeDate(4), 40, "CompanyD"));
+
+ /* Load a whole set of events transactionally. */
+ Transaction txn = env.beginTransaction(null, null);
+ int maxPrice = 50;
+ System.out.println("-> Inserting some randomly generated events");
+ for (int i = 0; i < 25; i++) {
+ Event e = new Event(makeDate(rand.nextInt(365)),
+ rand.nextInt(maxPrice),
+ "Company_X");
+ if ((i%2) ==0) {
+ e.addRep("Bob");
+ e.addRep("Nikunj");
+ } else {
+ e.addRep("Yongmin");
+ }
+ eventByTime.put(e);
+ }
+ txn.commitWriteNoSync();
+
+ /*
+ * Windows of events - display the events between June 1 and Aug 31
+ */
+ System.out.println("\n-> Display the events between June 1 and Aug 31");
+ Date startDate = makeDate(Calendar.JUNE, 1);
+ Date endDate = makeDate(Calendar.AUGUST, 31);
+
+ EntityCursor<Event> eventWindow =
+ eventByTime.entities(startDate, true, endDate, true);
+ printEvents(eventWindow);
+
+ /*
+ * Display all events, ordered by a secondary index on price.
+ */
+ System.out.println("\n-> Display all events, ordered by price");
+ EntityCursor<Event> byPriceEvents = eventByPrice.entities();
+ printEvents(byPriceEvents);
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ env.close();
+ }
+
+ /**
+ * Print all events covered by this cursor.
+ */
+ private void printEvents(EntityCursor<Event> eCursor)
+ throws DatabaseException {
+ try {
+ for (Event e: eCursor) {
+ System.out.println(e);
+ }
+ } finally {
+ /* Be sure to close the cursor. */
+ eCursor.close();
+ }
+ }
+
+ /**
+ * Little utility for making up java.util.Dates for different days, just
+ * to generate test data.
+ */
+ private Date makeDate(int day) {
+
+ cal.set((Calendar.DAY_OF_YEAR), day);
+ return cal.getTime();
+ }
+
+ /**
+ * Little utility for making up java.util.Dates for different days, just
+ * to make the test data easier to read.
+ */
+ private Date makeDate(int month, int day) {
+
+ cal.set((Calendar.MONTH), month);
+ cal.set((Calendar.DAY_OF_MONTH), day);
+ return cal.getTime();
+ }
+}
diff --git a/examples_java/src/persist/PersonExample.java b/examples_java/src/persist/PersonExample.java
new file mode 100644
index 0000000..484f1e7
--- /dev/null
+++ b/examples_java/src/persist/PersonExample.java
@@ -0,0 +1,256 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2002-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.HashSet;
+import java.util.Set;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+import com.sleepycat.persist.EntityCursor;
+import com.sleepycat.persist.EntityIndex;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.StoreConfig;
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.Persistent;
+import com.sleepycat.persist.model.PrimaryKey;
+import com.sleepycat.persist.model.SecondaryKey;
+import static com.sleepycat.persist.model.DeleteAction.NULLIFY;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE;
+import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
+import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE;
+import static com.sleepycat.persist.model.Relationship.MANY_TO_MANY;
+
+public class PersonExample {
+
+ /* An entity class. */
+ @Entity
+ static class Person {
+
+ @PrimaryKey
+ String ssn;
+
+ String name;
+ Address address;
+
+ @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Person.class)
+ String parentSsn;
+
+ @SecondaryKey(relate=ONE_TO_MANY)
+ Set<String> emailAddresses = new HashSet<String>();
+
+ @SecondaryKey(relate=MANY_TO_MANY,
+ relatedEntity=Employer.class,
+ onRelatedEntityDelete=NULLIFY)
+ Set<Long> employerIds = new HashSet<Long>();
+
+ Person(String name, String ssn, String parentSsn) {
+ this.name = name;
+ this.ssn = ssn;
+ this.parentSsn = parentSsn;
+ }
+
+ private Person() {} // For deserialization
+ }
+
+ /* Another entity class. */
+ @Entity
+ static class Employer {
+
+ @PrimaryKey(sequence="ID")
+ long id;
+
+ @SecondaryKey(relate=ONE_TO_ONE)
+ String name;
+
+ Address address;
+
+ Employer(String name) {
+ this.name = name;
+ }
+
+ private Employer() {} // For deserialization
+ }
+
+ /* A persistent class used in other classes. */
+ @Persistent
+ static class Address {
+ String street;
+ String city;
+ String state;
+ int zipCode;
+ private Address() {} // For deserialization
+ }
+
+ /* The data accessor class for the entity model. */
+ static class PersonAccessor {
+
+ /* Person accessors */
+ PrimaryIndex<String,Person> personBySsn;
+ SecondaryIndex<String,String,Person> personByParentSsn;
+ SecondaryIndex<String,String,Person> personByEmailAddresses;
+ SecondaryIndex<Long,String,Person> personByEmployerIds;
+
+ /* Employer accessors */
+ PrimaryIndex<Long,Employer> employerById;
+ SecondaryIndex<String,Long,Employer> employerByName;
+
+ /* Opens all primary and secondary indices. */
+ public PersonAccessor(EntityStore store)
+ throws DatabaseException {
+
+ personBySsn = store.getPrimaryIndex(
+ String.class, Person.class);
+
+ personByParentSsn = store.getSecondaryIndex(
+ personBySsn, String.class, "parentSsn");
+
+ personByEmailAddresses = store.getSecondaryIndex(
+ personBySsn, String.class, "emailAddresses");
+
+ personByEmployerIds = store.getSecondaryIndex(
+ personBySsn, Long.class, "employerIds");
+
+ employerById = store.getPrimaryIndex(
+ Long.class, Employer.class);
+
+ employerByName = store.getSecondaryIndex(
+ employerById, String.class, "name");
+ }
+ }
+
+ public static void main(String[] args)
+ throws DatabaseException, FileNotFoundException {
+
+ if (args.length != 2 || !"-h".equals(args[0])) {
+ System.err.println
+ ("Usage: java " + PersonExample.class.getName() +
+ " -h <envHome>");
+ System.exit(2);
+ }
+ PersonExample example = new PersonExample(new File(args[1]));
+ example.run();
+ example.close();
+ }
+
+ private Environment env;
+ private EntityStore store;
+ private PersonAccessor dao;
+
+ private PersonExample(File envHome)
+ throws DatabaseException, FileNotFoundException {
+
+ /* Open a transactional Berkeley DB engine environment. */
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ envConfig.setAllowCreate(true);
+ envConfig.setTransactional(true);
+ envConfig.setInitializeCache(true);
+ envConfig.setInitializeLocking(true);
+ env = new Environment(envHome, envConfig);
+
+ /* Open a transactional entity store. */
+ StoreConfig storeConfig = new StoreConfig();
+ storeConfig.setAllowCreate(true);
+ storeConfig.setTransactional(true);
+ store = new EntityStore(env, "PersonStore", storeConfig);
+
+ /* Initialize the data access object. */
+ dao = new PersonAccessor(store);
+ }
+
+ private void run()
+ throws DatabaseException {
+
+ /*
+ * Add a parent and two children using the Person primary index.
+ * Specifying a non-null parentSsn adds the child Person to the
+ * sub-index of children for that parent key.
+ */
+ dao.personBySsn.put
+ (new Person("Bob Smith", "111-11-1111", null));
+ dao.personBySsn.put
+ (new Person("Mary Smith", "333-33-3333", "111-11-1111"));
+ dao.personBySsn.put
+ (new Person("Jack Smith", "222-22-2222", "111-11-1111"));
+
+ /* Print the children of a parent using a sub-index and a cursor. */
+ EntityCursor<Person> children =
+ dao.personByParentSsn.subIndex("111-11-1111").entities();
+ try {
+ for (Person child : children) {
+ System.out.println(child.ssn + ' ' + child.name);
+ }
+ } finally {
+ children.close();
+ }
+
+ /* Get Bob by primary key using the primary index. */
+ Person bob = dao.personBySsn.get("111-11-1111");
+ assert bob != null;
+
+ /*
+ * Create two employers if they do not already exist. Their primary
+ * keys are assigned from a sequence.
+ */
+ Employer gizmoInc = dao.employerByName.get("Gizmo Inc");
+ if (gizmoInc == null) {
+ gizmoInc = new Employer("Gizmo Inc");
+ dao.employerById.put(gizmoInc);
+ }
+ Employer gadgetInc = dao.employerByName.get("Gadget Inc");
+ if (gadgetInc == null) {
+ gadgetInc = new Employer("Gadget Inc");
+ dao.employerById.put(gadgetInc);
+ }
+
+ /* Bob has two jobs and two email addresses. */
+ bob.employerIds.add(gizmoInc.id);
+ bob.employerIds.add(gadgetInc.id);
+ bob.emailAddresses.add("bob@bob.com");
+ bob.emailAddresses.add("bob@gmail.com");
+
+ /* Update Bob's record. */
+ dao.personBySsn.put(bob);
+
+ /* Bob can now be found by both email addresses. */
+ bob = dao.personByEmailAddresses.get("bob@bob.com");
+ assert bob != null;
+ bob = dao.personByEmailAddresses.get("bob@gmail.com");
+ assert bob != null;
+
+ /* Bob can also be found as an employee of both employers. */
+ EntityIndex<String,Person> employees;
+ employees = dao.personByEmployerIds.subIndex(gizmoInc.id);
+ assert employees.contains("111-11-1111");
+ employees = dao.personByEmployerIds.subIndex(gadgetInc.id);
+ assert employees.contains("111-11-1111");
+
+ /*
+ * When an employer is deleted, the onRelatedEntityDelete=NULLIFY for
+ * the employerIds key causes the deleted ID to be removed from Bob's
+ * employerIds.
+ */
+ dao.employerById.delete(gizmoInc.id);
+ bob = dao.personBySsn.get("111-11-1111");
+ assert bob != null;
+ assert !bob.employerIds.contains(gizmoInc.id);
+ }
+
+ private void close()
+ throws DatabaseException {
+
+ store.close();
+ env.close();
+ }
+}
diff --git a/examples_java/src/persist/gettingStarted/SimpleDA.java b/examples_java/src/persist/gettingStarted/SimpleDA.java
new file mode 100644
index 0000000..ab4c6e9
--- /dev/null
+++ b/examples_java/src/persist/gettingStarted/SimpleDA.java
@@ -0,0 +1,51 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2008-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist.gettingStarted;
+
+import java.io.File;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+import com.sleepycat.persist.SecondaryIndex;
+import com.sleepycat.persist.EntityCursor;
+
+public class SimpleDA {
+ // Open the indices
+ public SimpleDA(EntityStore store)
+ throws DatabaseException {
+
+ // Primary key for SimpleEntityClass classes
+ pIdx = store.getPrimaryIndex(
+ String.class, SimpleEntityClass.class);
+
+ // Secondary key for SimpleEntityClass classes
+ // Last field in the getSecondaryIndex() method must be
+ // the name of a class member; in this case, an
+ // SimpleEntityClass.class data member.
+ sIdx = store.getSecondaryIndex(
+ pIdx, String.class, "sKey");
+
+ sec_pcursor = pIdx.entities();
+ sec_scursor = sIdx.subIndex("skeyone").entities();
+ }
+
+ public void close()
+ throws DatabaseException {
+ sec_pcursor.close();
+ sec_scursor.close();
+ }
+
+ // Index Accessors
+ PrimaryIndex<String,SimpleEntityClass> pIdx;
+ SecondaryIndex<String,String,SimpleEntityClass> sIdx;
+
+ EntityCursor<SimpleEntityClass> sec_pcursor;
+ EntityCursor<SimpleEntityClass> sec_scursor;
+}
diff --git a/examples_java/src/persist/gettingStarted/SimpleEntityClass.java b/examples_java/src/persist/gettingStarted/SimpleEntityClass.java
new file mode 100644
index 0000000..c4c0d81
--- /dev/null
+++ b/examples_java/src/persist/gettingStarted/SimpleEntityClass.java
@@ -0,0 +1,42 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2008-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist.gettingStarted;
+
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.PrimaryKey;
+import static com.sleepycat.persist.model.Relationship.*;
+import com.sleepycat.persist.model.SecondaryKey;
+
+@Entity
+public class SimpleEntityClass {
+
+ // Primary key is pKey
+ @PrimaryKey
+ private String pKey;
+
+ // Secondary key is the sKey
+ @SecondaryKey(relate=MANY_TO_ONE)
+ private String sKey;
+
+ public void setpKey(String data) {
+ pKey = data;
+ }
+
+ public void setsKey(String data) {
+ sKey = data;
+ }
+
+ public String getpKey() {
+ return pKey;
+ }
+
+ public String getsKey() {
+ return sKey;
+ }
+}
diff --git a/examples_java/src/persist/gettingStarted/SimpleStoreGet.java b/examples_java/src/persist/gettingStarted/SimpleStoreGet.java
new file mode 100644
index 0000000..44ac2dc
--- /dev/null
+++ b/examples_java/src/persist/gettingStarted/SimpleStoreGet.java
@@ -0,0 +1,112 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2008-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist.gettingStarted;
+
+import java.io.File;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.StoreConfig;
+
+import java.io.FileNotFoundException;
+
+public class SimpleStoreGet {
+
+ private static File envHome = new File("./JEDB");
+
+ private Environment envmnt;
+ private EntityStore store;
+ private SimpleDA sda;
+
+ // The setup() method opens the environment and store
+ // for us.
+ public void setup()
+ throws DatabaseException {
+
+ try {
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ StoreConfig storeConfig = new StoreConfig();
+
+ // Open the environment and entity store
+ envmnt = new Environment(envHome, envConfig);
+ store = new EntityStore(envmnt, "EntityStore", storeConfig);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("setup(): " + fnfe.toString());
+ System.exit(-1);
+ }
+ }
+
+ public void shutdown()
+ throws DatabaseException {
+
+ store.close();
+ envmnt.close();
+ }
+
+
+ private void run()
+ throws DatabaseException {
+
+ setup();
+
+ // Open the data accessor. This is used to store
+ // persistent objects.
+ sda = new SimpleDA(store);
+
+ // Instantiate and store some entity classes
+ SimpleEntityClass sec1 = sda.pIdx.get("keyone");
+ SimpleEntityClass sec2 = sda.pIdx.get("keytwo");
+
+ SimpleEntityClass sec4 = sda.sIdx.get("skeythree");
+
+ System.out.println("sec1: " + sec1.getpKey());
+ System.out.println("sec2: " + sec2.getpKey());
+ System.out.println("sec4: " + sec4.getpKey());
+
+ System.out.println("############ Doing pcursor ##########");
+ for (SimpleEntityClass seci : sda.sec_pcursor ) {
+ System.out.println("sec from pcursor : " + seci.getpKey() );
+ }
+
+ sda.pIdx.delete("keyone");
+ System.out.println("############ Doing pcursor ##########");
+ System.out.println("sec from pcursor : " + sda.sec_pcursor.first().getpKey());
+ for (SimpleEntityClass seci : sda.sec_pcursor ) {
+ System.out.println("sec from pcursor : " + seci.getpKey() );
+ }
+
+ System.out.println("############ Doing scursor ##########");
+ for (SimpleEntityClass seci : sda.sec_scursor ) {
+ System.out.println("sec from scursor : " + seci.getpKey() );
+ }
+
+
+
+ sda.close();
+ shutdown();
+ }
+
+ public static void main(String args[]) {
+ SimpleStoreGet ssg = new SimpleStoreGet();
+ try {
+ ssg.run();
+ } catch (DatabaseException dbe) {
+ System.err.println("SimpleStoreGet: " + dbe.toString());
+ dbe.printStackTrace();
+ } catch (Exception e) {
+ System.out.println("Exception: " + e.toString());
+ e.printStackTrace();
+ }
+ System.out.println("All done.");
+ }
+
+}
diff --git a/examples_java/src/persist/gettingStarted/SimpleStorePut.java b/examples_java/src/persist/gettingStarted/SimpleStorePut.java
new file mode 100644
index 0000000..0e48a5b
--- /dev/null
+++ b/examples_java/src/persist/gettingStarted/SimpleStorePut.java
@@ -0,0 +1,116 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2008-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist.gettingStarted;
+
+import java.io.File;
+
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.StoreConfig;
+
+import java.io.FileNotFoundException;
+
+public class SimpleStorePut {
+
+ private static File envHome = new File("./JEDB");
+
+ private Environment envmnt;
+ private EntityStore store;
+ private SimpleDA sda;
+
+ // The setup() method opens the environment and store
+ // for us.
+ public void setup()
+ throws DatabaseException {
+
+ EnvironmentConfig envConfig = new EnvironmentConfig();
+ StoreConfig storeConfig = new StoreConfig();
+
+ envConfig.setAllowCreate(true);
+ storeConfig.setAllowCreate(true);
+
+ try {
+ // Open the environment and entity store
+ envmnt = new Environment(envHome, envConfig);
+ store = new EntityStore(envmnt, "EntityStore", storeConfig);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("setup(): " + fnfe.toString());
+ System.exit(-1);
+ }
+ }
+
+ // Close our environment and store.
+ public void shutdown()
+ throws DatabaseException {
+
+ store.close();
+ envmnt.close();
+ }
+
+
+ private void run()
+ throws DatabaseException {
+
+ setup();
+
+ // Open the data accessor. This is used to store
+ // persistent objects.
+ sda = new SimpleDA(store);
+
+ // Instantiate and store some entity classes
+ SimpleEntityClass sec1 = new SimpleEntityClass();
+ SimpleEntityClass sec2 = new SimpleEntityClass();
+ SimpleEntityClass sec3 = new SimpleEntityClass();
+ SimpleEntityClass sec4 = new SimpleEntityClass();
+ SimpleEntityClass sec5 = new SimpleEntityClass();
+
+ sec1.setpKey("keyone");
+ sec1.setsKey("skeyone");
+
+ sec2.setpKey("keytwo");
+ sec2.setsKey("skeyone");
+
+ sec3.setpKey("keythree");
+ sec3.setsKey("skeytwo");
+
+ sec4.setpKey("keyfour");
+ sec4.setsKey("skeythree");
+
+ sec5.setpKey("keyfive");
+ sec5.setsKey("skeyfour");
+
+ sda.pIdx.put(sec1);
+ sda.pIdx.put(sec2);
+ sda.pIdx.put(sec3);
+ sda.pIdx.put(sec4);
+ sda.pIdx.put(sec5);
+
+ sda.close();
+
+ shutdown();
+ }
+
+ public static void main(String args[]) {
+ SimpleStorePut ssp = new SimpleStorePut();
+ try {
+ ssp.run();
+ } catch (DatabaseException dbe) {
+ System.err.println("SimpleStorePut: " + dbe.toString());
+ dbe.printStackTrace();
+ } catch (Exception e) {
+ System.out.println("Exception: " + e.toString());
+ e.printStackTrace();
+ }
+ System.out.println("All done.");
+ }
+
+}
diff --git a/examples_java/src/persist/txn/PayloadDataEntity.java b/examples_java/src/persist/txn/PayloadDataEntity.java
new file mode 100644
index 0000000..9d464ff
--- /dev/null
+++ b/examples_java/src/persist/txn/PayloadDataEntity.java
@@ -0,0 +1,33 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2008-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist.txn;
+
+import com.sleepycat.persist.model.Entity;
+import com.sleepycat.persist.model.PrimaryKey;
+import static com.sleepycat.persist.model.Relationship.*;
+
+@Entity
+public class PayloadDataEntity {
+ @PrimaryKey
+ private int oID;
+
+ private String threadName;
+
+ private double doubleData;
+
+ PayloadDataEntity() {}
+
+ public double getDoubleData() { return doubleData; }
+ public int getID() { return oID; }
+ public String getThreadName() { return threadName; }
+
+ public void setDoubleData(double dd) { doubleData = dd; }
+ public void setID(int id) { oID = id; }
+ public void setThreadName(String tn) { threadName = tn; }
+}
diff --git a/examples_java/src/persist/txn/StoreWriter.java b/examples_java/src/persist/txn/StoreWriter.java
new file mode 100644
index 0000000..fb8b4e5
--- /dev/null
+++ b/examples_java/src/persist/txn/StoreWriter.java
@@ -0,0 +1,176 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2008-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+package persist.txn;
+
+import com.sleepycat.db.CursorConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DeadlockException;
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.Transaction;
+
+import com.sleepycat.persist.EntityCursor;
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.PrimaryIndex;
+
+import java.util.Iterator;
+import java.util.Random;
+import java.io.UnsupportedEncodingException;
+
+public class StoreWriter extends Thread
+{
+ private EntityStore myStore = null;
+ private Environment myEnv = null;
+ private PrimaryIndex<Integer,PayloadDataEntity> pdKey;
+ private Random generator = new Random();
+ private boolean passTxn = false;
+
+
+ private static final int MAX_RETRY = 20;
+
+ // Constructor. Get our handles from here
+ StoreWriter(Environment env, EntityStore store)
+
+ throws DatabaseException {
+ myStore = store;
+ myEnv = env;
+
+ // Open the data accessor. This is used to store persistent
+ // objects.
+ pdKey = myStore.getPrimaryIndex(Integer.class,
+ PayloadDataEntity.class);
+ }
+
+ // Thread method that writes a series of objects
+ // to the store using transaction protection.
+ // Deadlock handling is demonstrated here.
+ public void run () {
+ Transaction txn = null;
+
+ // Perform 50 transactions
+ for (int i=0; i<50; i++) {
+
+ boolean retry = true;
+ int retry_count = 0;
+ // while loop is used for deadlock retries
+ while (retry) {
+ // try block used for deadlock detection and
+ // general exception handling
+ try {
+
+ // Get a transaction
+ txn = myEnv.beginTransaction(null, null);
+
+ // Write 10 PayloadDataEntity objects to the
+ // store for each transaction
+ for (int j = 0; j < 10; j++) {
+ // Instantiate an object
+ PayloadDataEntity pd = new PayloadDataEntity();
+
+ // Set the Object ID. This is used as the primary key.
+ pd.setID(i + j);
+
+ // The thread name is used as a secondary key, and
+ // it is retrieved by this class's getName() method.
+ pd.setThreadName(getName());
+
+ // The last bit of data that we use is a double
+ // that we generate randomly. This data is not
+ // indexed.
+ pd.setDoubleData(generator.nextDouble());
+
+ // Do the put
+ pdKey.put(txn, pd);
+ }
+
+ // commit
+ System.out.println(getName() + " : committing txn : " + i);
+ System.out.println(getName() + " : Found " +
+ countObjects(txn) + " objects in the store.");
+ try {
+ txn.commit();
+ txn = null;
+ } catch (DatabaseException e) {
+ System.err.println("Error on txn commit: " +
+ e.toString());
+ }
+ retry = false;
+
+ } catch (DeadlockException de) {
+ System.out.println("################# " + getName() +
+ " : caught deadlock");
+ // retry if necessary
+ if (retry_count < MAX_RETRY) {
+ System.err.println(getName() +
+ " : Retrying operation.");
+ retry = true;
+ retry_count++;
+ } else {
+ System.err.println(getName() +
+ " : out of retries. Giving up.");
+ retry = false;
+ }
+ } catch (DatabaseException e) {
+ // abort and don't retry
+ retry = false;
+ System.err.println(getName() +
+ " : caught exception: " + e.toString());
+ System.err.println(getName() +
+ " : errno: " + e.getErrno());
+ e.printStackTrace();
+ } finally {
+ if (txn != null) {
+ try {
+ txn.abort();
+ } catch (Exception e) {
+ System.err.println("Error aborting transaction: " +
+ e.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+ }
+ }
+ }
+
+ // This simply counts the number of objects contained in the
+ // store and returns the result. You can use this method
+ // in three ways:
+ //
+ // First call it with an active txn handle.
+ //
+ // Secondly, configure the cursor for dirty reads
+ //
+ // Third, call countObjects AFTER the writer has committed
+ // its transaction.
+ //
+ // If you do none of these things, the writer thread will
+ // self-deadlock.
+ private int countObjects(Transaction txn) throws DatabaseException {
+ int count = 0;
+
+ CursorConfig cc = new CursorConfig();
+ // This is ignored if the store is not opened with uncommitted read
+ // support.
+ cc.setReadUncommitted(true);
+ EntityCursor<PayloadDataEntity> cursor = pdKey.entities(txn, cc);
+
+ try {
+ for (PayloadDataEntity pdi : cursor) {
+ count++;
+ }
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+
+ return count;
+
+ }
+}
diff --git a/examples_java/src/persist/txn/TxnGuideDPL.java b/examples_java/src/persist/txn/TxnGuideDPL.java
new file mode 100644
index 0000000..d429507
--- /dev/null
+++ b/examples_java/src/persist/txn/TxnGuideDPL.java
@@ -0,0 +1,162 @@
+/*-
+ * See the file LICENSE for redistribution information.
+ *
+ * Copyright (c) 2008-2009 Oracle. All rights reserved.
+ *
+ * $Id$
+ */
+
+// File TxnGuideDPL.java
+
+package persist.txn;
+
+import com.sleepycat.db.DatabaseConfig;
+import com.sleepycat.db.DatabaseException;
+import com.sleepycat.db.DatabaseType;
+import com.sleepycat.db.LockDetectMode;
+
+import com.sleepycat.db.Environment;
+import com.sleepycat.db.EnvironmentConfig;
+
+import com.sleepycat.persist.EntityStore;
+import com.sleepycat.persist.StoreConfig;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+
+public class TxnGuideDPL {
+
+ private static String myEnvPath = "./";
+ private static String storeName = "exampleStore";
+
+ // Handles
+ private static EntityStore myStore = null;
+ private static Environment myEnv = null;
+
+ private static final int NUMTHREADS = 5;
+
+ private static void usage() {
+ System.out.println("TxnGuideDPL [-h <env directory>]");
+ System.exit(-1);
+ }
+
+ public static void main(String args[]) {
+ try {
+ // Parse the arguments list
+ parseArgs(args);
+ // Open the environment and store
+ openEnv();
+
+ // Start the threads
+ StoreWriter[] threadArray;
+ threadArray = new StoreWriter[NUMTHREADS];
+ for (int i = 0; i < NUMTHREADS; i++) {
+ threadArray[i] = new StoreWriter(myEnv, myStore);
+ threadArray[i].start();
+ }
+
+ for (int i = 0; i < NUMTHREADS; i++) {
+ threadArray[i].join();
+ }
+ } catch (Exception e) {
+ System.err.println("TxnGuideDPL: " + e.toString());
+ e.printStackTrace();
+ } finally {
+ closeEnv();
+ }
+ System.out.println("All done.");
+ }
+
+
+ private static void openEnv() throws DatabaseException {
+ System.out.println("opening env and store");
+
+ // Set up the environment.
+ EnvironmentConfig myEnvConfig = new EnvironmentConfig();
+ myEnvConfig.setAllowCreate(true);
+ myEnvConfig.setInitializeCache(true);
+ myEnvConfig.setInitializeLocking(true);
+ myEnvConfig.setInitializeLogging(true);
+ myEnvConfig.setRunRecovery(true);
+ myEnvConfig.setTransactional(true);
+ // EnvironmentConfig.setThreaded(true) is the default behavior
+ // in Java, so we do not have to do anything to cause the
+ // environment handle to be free-threaded.
+
+ // Indicate that we want db to internally perform deadlock
+ // detection. Also indicate that the transaction that has
+ // performed the least amount of write activity to
+ // receive the deadlock notification, if any.
+ myEnvConfig.setLockDetectMode(LockDetectMode.MINWRITE);
+
+ // Set up the entity store
+ StoreConfig myStoreConfig = new StoreConfig();
+ myStoreConfig.setAllowCreate(true);
+ myStoreConfig.setTransactional(true);
+
+ // Need a DatabaseConfig object so as to set uncommitted read
+ // support.
+ DatabaseConfig myDbConfig = new DatabaseConfig();
+ myDbConfig.setType(DatabaseType.BTREE);
+ myDbConfig.setAllowCreate(true);
+ myDbConfig.setTransactional(true);
+ myDbConfig.setReadUncommitted(true);
+
+ try {
+ // Open the environment
+ myEnv = new Environment(new File(myEnvPath), // Env home
+ myEnvConfig);
+
+ // Open the store
+ myStore = new EntityStore(myEnv, storeName, myStoreConfig);
+
+ // Set the DatabaseConfig object, so that the underlying
+ // database is configured for uncommitted reads.
+ myStore.setPrimaryConfig(PayloadDataEntity.class, myDbConfig);
+ } catch (FileNotFoundException fnfe) {
+ System.err.println("openEnv: " + fnfe.toString());
+ System.exit(-1);
+ }
+ }
+
+ private static void closeEnv() {
+ System.out.println("Closing env and store");
+ if (myStore != null ) {
+ try {
+ myStore.close();
+ } catch (DatabaseException e) {
+ System.err.println("closeEnv: myStore: " +
+ e.toString());
+ e.printStackTrace();
+ }
+ }
+
+ if (myEnv != null ) {
+ try {
+ myEnv.close();
+ } catch (DatabaseException e) {
+ System.err.println("closeEnv: " + e.toString());
+ e.printStackTrace();
+ }
+ }
+ }
+
+ private TxnGuideDPL() {}
+
+ private static void parseArgs(String args[]) {
+ int nArgs = args.length;
+ for(int i = 0; i < args.length; ++i) {
+ if (args[i].startsWith("-")) {
+ switch(args[i].charAt(1)) {
+ case 'h':
+ if (i < nArgs - 1) {
+ myEnvPath = new String(args[++i]);
+ }
+ break;
+ default:
+ usage();
+ }
+ }
+ }
+ }
+}