diff options
Diffstat (limited to 'examples_java/src')
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(); + } + } + } + } +} |