KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > persist > EventExample


1 /*-
2  * See the file LICENSE for redistribution information.
3  *
4  * Copyright (c) 2004,2006 Oracle. All rights reserved.
5  *
6  * $Id: EventExample.java,v 1.1 2006/11/02 16:34:58 linda Exp $
7  */

8
9 package persist;
10
11 import java.io.File JavaDoc;
12 import java.io.Serializable JavaDoc;
13 import java.util.Calendar JavaDoc;
14 import java.util.Date JavaDoc;
15 import java.util.HashSet JavaDoc;
16 import java.util.Random JavaDoc;
17 import java.util.Set JavaDoc;
18
19 import com.sleepycat.bind.EntryBinding;
20 import com.sleepycat.bind.serial.SerialBinding;
21 import com.sleepycat.bind.serial.StoredClassCatalog;
22 import com.sleepycat.bind.tuple.IntegerBinding;
23 import com.sleepycat.bind.tuple.LongBinding;
24 import com.sleepycat.je.Cursor;
25 import com.sleepycat.je.Database;
26 import com.sleepycat.je.DatabaseConfig;
27 import com.sleepycat.je.DatabaseEntry;
28 import com.sleepycat.je.DatabaseException;
29 import com.sleepycat.je.Environment;
30 import com.sleepycat.je.EnvironmentConfig;
31 import com.sleepycat.je.OperationStatus;
32 import com.sleepycat.je.SecondaryConfig;
33 import com.sleepycat.je.SecondaryCursor;
34 import com.sleepycat.je.SecondaryDatabase;
35 import com.sleepycat.je.SecondaryKeyCreator;
36 import com.sleepycat.je.Transaction;
37
38 /**
39  * EventExample is a trivial example which stores Java objects that represent
40  * an event. Events are primarily indexed by a timestamp, but have other
41  * attributes, such as price, account reps, customer name and quantity.
42  * Some of those other attributes are indexed.
43  * <p>
44  * The example simply shows the creation of a JE environment and database,
45  * inserting some events, and retrieving the events.
46  * <p>
47  * This example is meant to be paired with its twin, EventExampleDPL.java.
48  * EventExample.java and EventExampleDPL.java perform the same functionality,
49  * but use the Base API and the Direct Persistence Layer api, respectively.
50  * This may be a useful way to compare the two apis.
51  * <p>
52  * To run the example:
53  * <pre>
54  * cd jehome/examples
55  * javac je/EventExample.java
56  * java -cp "../lib/je.jar;." je.EventExample -h <environmentDirectory>
57  * </pre>
58  */

59 public class EventExample {
60
61     /*
62      * The Event class embodies our example event and is the application
63      * data. JE data records are represented at key/data tuples. In this
64      * example, the key portion of the record is the event time, and the data
65      * portion is the Event instance.
66      */

67     static class Event implements Serializable JavaDoc {
68
69         /* This example will add secondary indices on price and accountReps. */
70         private int price;
71         private Set JavaDoc<String JavaDoc> accountReps;
72
73         private String JavaDoc customerName;
74         private int quantity;
75
76         Event(int price,
77               String JavaDoc customerName) {
78
79             this.price = price;
80             this.customerName = customerName;
81             this.accountReps = new HashSet JavaDoc<String JavaDoc>();
82         }
83
84         void addRep(String JavaDoc rep) {
85             accountReps.add(rep);
86         }
87
88         @Override JavaDoc
89         public String JavaDoc toString() {
90             StringBuilder JavaDoc sb = new StringBuilder JavaDoc();
91             sb.append(" price=").append(price);
92             sb.append(" customerName=").append(customerName);
93             sb.append(" reps=");
94             if (accountReps.size() == 0) {
95                 sb.append("none");
96             } else {
97                 for (String JavaDoc rep: accountReps) {
98                     sb.append(rep).append(" ");
99                 }
100             }
101             return sb.toString();
102         }
103
104         int getPrice() {
105             return price;
106         }
107     }
108
109     /* A JE environment is roughly equivalent to a relational database. */
110     private Environment env;
111
112     /*
113      * A JE table is roughly equivalent to a relational table with a
114      * primary index.
115      */

116     private Database eventDb;
117
118     /* A secondary database indexes an additional field of the data record */
119     private SecondaryDatabase eventByPriceDb;
120
121     /*
122      * The catalogs and bindings are used to convert Java objects to the byte
123      * array format used by JE key/data in the base api. The Direct Persistence
124      * Layer api supports Java objects as arguments directly.
125      */

126     private Database catalogDb;
127     private EntryBinding eventBinding;
128
129     /* Used for generating example data. */
130     private Calendar JavaDoc cal;
131
132
133     /*
134      * First manually make a directory to house the JE environment.
135      * Usage: java -cp je.jar EventExample -h <envHome>
136      * All JE on-disk storage is held within envHome.
137      */

138     public static void main(String JavaDoc[] args)
139         throws DatabaseException {
140
141         if (args.length != 2 || !"-h".equals(args[0])) {
142             System.err.println
143                 ("Usage: java " + EventExample.class.getName() +
144                  " -h <envHome>");
145             System.exit(2);
146         }
147         EventExample example = new EventExample(new File JavaDoc(args[1]));
148         example.run();
149         example.close();
150     }
151
152     private EventExample(File JavaDoc envHome)
153         throws DatabaseException {
154
155         /* Open a transactional Berkeley DB engine environment. */
156         System.out.println("-> Creating a JE environment");
157         EnvironmentConfig envConfig = new EnvironmentConfig();
158         envConfig.setAllowCreate(true);
159         envConfig.setTransactional(true);
160         env = new Environment(envHome, envConfig);
161
162         init();
163         cal = Calendar.getInstance();
164     }
165
166     /**
167      * Create all primary and secondary indices.
168      */

169     private void init()
170         throws DatabaseException {
171
172         System.out.println("-> Creating a JE database");
173         DatabaseConfig dbConfig = new DatabaseConfig();
174         dbConfig.setTransactional(true);
175         dbConfig.setAllowCreate(true);
176         eventDb = env.openDatabase(null, // use auto-commit txn
177
"eventDb", // database name
178
dbConfig);
179
180
181         /*
182          * In our example, the database record is composed of a key portion
183          * which represents the event timestamp, and a data portion holds an
184          * instance of the Event class.
185          *
186          * JE's base api accepts and returns key and data as byte arrays, so we
187          * need some support for marshaling between objects and byte arrays. We
188          * call this binding, and supply a package of helper classes to support
189          * this. It's entirely possible to do all binding on your own.
190          *
191          * A class catalog database is needed for storing class descriptions
192          * for the serial binding used below. This avoids storing class
193          * descriptions redundantly in each record.
194          */

195         DatabaseConfig catalogConfig = new DatabaseConfig();
196         catalogConfig.setTransactional(true);
197         catalogConfig.setAllowCreate(true);
198         catalogDb = env.openDatabase(null, "catalogDb", catalogConfig);
199         StoredClassCatalog catalog = new StoredClassCatalog(catalogDb);
200
201         /*
202          * Create a serial binding for Event data objects. Serial
203          * bindings can be used to store any Serializable object.
204          * We can use some pre-defined binding classes to convert
205          * primitives like the long key value to the a byte array.
206          */

207         eventBinding = new SerialBinding(catalog, Event.class);
208
209         /*
210          * Open a secondary database to allow accessing the primary
211          * database a secondary key value. In this case, access events
212          * by price.
213          */

214         SecondaryConfig secConfig = new SecondaryConfig();
215         secConfig.setTransactional(true);
216         secConfig.setAllowCreate(true);
217         secConfig.setSortedDuplicates(true);
218         secConfig.setKeyCreator(new PriceKeyCreator(eventBinding));
219         eventByPriceDb = env.openSecondaryDatabase(null,
220                                                    "priceDb",
221                                                    eventDb,
222                                                    secConfig);
223
224     }
225
226     private void run()
227         throws DatabaseException {
228
229         Random JavaDoc rand = new Random JavaDoc();
230
231         /* DatabaseEntry represents the key and data of each record */
232         DatabaseEntry key = new DatabaseEntry();
233         DatabaseEntry data = new DatabaseEntry();
234
235         /*
236          * Create a set of events. Each insertion is a separate, auto-commit
237          * transaction.
238          */

239         System.out.println("-> Inserting 4 events");
240         LongBinding.longToEntry(makeDate(1), key);
241         eventBinding.objectToEntry(new Event(100, "Company_A"),
242                                    data);
243         eventDb.put(null, key, data);
244
245         LongBinding.longToEntry(makeDate(2), key);
246         eventBinding.objectToEntry(new Event(2, "Company_B"),
247                                    data);
248         eventDb.put(null, key, data);
249
250         LongBinding.longToEntry(makeDate(3), key);
251         eventBinding.objectToEntry(new Event(20, "Company_C"),
252                                    data);
253         eventDb.put(null, key, data);
254
255         LongBinding.longToEntry(makeDate(4), key);
256         eventBinding.objectToEntry(new Event(40, "CompanyD"),
257                                    data);
258         eventDb.put(null, key, data);
259
260         /* Load a whole set of events transactionally. */
261         Transaction txn = env.beginTransaction(null, null);
262         int maxPrice = 50;
263         System.out.println("-> Inserting some randomly generated events");
264         for (int i = 0; i < 25; i++) {
265             long time = makeDate(rand.nextInt(365));
266             Event e = new Event(rand.nextInt(maxPrice),"Company_X");
267             if ((i%2) ==0) {
268                 e.addRep("Jane");
269                 e.addRep("Nikunj");
270             } else {
271                 e.addRep("Yongmin");
272             }
273             LongBinding.longToEntry(time, key);
274             eventBinding.objectToEntry(e, data);
275             eventDb.put(txn, key, data);
276         }
277         txn.commitWriteNoSync();
278
279         /*
280          * Windows of events - display the events between June 1 and Aug 31
281          */

282         System.out.println("\n-> Display the events between June 1 and Aug 31");
283         long startDate = makeDate(Calendar.JUNE, 31);
284         long endDate = makeDate(Calendar.AUGUST, 31);
285
286         /* Position the cursor and print the first event. */
287         Cursor eventWindow = eventDb.openCursor(null, null);
288         LongBinding.longToEntry(makeDate(Calendar.JUNE, 1), key);
289
290         if ((eventWindow.getSearchKeyRange(key, data, null)) !=
291             OperationStatus.SUCCESS) {
292             System.out.println("No events found!");
293             eventWindow.close();
294             return;
295         }
296         try {
297             printEvents(key, data, eventWindow, endDate);
298         } finally {
299             eventWindow.close();
300         }
301         
302         /*
303          * Display all events, ordered by a secondary index on price.
304          */

305         System.out.println("\n-> Display all events, ordered by price");
306         SecondaryCursor priceCursor =
307             eventByPriceDb.openSecondaryCursor(null, null);
308         try {
309             printEvents(priceCursor);
310         } finally {
311             priceCursor.close();
312         }
313     }
314
315     private void close()
316         throws DatabaseException {
317
318         eventByPriceDb.close();
319         eventDb.close();
320         catalogDb.close();
321         env.close();
322     }
323     
324     /**
325      * Print all events covered by this cursor up to the end date. We know
326      * that the cursor operates on long keys and Event data items, but there's
327      * no type-safe way of expressing that within the JE base api.
328      */

329     private void printEvents(DatabaseEntry firstKey,
330                              DatabaseEntry firstData,
331                              Cursor cursor,
332                              long endDate)
333         throws DatabaseException {
334         
335         System.out.println("time=" +
336                            new Date JavaDoc(LongBinding.entryToLong(firstKey)) +
337                            eventBinding.entryToObject(firstData));
338         DatabaseEntry key = new DatabaseEntry();
339         DatabaseEntry data = new DatabaseEntry();
340
341         while (cursor.getNext(key, data, null) ==
342                OperationStatus.SUCCESS) {
343             if (LongBinding.entryToLong(key) > endDate) {
344                 break;
345             }
346             System.out.println("time=" +
347                                new Date JavaDoc(LongBinding.entryToLong(key)) +
348                                eventBinding.entryToObject(data));
349         }
350     }
351
352     private void printEvents(SecondaryCursor cursor)
353         throws DatabaseException {
354         DatabaseEntry timeKey = new DatabaseEntry();
355         DatabaseEntry priceKey = new DatabaseEntry();
356         DatabaseEntry eventData = new DatabaseEntry();
357
358         while (cursor.getNext(priceKey, timeKey, eventData, null) ==
359                OperationStatus.SUCCESS) {
360             System.out.println("time=" +
361                                new Date JavaDoc(LongBinding.entryToLong(timeKey)) +
362                                eventBinding.entryToObject(eventData));
363         }
364     }
365
366     /**
367      * Little utility for making up java.util.Dates for different days, just
368      * to generate test data.
369      */

370     private long makeDate(int day) {
371
372         cal.set((Calendar.DAY_OF_YEAR), day);
373         return cal.getTime().getTime();
374     }
375     /**
376      * Little utility for making up java.util.Dates for different days, just
377      * to make the test data easier to read.
378      */

379     private long makeDate(int month, int day) {
380
381         cal.set((Calendar.MONTH), month);
382         cal.set((Calendar.DAY_OF_MONTH), day);
383         return cal.getTime().getTime();
384     }
385
386     /**
387      * A key creator that knows how to extract the secondary key from the data
388      * entry of the primary database. To do so, it uses both the dataBinding
389      * of the primary database and the secKeyBinding.
390      */

391     private static class PriceKeyCreator implements SecondaryKeyCreator {
392
393         private EntryBinding dataBinding;
394
395         PriceKeyCreator(EntryBinding eventBinding) {
396             this.dataBinding = eventBinding;
397         }
398
399         public boolean createSecondaryKey(SecondaryDatabase secondaryDb,
400                                           DatabaseEntry keyEntry,
401                                           DatabaseEntry dataEntry,
402                                           DatabaseEntry resultEntry)
403             throws DatabaseException {
404
405             /*
406              * Convert the data entry to an Event object, extract the secondary
407              * key value from it, and then convert it to the resulting
408              * secondary key entry.
409              */

410             Event e = (Event) dataBinding.entryToObject(dataEntry);
411             int price = e.getPrice();
412             IntegerBinding.intToEntry(price, resultEntry);
413             return true;
414         }
415     }
416 }
417
Popular Tags