1/*-
2 * See the file LICENSE for redistribution information.
3 *
4 * Copyright (c) 2002,2008 Oracle.  All rights reserved.
5 *
6 * $Id: SampleDatabase.java,v 12.8 2008/02/07 17:12:23 mark Exp $
7 */
8
9package collections.ship.tuple;
10
11import java.io.File;
12import java.io.FileNotFoundException;
13
14import com.sleepycat.bind.serial.ClassCatalog;
15import com.sleepycat.bind.serial.StoredClassCatalog;
16import com.sleepycat.bind.serial.TupleSerialKeyCreator;
17import com.sleepycat.bind.tuple.TupleInput;
18import com.sleepycat.bind.tuple.TupleOutput;
19import com.sleepycat.db.Database;
20import com.sleepycat.db.DatabaseConfig;
21import com.sleepycat.db.DatabaseException;
22import com.sleepycat.db.DatabaseType;
23import com.sleepycat.db.Environment;
24import com.sleepycat.db.EnvironmentConfig;
25import com.sleepycat.db.ForeignKeyDeleteAction;
26import com.sleepycat.db.SecondaryConfig;
27import com.sleepycat.db.SecondaryDatabase;
28
29/**
30 * SampleDatabase defines the storage containers, indices and foreign keys
31 * for the sample database.
32 *
33 * @author Mark Hayes
34 */
35public class SampleDatabase {
36
37    private static final String CLASS_CATALOG = "java_class_catalog";
38    private static final String SUPPLIER_STORE = "supplier_store";
39    private static final String PART_STORE = "part_store";
40    private static final String SHIPMENT_STORE = "shipment_store";
41    private static final String SHIPMENT_PART_INDEX = "shipment_part_index";
42    private static final String SHIPMENT_SUPPLIER_INDEX =
43	"shipment_supplier_index";
44    private static final String SUPPLIER_CITY_INDEX = "supplier_city_index";
45
46    private Environment env;
47    private Database partDb;
48    private Database supplierDb;
49    private Database shipmentDb;
50    private SecondaryDatabase supplierByCityDb;
51    private SecondaryDatabase shipmentByPartDb;
52    private SecondaryDatabase shipmentBySupplierDb;
53    private StoredClassCatalog javaCatalog;
54
55    /**
56     * Open all storage containers, indices, and catalogs.
57     */
58    public SampleDatabase(String homeDirectory)
59        throws DatabaseException, FileNotFoundException {
60
61        // Open the Berkeley DB environment in transactional mode.
62        //
63        System.out.println("Opening environment in: " + homeDirectory);
64        EnvironmentConfig envConfig = new EnvironmentConfig();
65        envConfig.setTransactional(true);
66        envConfig.setAllowCreate(true);
67        envConfig.setInitializeCache(true);
68        envConfig.setInitializeLocking(true);
69        env = new Environment(new File(homeDirectory), envConfig);
70
71        // Set the Berkeley DB config for opening all stores.
72        //
73        DatabaseConfig dbConfig = new DatabaseConfig();
74        dbConfig.setTransactional(true);
75        dbConfig.setAllowCreate(true);
76        dbConfig.setType(DatabaseType.BTREE);
77
78        // Create the Serial class catalog.  This holds the serialized class
79        // format for all database records of serial format.
80        //
81        Database catalogDb = env.openDatabase(null, CLASS_CATALOG, null,
82                                              dbConfig);
83        javaCatalog = new StoredClassCatalog(catalogDb);
84
85        // Open the Berkeley DB database for the part, supplier and shipment
86        // stores.  The stores are opened with no duplicate keys allowed.
87        //
88        partDb = env.openDatabase(null, PART_STORE, null, dbConfig);
89
90        supplierDb = env.openDatabase(null, SUPPLIER_STORE, null, dbConfig);
91
92        shipmentDb = env.openDatabase(null, SHIPMENT_STORE, null, dbConfig);
93
94        // Open the SecondaryDatabase for the city index of the supplier store,
95        // and for the part and supplier indices of the shipment store.
96        // Duplicate keys are allowed since more than one supplier may be in
97        // the same city, and more than one shipment may exist for the same
98        // supplier or part.  A foreign key constraint is defined for the
99        // supplier and part indices to ensure that a shipment only refers to
100        // existing part and supplier keys.  The CASCADE delete action means
101        // that shipments will be deleted if their associated part or supplier
102        // is deleted.
103        //
104        SecondaryConfig secConfig = new SecondaryConfig();
105        secConfig.setTransactional(true);
106        secConfig.setAllowCreate(true);
107        secConfig.setType(DatabaseType.BTREE);
108        secConfig.setSortedDuplicates(true);
109
110        secConfig.setKeyCreator(new SupplierByCityKeyCreator(javaCatalog,
111                                                     SupplierData.class));
112        supplierByCityDb = env.openSecondaryDatabase(null, SUPPLIER_CITY_INDEX,
113                                                     null, supplierDb,
114                                                     secConfig);
115
116        secConfig.setForeignKeyDatabase(partDb);
117        secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
118        secConfig.setKeyCreator(new ShipmentByPartKeyCreator(javaCatalog,
119                                                     ShipmentData.class));
120        shipmentByPartDb = env.openSecondaryDatabase(null, SHIPMENT_PART_INDEX,
121                                                     null, shipmentDb,
122                                                     secConfig);
123
124        secConfig.setForeignKeyDatabase(supplierDb);
125        secConfig.setForeignKeyDeleteAction(ForeignKeyDeleteAction.CASCADE);
126        secConfig.setKeyCreator(new ShipmentBySupplierKeyCreator(javaCatalog,
127                                                     ShipmentData.class));
128        shipmentBySupplierDb = env.openSecondaryDatabase(null,
129                                                     SHIPMENT_SUPPLIER_INDEX,
130                                                     null, shipmentDb,
131                                                     secConfig);
132    }
133
134    /**
135     * Return the storage environment for the database.
136     */
137    public final Environment getEnvironment() {
138
139        return env;
140    }
141
142    /**
143     * Return the class catalog.
144     */
145    public final StoredClassCatalog getClassCatalog() {
146
147        return javaCatalog;
148    }
149
150    /**
151     * Return the part storage container.
152     */
153    public final Database getPartDatabase() {
154
155        return partDb;
156    }
157
158    /**
159     * Return the supplier storage container.
160     */
161    public final Database getSupplierDatabase() {
162
163        return supplierDb;
164    }
165
166    /**
167     * Return the shipment storage container.
168     */
169    public final Database getShipmentDatabase() {
170
171        return shipmentDb;
172    }
173
174    /**
175     * Return the shipment-by-part index.
176     */
177    public final SecondaryDatabase getShipmentByPartDatabase() {
178
179        return shipmentByPartDb;
180    }
181
182    /**
183     * Return the shipment-by-supplier index.
184     */
185    public final SecondaryDatabase getShipmentBySupplierDatabase() {
186
187        return shipmentBySupplierDb;
188    }
189
190    /**
191     * Return the supplier-by-city index.
192     */
193    public final SecondaryDatabase getSupplierByCityDatabase() {
194
195        return supplierByCityDb;
196    }
197
198    /**
199     * Close all stores (closing a store automatically closes its indices).
200     */
201    public void close()
202        throws DatabaseException {
203
204        // Close secondary databases, then primary databases.
205        supplierByCityDb.close();
206        shipmentByPartDb.close();
207        shipmentBySupplierDb.close();
208        partDb.close();
209        supplierDb.close();
210        shipmentDb.close();
211        // And don't forget to close the catalog and the environment.
212        javaCatalog.close();
213        env.close();
214    }
215
216    /**
217     * The SecondaryKeyCreator for the SupplierByCity index.  This is an
218     * extension of the abstract class TupleSerialKeyCreator, which implements
219     * SecondaryKeyCreator for the case where the data keys are of the format
220     * TupleFormat and the data values are of the format SerialFormat.
221     */
222    private static class SupplierByCityKeyCreator
223        extends TupleSerialKeyCreator {
224
225        /**
226         * Construct the city key extractor.
227         * @param catalog is the class catalog.
228         * @param valueClass is the supplier value class.
229         */
230        private SupplierByCityKeyCreator(ClassCatalog catalog,
231                                         Class valueClass) {
232
233            super(catalog, valueClass);
234        }
235
236        /**
237         * Extract the city key from a supplier key/value pair.  The city key
238         * is stored in the supplier value, so the supplier key is not used.
239         */
240        public boolean createSecondaryKey(TupleInput primaryKeyInput,
241                                          Object valueInput,
242                                          TupleOutput indexKeyOutput) {
243
244            SupplierData supplierData = (SupplierData) valueInput;
245            String city = supplierData.getCity();
246            if (city != null) {
247                indexKeyOutput.writeString(supplierData.getCity());
248                return true;
249            } else {
250                return false;
251            }
252        }
253    }
254
255    /**
256     * The SecondaryKeyCreator for the ShipmentByPart index.  This is an
257     * extension of the abstract class TupleSerialKeyCreator, which implements
258     * SecondaryKeyCreator for the case where the data keys are of the format
259     * TupleFormat and the data values are of the format SerialFormat.
260     */
261    private static class ShipmentByPartKeyCreator
262        extends TupleSerialKeyCreator {
263
264        /**
265         * Construct the part key extractor.
266         * @param catalog is the class catalog.
267         * @param valueClass is the shipment value class.
268         */
269        private ShipmentByPartKeyCreator(ClassCatalog catalog,
270                                         Class valueClass) {
271            super(catalog, valueClass);
272        }
273
274        /**
275         * Extract the part key from a shipment key/value pair.  The part key
276         * is stored in the shipment key, so the shipment value is not used.
277         */
278        public boolean createSecondaryKey(TupleInput primaryKeyInput,
279                                          Object valueInput,
280                                          TupleOutput indexKeyOutput) {
281
282            String partNumber = primaryKeyInput.readString();
283            // don't bother reading the supplierNumber
284            indexKeyOutput.writeString(partNumber);
285            return true;
286        }
287    }
288
289    /**
290     * The SecondaryKeyCreator for the ShipmentBySupplier index.  This is an
291     * extension of the abstract class TupleSerialKeyCreator, which implements
292     * SecondaryKeyCreator for the case where the data keys are of the format
293     * TupleFormat and the data values are of the format SerialFormat.
294     */
295    private static class ShipmentBySupplierKeyCreator
296        extends TupleSerialKeyCreator {
297
298        /**
299         * Construct the supplier key extractor.
300         * @param catalog is the class catalog.
301         * @param valueClass is the shipment value class.
302         */
303        private ShipmentBySupplierKeyCreator(ClassCatalog catalog,
304                                             Class valueClass) {
305            super(catalog, valueClass);
306        }
307
308        /**
309         * Extract the supplier key from a shipment key/value pair.  The
310         * supplier key is stored in the shipment key, so the shipment value is
311         * not used.
312         */
313        public boolean createSecondaryKey(TupleInput primaryKeyInput,
314                                          Object valueInput,
315                                          TupleOutput indexKeyOutput) {
316
317            primaryKeyInput.readString(); // skip the partNumber
318            String supplierNumber = primaryKeyInput.readString();
319            indexKeyOutput.writeString(supplierNumber);
320            return true;
321        }
322    }
323}
324