KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > je > SecondaryExample


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2004,2006 Oracle. All rights reserved.
5  *
6  * $Id: SecondaryExample.java,v 1.20 2006/10/30 21:14:03 bostic Exp $
7  */

8
9 package je;
10
11 import java.io.File JavaDoc;
12 import java.io.Serializable JavaDoc;
13
14 import com.sleepycat.bind.EntryBinding;
15 import com.sleepycat.bind.serial.SerialBinding;
16 import com.sleepycat.bind.serial.StoredClassCatalog;
17 import com.sleepycat.bind.tuple.IntegerBinding;
18 import com.sleepycat.bind.tuple.TupleBinding;
19 import com.sleepycat.je.Cursor;
20 import com.sleepycat.je.Database;
21 import com.sleepycat.je.DatabaseConfig;
22 import com.sleepycat.je.DatabaseEntry;
23 import com.sleepycat.je.DatabaseException;
24 import com.sleepycat.je.Environment;
25 import com.sleepycat.je.EnvironmentConfig;
26 import com.sleepycat.je.LockMode;
27 import com.sleepycat.je.OperationStatus;
28 import com.sleepycat.je.SecondaryConfig;
29 import com.sleepycat.je.SecondaryDatabase;
30 import com.sleepycat.je.SecondaryKeyCreator;
31 import com.sleepycat.je.Transaction;
32
33 /**
34  * SecondaryExample operates in the same way as BindingExample, but adds a
35  * SecondaryDatabase for accessing the primary database by a secondary key.
36  */

37 class SecondaryExample {
38     private static final int EXIT_SUCCESS = 0;
39     private static final int EXIT_FAILURE = 1;
40
41     private int numRecords; // num records to insert or retrieve
42
private int offset; // where we want to start inserting
43
private boolean doInsert; // if true, insert, else retrieve
44
private File JavaDoc envDir;
45
46     public SecondaryExample(int numRecords,
47                           boolean doInsert,
48                           File JavaDoc envDir,
49                           int offset) {
50         this.numRecords = numRecords;
51         this.doInsert = doInsert;
52         this.envDir = envDir;
53         this.offset = offset;
54     }
55
56     /**
57      * Usage string
58      */

59     public static void usage() {
60         System.out.println("usage: java " +
61                            "je.SecondaryExample " +
62                            "<dbEnvHomeDirectory> " +
63                            "<insert|retrieve> <numRecords> [offset]");
64         System.exit(EXIT_FAILURE);
65     }
66
67     public static void main(String JavaDoc argv[]) {
68
69         if (argv.length < 2) {
70             usage();
71             return;
72         }
73         File JavaDoc envHomeDirectory = new File JavaDoc(argv[0]);
74
75         boolean doInsertArg = false;
76         if (argv[1].equalsIgnoreCase("insert")) {
77             doInsertArg = true;
78         } else if (argv[1].equalsIgnoreCase("retrieve")) {
79             doInsertArg = false;
80         } else {
81             usage();
82         }
83
84         int startOffset = 0;
85         int numRecordsVal = 0;
86
87         if (doInsertArg) {
88
89             if (argv.length > 2) {
90                 numRecordsVal = Integer.parseInt(argv[2]);
91             } else {
92                 usage();
93                 return;
94             }
95
96             if (argv.length > 3) {
97                 startOffset = Integer.parseInt(argv[3]);
98             }
99         }
100
101         try {
102             SecondaryExample app = new SecondaryExample(numRecordsVal,
103                                                         doInsertArg,
104                                                         envHomeDirectory,
105                                                         startOffset);
106             app.run();
107         } catch (DatabaseException e) {
108             e.printStackTrace();
109             System.exit(EXIT_FAILURE);
110         }
111         System.exit(EXIT_SUCCESS);
112     }
113
114     /**
115      * Insert or retrieve data.
116      */

117     public void run() throws DatabaseException {
118
119         /* Create a new, transactional database environment. */
120         EnvironmentConfig envConfig = new EnvironmentConfig();
121         envConfig.setTransactional(true);
122         envConfig.setAllowCreate(true);
123         Environment exampleEnv = new Environment(envDir, envConfig);
124         
125         /*
126          * Make a database within that environment. Because this will be used
127          * as a primary database, it must not allow duplicates. The primary key
128          * of a primary database must be unique.
129          */

130         Transaction txn = exampleEnv.beginTransaction(null, null);
131         DatabaseConfig dbConfig = new DatabaseConfig();
132         dbConfig.setTransactional(true);
133         dbConfig.setAllowCreate(true);
134         Database exampleDb =
135         exampleEnv.openDatabase(txn, "bindingsDb", dbConfig);
136
137         /*
138          * In our example, the database record is composed of an integer key
139          * and and instance of the MyData class as data.
140          *
141          * A class catalog database is needed for storing class descriptions
142          * for the serial binding used below. This avoids storing class
143          * descriptions redundantly in each record.
144          */

145         DatabaseConfig catalogConfig = new DatabaseConfig();
146         catalogConfig.setTransactional(true);
147         catalogConfig.setAllowCreate(true);
148         Database catalogDb =
149         exampleEnv.openDatabase(txn, "catalogDb", catalogConfig);
150         StoredClassCatalog catalog = new StoredClassCatalog(catalogDb);
151
152         /*
153          * Create a serial binding for MyData data objects. Serial
154          * bindings can be used to store any Serializable object.
155          */

156         EntryBinding dataBinding = new SerialBinding(catalog, MyData.class);
157         
158         /*
159          * Further below we'll use a tuple binding (IntegerBinding
160          * specifically) for integer keys. Tuples, unlike serialized
161          * Java objects, have a well defined sort order.
162          */

163
164         /*
165          * Define a String tuple binding for a secondary key. The
166          * secondary key is the msg field of the MyData object.
167          */

168         EntryBinding secKeyBinding =
169             TupleBinding.getPrimitiveBinding(String JavaDoc.class);
170
171         /*
172          * Open a secondary database to allow accessing the primary
173          * database by the secondary key value.
174          */

175         SecondaryConfig secConfig = new SecondaryConfig();
176         secConfig.setTransactional(true);
177         secConfig.setAllowCreate(true);
178         secConfig.setSortedDuplicates(true);
179         secConfig.setKeyCreator(new MyKeyCreator(secKeyBinding, dataBinding));
180         SecondaryDatabase exampleSecDb =
181         exampleEnv.openSecondaryDatabase(txn, "bindingsSecDb",
182                          exampleDb, secConfig);
183         txn.commit();
184
185         /* DatabaseEntry represents the key and data of each record. */
186         DatabaseEntry keyEntry = new DatabaseEntry();
187         DatabaseEntry dataEntry = new DatabaseEntry();
188
189         if (doInsert) {
190
191             /*
192              * Put some data in. Note that the primary database is always used
193              * to add data. Adding or changing data in the secondary database
194              * is not allowed; however, deleting through the secondary database
195              * is allowed.
196              */

197             for (int i = offset; i < numRecords + offset; i++) {
198                 txn = exampleEnv.beginTransaction(null, null);
199                 StringBuffer JavaDoc stars = new StringBuffer JavaDoc();
200                 for (int j = 0; j < i; j++) {
201                     stars.append('*');
202                 }
203                 MyData data = new MyData(i, stars.toString());
204
205                 IntegerBinding.intToEntry(i, keyEntry);
206                 dataBinding.objectToEntry(data, dataEntry);
207
208                 OperationStatus status =
209                     exampleDb.put(txn, keyEntry, dataEntry);
210
211                 /*
212                  * Note that put will throw a DatabaseException when error
213                  * conditions are found such as deadlock. However, the status
214                  * return conveys a variety of information. For example, the
215                  * put might succeed, or it might not succeed if the record
216                  * exists and duplicates were not
217                  */

218                 if (status != OperationStatus.SUCCESS) {
219                     throw new DatabaseException
220             ("Data insertion got status " + status);
221                 }
222                 txn.commit();
223             }
224         } else {
225             
226             /*
227          * Retrieve the data by secondary key by opening a cursor on the
228          * secondary database. The key parameter for a secondary cursor is
229          * always the secondary key, but the data parameter is always the
230          * data of the primary database. You can cast the cursor to a
231          * SecondaryCursor and use additional method signatures for
232          * retrieving the primary key also. Or you can call
233          * openSecondaryCursor() to avoid casting.
234          */

235             txn = exampleEnv.beginTransaction(null, null);
236             Cursor cursor = exampleSecDb.openCursor(txn, null);
237
238             while (cursor.getNext(keyEntry, dataEntry, LockMode.DEFAULT) ==
239                    OperationStatus.SUCCESS) {
240
241                 String JavaDoc key = (String JavaDoc) secKeyBinding.entryToObject(keyEntry);
242                 MyData data = (MyData) dataBinding.entryToObject(dataEntry);
243
244                 System.out.println("key=" + key + " data=" + data);
245             }
246             cursor.close();
247             txn.commit();
248         }
249
250         /*
251          * Always close secondary databases before closing their associated
252          * primary database.
253          */

254         catalogDb.close();
255         exampleSecDb.close();
256         exampleDb.close();
257         exampleEnv.close();
258     }
259
260     private static class MyData implements Serializable JavaDoc {
261
262         private int num;
263         private String JavaDoc msg;
264
265         MyData(int number, String JavaDoc message) {
266             this.num = number;
267             this.msg = message;
268         }
269
270         String JavaDoc getMessage() {
271             return msg;
272         }
273
274         public String JavaDoc toString() {
275             return String.valueOf(num) + ' ' + msg;
276         }
277     }
278
279     /**
280      * A key creator that knows how to extract the secondary key from the data
281      * entry of the primary database. To do so, it uses both the dataBinding
282      * of the primary database and the secKeyBinding.
283      */

284     private static class MyKeyCreator implements SecondaryKeyCreator {
285
286         private EntryBinding secKeyBinding;
287         private EntryBinding dataBinding;
288
289         MyKeyCreator(EntryBinding secKeyBinding, EntryBinding dataBinding) {
290             this.secKeyBinding = secKeyBinding;
291             this.dataBinding = dataBinding;
292         }
293
294         public boolean createSecondaryKey(SecondaryDatabase secondaryDb,
295                                           DatabaseEntry keyEntry,
296                                           DatabaseEntry dataEntry,
297                                           DatabaseEntry resultEntry)
298             throws DatabaseException {
299
300             /*
301              * Convert the data entry to a MyData object, extract the secondary
302              * key value from it, and then convert it to the resulting
303              * secondary key entry.
304              */

305             MyData data = (MyData) dataBinding.entryToObject(dataEntry);
306             String JavaDoc key = data.getMessage();
307             if (key != null) {
308                 secKeyBinding.objectToEntry(key, resultEntry);
309                 return true;
310             } else {
311
312                 /*
313                  * The message property of MyData is optional, so if it is null
314                  * then return false to prevent it from being indexed. Note
315                  * that if a required key is missing or an error occurs, an
316                  * exception should be thrown by this method.
317                  */

318                 return false;
319             }
320         }
321     }
322 }
323
Popular Tags