1<?xml version="1.0" encoding="UTF-8" standalone="no"?> 2<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 3<html xmlns="http://www.w3.org/1999/xhtml"> 4 <head> 5 <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> 6 <title>Program Listing</title> 7 <link rel="stylesheet" href="gettingStarted.css" type="text/css" /> 8 <meta name="generator" content="DocBook XSL Stylesheets V1.73.2" /> 9 <link rel="start" href="index.html" title="Getting Started with Replicated Berkeley DB Applications" /> 10 <link rel="up" href="txnapp.html" title="Chapter 2. Transactional Application" /> 11 <link rel="prev" href="txnapp.html" title="Chapter 2. Transactional Application" /> 12 <link rel="next" href="repapp.html" title="Chapter 3. The DB Replication Manager" /> 13 </head> 14 <body> 15 <div class="navheader"> 16 <table width="100%" summary="Navigation header"> 17 <tr> 18 <th colspan="3" align="center">Program Listing</th> 19 </tr> 20 <tr> 21 <td width="20%" align="left"><a accesskey="p" href="txnapp.html">Prev</a> </td> 22 <th width="60%" align="center">Chapter 2. Transactional Application</th> 23 <td width="20%" align="right"> <a accesskey="n" href="repapp.html">Next</a></td> 24 </tr> 25 </table> 26 <hr /> 27 </div> 28 <div class="sect1" lang="en" xml:lang="en"> 29 <div class="titlepage"> 30 <div> 31 <div> 32 <h2 class="title" style="clear: both"><a id="simpleprogramlisting"></a>Program Listing</h2> 33 </div> 34 </div> 35 </div> 36 <div class="toc"> 37 <dl> 38 <dt> 39 <span class="sect2"> 40 <a href="simpleprogramlisting.html#repconfiginfo_cxx"> 41 42 <span>Class: RepConfig</span> 43 </a> 44 </span> 45 </dt> 46 <dt> 47 <span class="sect2"> 48 <a href="simpleprogramlisting.html#simpletxnusage_java">Class: SimpleTxn</a> 49 </span> 50 </dt> 51 <dt> 52 <span class="sect2"> 53 <a href="simpleprogramlisting.html#simpletxnmain_java">Method: SimpleTxn.main()</a> 54 </span> 55 </dt> 56 <dt> 57 <span class="sect2"> 58 <a href="simpleprogramlisting.html#simpletxn_init_java">Method: SimpleTxn.init()</a> 59 </span> 60 </dt> 61 <dt> 62 <span class="sect2"> 63 <a href="simpleprogramlisting.html#doloop_java">Method: SimpleTxn.doloop()</a> 64 </span> 65 </dt> 66 <dt> 67 <span class="sect2"> 68 <a href="simpleprogramlisting.html#printstocks_c"> 69 70 71 <span>Method: SimpleTxn.printStocks()</span> 72 </a> 73 </span> 74 </dt> 75 </dl> 76 </div> 77 <p> 78 Our example program is a fairly simple transactional 79 application. At this early stage of its development, the 80 application contains no hint that it must be network-aware 81 so the only command line argument that it takes is one that 82 allows us to specify the environment home directory. 83 (Eventually, we will specify things like host names and 84 ports from the command line). 85 </p> 86 <p> 87 Note that the application performs all writes under the 88 protection of a transaction; however, multiple database 89 operations are not performed per transaction. Consequently, 90 we simplify things a bit by using autocommit for our 91 database writes. 92 </p> 93 <p> 94 Also, this application is single-threaded. It is possible 95 to write a multi-threaded or multi-process application that 96 performs replication. That said, the concepts described in 97 this book are applicable to both single threaded and 98 multi-threaded applications so nothing 99 is gained by multi-threading this application other than 100 distracting complexity. This manual 101 does, however, identify where care must be taken when 102 performing replication with a non-single threaded 103 application. 104 </p> 105 <p> 106 Finally, remember that transaction processing is not described in 107 this manual. Rather, see the 108 <em class="citetitle">Berkeley DB Getting Started with Transaction Processing</em> guide for details on 109 that topic. 110 </p> 111 <div class="sect2" lang="en" xml:lang="en"> 112 <div class="titlepage"> 113 <div> 114 <div> 115 <h3 class="title"><a id="repconfiginfo_cxx"></a> 116 117 <span>Class: RepConfig</span> 118 </h3> 119 </div> 120 </div> 121 </div> 122 <p> 123 Before we begin, we present a 124 class that we will use to maintain useful 125 information for us. Under normal circumstances, 126 this class would not be necessary for a simple 127 transactional example such as this. However, this code will 128 grow into a replicated example that needs to 129 track a lot more information for the 130 application, and so we lay the groundwork for 131 it here. 132 </p> 133 <p> 134 The class that we create is called 135 136 <code class="classname">RepConfig</code> 137 and its only purpose at this time is to track 138 the location of our environment home directory. 139 </p> 140 <pre class="programlisting">package db.repquote_gsg; 141 142public class RepConfig 143{ 144 // Constant values used in the RepQuote application. 145 public static final String progname = "SimpleTxn"; 146 public static final int CACHESIZE = 10 * 1024 * 1024; 147 148 // member variables containing configuration information 149 public String home; // String specifying the home directory for 150 // rep files. 151 152 public RepConfig() 153 { 154 home = "TESTDIR"; 155 } 156 157 public java.io.File getHome() 158 { 159 return new java.io.File(home); 160 } 161 162} </pre> 163 </div> 164 <div class="sect2" lang="en" xml:lang="en"> 165 <div class="titlepage"> 166 <div> 167 <div> 168 <h3 class="title"><a id="simpletxnusage_java"></a>Class: SimpleTxn</h3> 169 </div> 170 </div> 171 </div> 172 <p> 173 Our transactional example will 174 consist of a class, 175 <code class="classname">SimpleTxn</code>, that performs 176 all our work for us. 177 </p> 178 <p> 179 First, we provide the package declaration and 180 then a few import statements that the class 181 needs. 182 </p> 183 <pre class="programlisting">package db.repquote_gsg; 184 185import java.io.FileNotFoundException; 186import java.io.BufferedReader; 187import java.io.InputStreamReader; 188import java.io.IOException; 189import java.io.UnsupportedEncodingException; 190 191import com.sleepycat.db.Cursor; 192import com.sleepycat.db.Database; 193import com.sleepycat.db.DatabaseConfig; 194import com.sleepycat.db.DatabaseEntry; 195import com.sleepycat.db.DatabaseException; 196import com.sleepycat.db.DatabaseType; 197import com.sleepycat.db.Environment; 198import com.sleepycat.db.EnvironmentConfig; 199import com.sleepycat.db.LockMode; 200import com.sleepycat.db.OperationStatus; 201import db.repquote_gsg.RepConfig; 202 203public class SimpleTxn 204{ 205 private RepConfig repConfig; 206 private Environment dbenv; </pre> 207 <p> 208 Next, we provide our class constructor. This simply initializes our 209 class data members. 210</p> 211 <pre class="programlisting"> public SimpleTxn() 212 throws DatabaseException 213 { 214 repConfig = null; 215 dbenv = null; 216 } </pre> 217 <p> 218 And then we provide our <code class="methodname">usage()</code> method. At 219 this point, this method has very little to report: 220</p> 221 <pre class="programlisting"> public static void usage() 222 { 223 System.err.println("usage: " + repConfig.progname); 224 System.err.println("-h home"); 225 226 System.err.println("\t -h home directory\n"); 227 228 System.exit(1); 229 } </pre> 230 </div> 231 <div class="sect2" lang="en" xml:lang="en"> 232 <div class="titlepage"> 233 <div> 234 <div> 235 <h3 class="title"><a id="simpletxnmain_java"></a>Method: SimpleTxn.main()</h3> 236 </div> 237 </div> 238 </div> 239 <p> 240 Having implemented our 241 <code class="methodname">usage()</code> 242 method, we can jump directly into our 243 <code class="methodname">main()</code> 244 method. This method begins by instantiating a 245 <code class="classname">RepConfig</code> object, and 246 then collecting the command line arguments so 247 that it can populate the object with the 248 appropriate data (just the environment home 249 directory, at this time): 250 </p> 251 <pre class="programlisting"> public static void main(String[] argv) 252 throws Exception 253 { 254 RepConfig config = new RepConfig(); 255 // Extract the command line parameters 256 for (int i = 0; i < argv.length; i++) 257 { 258 if (argv[i].compareTo("-h") == 0) { 259 // home - a string arg. 260 i++; 261 config.home = argv[i]; 262 } else { 263 System.err.println("Unrecognized option: " + argv[i]); 264 usage(); 265 } 266 } </pre> 267 <p> 268 And then perform a little sanity checking on the command line 269 input: 270</p> 271 <pre class="programlisting"> // Error check command line. 272 if (config.home.length() == 0) 273 usage(); </pre> 274 <p> 275 Now we perform the class' work. To begin, we initialize the 276 object. The <code class="methodname">init()</code> method actually 277 opens our environment for us (shown in the next section). 278 </p> 279 <pre class="programlisting"> SimpleTxn runner = null; 280 try { 281 runner = new SimpleTxn(); 282 runner.init(config); </pre> 283 <p> 284 And then we call our <code class="methodname">doloop()</code> 285 method. This method is where we perform all our database 286 activity. See <a class="xref" href="simpleprogramlisting.html#doloop_java" title="Method: SimpleTxn.doloop()">Method: SimpleTxn.doloop()</a> 287 for it's details. 288 </p> 289 <pre class="programlisting"> runner.doloop(); </pre> 290 <p> 291 And then, finally terminate the application (which closes our 292 environment handle) and end the method. 293 </p> 294 <pre class="programlisting"> runner.terminate(); 295 } catch (DatabaseException dbe) { 296 System.err.println("Caught an exception during " + 297 "initialization or processing: " + dbe.toString()); 298 if (runner != null) 299 runner.terminate(); 300 } 301 System.exit(0); 302 } // end main </pre> 303 </div> 304 <div class="sect2" lang="en" xml:lang="en"> 305 <div class="titlepage"> 306 <div> 307 <div> 308 <h3 class="title"><a id="simpletxn_init_java"></a>Method: SimpleTxn.init()</h3> 309 </div> 310 </div> 311 </div> 312 <p> 313 The <code class="methodname">SimpleTxn.init()</code> 314 method is used to open our environment handle. 315 For readers familiar with writing transactional 316 DB applications, there should be no surprises 317 here. However, we will be adding to this in later 318 chapters as we roll replication into this example. 319 </p> 320 <p> 321 The only thing worth noting in this method here is that 322 we relax our transactional durability guarantee for this application. 323 We do this because the application will eventually be replicated and 324 so we don't need a high durability guarantee. 325</p> 326 <pre class="programlisting"> public int init(RepConfig config) 327 throws DatabaseException 328 { 329 int ret = 0; 330 repConfig = config; 331 EnvironmentConfig envConfig = new EnvironmentConfig(); 332 envConfig.setErrorStream(System.err); 333 envConfig.setErrorPrefix(RepConfig.progname); 334 335 envConfig.setCacheSize(RepConfig.CACHESIZE); 336 envConfig.setTxnNoSync(true); 337 338 envConfig.setAllowCreate(true); 339 envConfig.setRunRecovery(true); 340 envConfig.setInitializeLocking(true); 341 envConfig.setInitializeLogging(true); 342 envConfig.setInitializeCache(true); 343 envConfig.setTransactional(true); 344 try { 345 dbenv = new Environment(repConfig.getHome(), envConfig); 346 } catch(FileNotFoundException e) { 347 System.err.println("FileNotFound exception: " + e.toString()); 348 System.err.println( 349 "Ensure that the environment directory is pre-created."); 350 ret = 1; 351 } 352 353 return ret; 354 } </pre> 355 <p> 356 Finally, we present the <code class="methodname">SimpleTxn.terminate()</code> 357 method here. All this does is close the environment handle. Again, 358 there should be no surprises here, but we provide the 359 implementation for the sake of completeness anyway. 360</p> 361 <pre class="programlisting"> public void terminate() 362 throws DatabaseException 363 { 364 dbenv.close(); 365 } </pre> 366 </div> 367 <div class="sect2" lang="en" xml:lang="en"> 368 <div class="titlepage"> 369 <div> 370 <div> 371 <h3 class="title"><a id="doloop_java"></a>Method: SimpleTxn.doloop()</h3> 372 </div> 373 </div> 374 </div> 375 <p> 376 We now implement our application's 377 primary data processing method. This 378 method provides a command prompt at which the 379 user can enter a stock ticker value and a price for 380 that value. This information is then entered to the 381 database. 382 </p> 383 <p> 384 To display the database, simply enter 385 <code class="literal">return</code> at the prompt. 386 </p> 387 <p> 388 To begin, we declare a database pointer: 389 </p> 390 <pre class="programlisting"> public int doloop() 391 throws DatabaseException, , UnsupportedEncodingException 392 { 393 Database db = null; </pre> 394 <p> 395 Next, we begin the loop and we immediately open our 396 database if it has not already been opened. 397 </p> 398 <pre class="programlisting"> for (;;) 399 { 400 if (db == null) { 401 DatabaseConfig dbconf = new DatabaseConfig(); 402 dbconf.setType(DatabaseType.BTREE); 403 dbconf.setAllowCreate(true); 404 dbconf.setTransactional(true); 405 406 try { 407 db = dbenv.openDatabase(null, // Txn handle 408 RepConfig.progname, // db filename 409 null, // db name 410 dbconf); 411 } catch (FileNotFoundException fnfe) { 412 System.err.println("File not found exception" + fnfe.toString()); 413 // Get here only if the environment home directory 414 // somehow does not exist. 415 } 416 } </pre> 417 <p> 418 Now we implement our command prompt. This is a simple and not 419 very robust implementation of a command prompt. 420 If the user enters the keywords <code class="literal">exit</code> 421 or <code class="literal">quit</code>, the loop is exited and the 422 application ends. If the user enters nothing and instead simply 423 presses <code class="literal">return</code>, the entire contents of the 424 database is displayed. We use our 425 <code class="methodname">printStocks()</code> method to display the 426 database. (That implementation is shown next in this chapter.) 427 </p> 428 <p> 429 Notice that very little error checking is performed on the data 430 entered at this prompt. If the user fails to enter at least one 431 space in the value string, a simple help message is printed and 432 the prompt is returned to the user. That is the only error 433 checking performed here. In a real-world application, 434 at a minimum the application would probably check to ensure 435 that the price was in fact an integer or float value. 436 However, in order to keep this example code as simple as 437 possible, we refrain from implementing a thorough user interface. 438 </p> 439 <pre class="programlisting"> BufferedReader stdin = 440 new BufferedReader(new InputStreamReader(System.in)); 441 442 // listen for input, and add it to the database. 443 System.out.print("QUOTESERVER> "); 444 System.out.flush(); 445 String nextline = null; 446 try { 447 nextline = stdin.readLine(); 448 } catch (IOException ioe) { 449 System.err.println("Unable to get data from stdin"); 450 break; 451 } 452 String[] words = nextline.split("\\s"); 453 454 // A blank line causes the DB to be dumped to stdout. 455 if (words.length == 0 || 456 (words.length == 1 && words[0].length() == 0)) { 457 try { 458 printStocks(db); 459 } catch (DatabaseException e) { 460 System.err.println("Got db exception reading " + 461 "DB: " + e.toString()); 462 break; 463 } 464 continue; 465 } 466 467 if (words.length == 1 && 468 (words[0].compareToIgnoreCase("quit") == 0 || 469 words[0].compareToIgnoreCase("exit") == 0)) { 470 break; 471 } else if (words.length != 2) { 472 System.err.println("Format: TICKER VALUE"); 473 continue; 474 } </pre> 475 <p> 476 Now we assign data to the <code class="classname">DatabaseEntry</code> 477 classes that we will use to write the new information to the database. 478 </p> 479 <pre class="programlisting"> DatabaseEntry key = 480 new DatabaseEntry(words[0].getBytes("UTF-8")); 481 DatabaseEntry data = 482 new DatabaseEntry(words[1].getBytes("UTF-8")); </pre> 483 <p> 484 Having done that, we can write the new information to the 485 database. Remember that because a transaction handle is not 486 explicitly used, but we did open the database such that it 487 supports transactions, then autocommit is automatically 488 used for this database write. 489 </p> 490 <p> 491 Autocommit is described in the 492 <em class="citetitle">Berkeley DB Getting Started with Transaction Processing</em> guide. 493 </p> 494 <p> 495 Also, the database is not configured for duplicate records, so 496 the data portion of a record is overwritten if the provided 497 key already exists in the database. However, in this case 498 DB returns <code class="methodname">OperationStatus.KEYEXIST</code> — which 499 we ignore. 500 </p> 501 <pre class="programlisting"> db.put(null, key, data); </pre> 502 <p> 503 Finally, we close our database before returning from the 504 method. 505 </p> 506 <pre class="programlisting"> } 507 if (db != null) 508 db.close(true); 509 return 0; 510 } </pre> 511 </div> 512 <div class="sect2" lang="en" xml:lang="en"> 513 <div class="titlepage"> 514 <div> 515 <div> 516 <h3 class="title"><a id="printstocks_c"></a> 517 518 519 <span>Method: SimpleTxn.printStocks()</span> 520 </h3> 521 </div> 522 </div> 523 </div> 524 <p> 525 The 526 <code class="methodname">printStocks()</code> 527 528 <span>method</span> 529 simply takes a database handle, opens a cursor, and uses 530 it to display all the information it finds in a database. 531 This is trivial cursor operation that should hold 532 no surprises for you. We simply provide it here for 533 the sake of completeness. 534 </p> 535 <p> 536 If you are unfamiliar with basic cursor operations, 537 please see the <em class="citetitle">Getting Started with Berkeley DB</em> 538 guide. 539 </p> 540 <pre class="programlisting"> public void terminate() 541 throws DatabaseException 542 { 543 dbenv.close(); 544 } 545 546 /* 547 * void return type since error conditions are propagated 548 * via exceptions. 549 */ 550 private void printStocks(Database db) 551 throws DatabaseException 552 { 553 Cursor dbc = db.openCursor(null, null); 554 555 System.out.println("\tSymbol\tPrice"); 556 System.out.println("\t======\t====="); 557 558 DatabaseEntry key = new DatabaseEntry(); 559 DatabaseEntry data = new DatabaseEntry(); 560 OperationStatus ret; 561 for (ret = dbc.getFirst(key, data, LockMode.DEFAULT); 562 ret == OperationStatus.SUCCESS; 563 ret = dbc.getNext(key, data, LockMode.DEFAULT)) { 564 String keystr = new String 565 (key.getData(), key.getOffset(), key.getSize()); 566 String datastr = new String 567 (data.getData(), data.getOffset(), data.getSize()); 568 System.out.println("\t"+keystr+"\t"+datastr); 569 570 } 571 dbc.close(); 572 } 573} // end class </pre> 574 </div> 575 </div> 576 <div class="navfooter"> 577 <hr /> 578 <table width="100%" summary="Navigation footer"> 579 <tr> 580 <td width="40%" align="left"><a accesskey="p" href="txnapp.html">Prev</a> </td> 581 <td width="20%" align="center"> 582 <a accesskey="u" href="txnapp.html">Up</a> 583 </td> 584 <td width="40%" align="right"> <a accesskey="n" href="repapp.html">Next</a></td> 585 </tr> 586 <tr> 587 <td width="40%" align="left" valign="top">Chapter 2. Transactional Application </td> 588 <td width="20%" align="center"> 589 <a accesskey="h" href="index.html">Home</a> 590 </td> 591 <td width="40%" align="right" valign="top"> Chapter 3. The DB Replication Manager</td> 592 </tr> 593 </table> 594 </div> 595 </body> 596</html> 597