diff options
Diffstat (limited to 'examples_java/src/collections/ship')
78 files changed, 8842 insertions, 0 deletions
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 + ']'; + } +} |