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 <span>Class: RepConfigInfo</span> 42 43 </a> 44 </span> 45 </dt> 46 <dt> 47 <span class="sect2"> 48 <a href="simpleprogramlisting.html#repmgr_cxx">Class: excxx_repquote_gsg_simple</a> 49 </span> 50 </dt> 51 <dt> 52 <span class="sect2"> 53 <a href="simpleprogramlisting.html#usage_cxx">Function: usage()</a> 54 </span> 55 </dt> 56 <dt> 57 <span class="sect2"> 58 <a href="simpleprogramlisting.html#main_cxx">Function: main()</a> 59 </span> 60 </dt> 61 <dt> 62 <span class="sect2"> 63 <a href="simpleprogramlisting.html#repmgr_init_cxx">Method: SimpleTxn::init()</a> 64 </span> 65 </dt> 66 <dt> 67 <span class="sect2"> 68 <a href="simpleprogramlisting.html#doloop_cxx">Method: SimpleTxn::doloop()</a> 69 </span> 70 </dt> 71 <dt> 72 <span class="sect2"> 73 <a href="simpleprogramlisting.html#printstocks_c"> 74 75 <span>Method: SimpleTxn::print_stocks()</span> 76 77 </a> 78 </span> 79 </dt> 80 </dl> 81 </div> 82 <p> 83 Our example program is a fairly simple transactional 84 application. At this early stage of its development, the 85 application contains no hint that it must be network-aware 86 so the only command line argument that it takes is one that 87 allows us to specify the environment home directory. 88 (Eventually, we will specify things like host names and 89 ports from the command line). 90 </p> 91 <p> 92 Note that the application performs all writes under the 93 protection of a transaction; however, multiple database 94 operations are not performed per transaction. Consequently, 95 we simplify things a bit by using autocommit for our 96 database writes. 97 </p> 98 <p> 99 Also, this application is single-threaded. It is possible 100 to write a multi-threaded or multi-process application that 101 performs replication. That said, the concepts described in 102 this book are applicable to both single threaded and 103 multi-threaded applications so nothing 104 is gained by multi-threading this application other than 105 distracting complexity. This manual 106 does, however, identify where care must be taken when 107 performing replication with a non-single threaded 108 application. 109 </p> 110 <p> 111 Finally, remember that transaction processing is not described in 112 this manual. Rather, see the 113 <em class="citetitle">Berkeley DB Getting Started with Transaction Processing</em> guide for details on 114 that topic. 115 </p> 116 <div class="sect2" lang="en" xml:lang="en"> 117 <div class="titlepage"> 118 <div> 119 <div> 120 <h3 class="title"><a id="repconfiginfo_cxx"></a> 121 <span>Class: RepConfigInfo</span> 122 123 </h3> 124 </div> 125 </div> 126 </div> 127 <p> 128 Before we begin, we present a 129 class that we will use to maintain useful 130 information for us. Under normal circumstances, 131 this class would not be necessary for a simple 132 transactional example such as this. However, this code will 133 grow into a replicated example that needs to 134 track a lot more information for the 135 application, and so we lay the groundwork for 136 it here. 137 </p> 138 <p> 139 The class that we create is called 140 <code class="classname">RepConfigInfo</code> 141 142 and its only purpose at this time is to track 143 the location of our environment home directory. 144 </p> 145 <pre class="programlisting">#include <db_cxx.h> 146 147class RepConfigInfo { 148public: 149 RepConfigInfo(); 150 virtual ~RepConfigInfo(); 151 152public: 153 char* home; 154}; 155 156 157RepConfigInfo::RepConfigInfo() 158{ 159 home = "TESTDIR"; 160} 161 162RepConfigInfo::~RepConfigInfo() 163{ 164} </pre> 165 </div> 166 <div class="sect2" lang="en" xml:lang="en"> 167 <div class="titlepage"> 168 <div> 169 <div> 170 <h3 class="title"><a id="repmgr_cxx"></a>Class: excxx_repquote_gsg_simple</h3> 171 </div> 172 </div> 173 </div> 174 <p> 175 Our transactional example will 176 instantiate a class, 177 <code class="classname">SimpleTxn</code>, that performs 178 all our work for us. Before we implement our 179 <code class="function">main()</code> function, we show 180 the <code class="classname">SimpleTxn</code> class 181 declaration. 182 </p> 183 <p> 184 First, we provide some declarations and 185 definitions that are needed later in 186 our example: 187 </p> 188 <pre class="programlisting">#include <iostream> 189#include <db_cxx.h> 190#include "RepConfig.h" 191 192 193using std::cout; 194using std::cin; 195using std::cerr; 196using std::endl; 197using std::flush; 198 199#define CACHESIZE (10 * 1024 * 1024) 200#define DATABASE "quote.db" 201 202const char *progname = "excxx_reqquote_gsg_simple"; 203 204#ifdef _WIN32 205#define WIN32_LEAN_AND_MEAN 206#include <windows.h> 207#include <direct.h> 208 209extern "C" { 210 extern int getopt(int, char * const *, const char *); 211 extern char *optarg; 212} 213#else 214#include <errno.h> 215#endif </pre> 216 <p> 217 And then we define our <code class="classname">SimpleTxn</code> class: 218</p> 219 <pre class="programlisting">class SimpleTxn 220{ 221public: 222 // Constructor. 223 SimpleTxn(); 224 // Initialization method. Creates and opens our environment handle. 225 int init(RepConfigInfo* config); 226 // The doloop is where all the work is performed. 227 int doloop(); 228 // terminate() provides our shutdown code. 229 int terminate(); 230 231private: 232 // disable copy constructor. 233 SimpleTxn(const SimpleTxn &); 234 void operator = (const SimpleTxn &); 235 236 // internal data members. 237 RepConfigInfo *app_config; 238 DbEnv dbenv; 239 240 // private methods. 241 // print_stocks() is used to display the contents of our database. 242 static int print_stocks(Db *dbp); 243}; </pre> 244 <p> 245 Note that we show the implementation of the various 246 <code class="classname">SimpleTxn</code> methods later in this section. 247 </p> 248 </div> 249 <div class="sect2" lang="en" xml:lang="en"> 250 <div class="titlepage"> 251 <div> 252 <div> 253 <h3 class="title"><a id="usage_cxx"></a>Function: usage()</h3> 254 </div> 255 </div> 256 </div> 257 <p> 258 Our <code class="function">usage()</code> is at this 259 stage of development trivial because we only 260 have one command line argument to manage. 261 Still, we show it here for the sake of 262 completeness. 263 </p> 264 <pre class="programlisting">static void usage() 265{ 266 cerr << "usage: " << progname << endl 267 << "-h home" << endl; 268 269 exit(EXIT_FAILURE); 270} </pre> 271 </div> 272 <div class="sect2" lang="en" xml:lang="en"> 273 <div class="titlepage"> 274 <div> 275 <div> 276 <h3 class="title"><a id="main_cxx"></a>Function: main()</h3> 277 </div> 278 </div> 279 </div> 280 <p> 281 Now we provide our <code class="function">main()</code> 282 function. This is a trivial function whose only 283 job is to collect command line information, 284 then instantiate a <code class="classname">SimpleTxn</code> 285 object, run it, then terminate it. 286 </p> 287 <p> 288 We begin by declaring some useful variables. Of 289 these, note that we instantiate our 290 <code class="classname">RepConfigInfo</code> 291 object here. Recall that this is used to store 292 information useful to our code. This class becomes more 293 interesting later in this book. 294 </p> 295 <pre class="programlisting">int main(int argc, char **argv) 296{ 297 RepConfigInfo config; 298 char ch; 299 int ret; </pre> 300 <p> 301 Then we collect our command line information. Again, this is at 302 this point fairly trivial: 303 </p> 304 <pre class="programlisting"> // Extract the command line parameters 305 while ((ch = getopt(argc, argv, "h:")) != EOF) { 306 switch (ch) { 307 case 'h': 308 config.home = optarg; 309 break; 310 case '?': 311 default: 312 usage(); 313 } 314 } 315 316 // Error check command line. 317 if (config.home == NULL) 318 usage(); </pre> 319 <p> 320 Now we instantiate and initialize our <code class="classname">SimpleTxn</code> 321 class, which is what is responsible for doing all our real work. 322 The <code class="methodname">SimpleTxn::init()</code> method creates and 323 opens our environment handle. 324</p> 325 <pre class="programlisting"> SimpleTxn runner; 326 try { 327 if((ret = runner.init(&config)) != 0) 328 goto err; </pre> 329 <p> 330 Then we call the <code class="methodname">SimpleTxn::doloop()</code> 331 method, which is where the actual transactional work is 332 performed for this application. 333 </p> 334 <pre class="programlisting"> if((ret = runner.doloop()) != 0) 335 goto err; </pre> 336 <p> 337 Finally, catch exceptions and terminate the program: 338 </p> 339 <pre class="programlisting"> } catch (DbException dbe) { 340 cerr << "Caught an exception during initialization or" 341 << " processing: " << dbe.what() << endl; 342 } 343err: 344 runner.terminate(); 345 return 0; 346} </pre> 347 </div> 348 <div class="sect2" lang="en" xml:lang="en"> 349 <div class="titlepage"> 350 <div> 351 <div> 352 <h3 class="title"><a id="repmgr_init_cxx"></a>Method: SimpleTxn::init()</h3> 353 </div> 354 </div> 355 </div> 356 <p> 357 The <code class="methodname">SimpleTxn::init()</code> 358 method is used to create and open our environment handle. 359 For readers familiar with writing transactional 360 DB applications, there should be no surprises 361 here. However, we will be adding to this in later 362 chapters as we roll replication into this example. 363 </p> 364 <p> 365 First, we show the class constructor 366 implementation, which is only used to initialize a 367 few variables: 368 </p> 369 <pre class="programlisting">SimpleTxn::SimpleTxn() : app_config(0), dbenv(0) 370{ 371} </pre> 372 <p> 373 We now provide the <code class="methodname">init()</code> method 374 implementation. The only thing of interest here is that we specify 375 <code class="literal">DB_TXN_NOSYNC</code> to our environment. This causes 376 our transactional commits to become non-durable, which is something 377 that we are doing only because of the nature of our example. 378</p> 379 <pre class="programlisting">int SimpleTxn::init(RepConfigInfo *config) 380{ 381 int ret = 0; 382 383 app_config = config; 384 385 dbenv.set_errfile(stderr); 386 dbenv.set_errpfx(progname); 387 388 /* 389 * We can now open our environment. 390 */ 391 dbenv.set_cachesize(0, CACHESIZE, 0); 392 dbenv.set_flags(DB_TXN_NOSYNC, 1); 393 394 try { 395 dbenv.open(app_config->home, 396 DB_CREATE | 397 DB_RECOVER | 398 DB_INIT_LOCK | 399 DB_INIT_LOG | 400 DB_INIT_MPOOL | 401 DB_INIT_TXN, 402 0); 403 } catch(DbException dbe) { 404 cerr << "Caught an exception during DB environment open." << endl 405 << "Ensure that the home directory is created prior to starting" 406 << " the application." << endl; 407 ret = ENOENT; 408 goto err; 409 } 410 411err: 412 return ret; 413} </pre> 414 <p> 415 Finally, we present the <code class="methodname">SimpleTxn::terminate()</code> 416 method here. All this does is close the environment handle. Again, 417 there should be no surprises here, but we provide the 418 implementation for the sake of completeness anyway. 419</p> 420 <pre class="programlisting">int SimpleTxn::terminate() 421{ 422 try { 423 dbenv.close(0); 424 } catch (DbException dbe) { 425 cerr << "error closing environment: " << dbe.what() << endl; 426 } 427 return 0; 428} </pre> 429 </div> 430 <div class="sect2" lang="en" xml:lang="en"> 431 <div class="titlepage"> 432 <div> 433 <div> 434 <h3 class="title"><a id="doloop_cxx"></a>Method: SimpleTxn::doloop()</h3> 435 </div> 436 </div> 437 </div> 438 <p> 439 Having written our <code class="function">main()</code> 440 function and support utility methods, we now implement 441 our application's 442 primary data processing method. This 443 method provides a command prompt at which the 444 user can enter a stock ticker value and a price for 445 that value. This information is then entered to the 446 database. 447 </p> 448 <p> 449 To display the database, simply enter 450 <code class="literal">return</code> at the prompt. 451 </p> 452 <p> 453 To begin, we declare a database pointer, 454 several <code class="classname">Dbt</code> variables, and 455 the usual assortment of variables used for buffers 456 and return codes. We also initialize all of this. 457 </p> 458 <pre class="programlisting">#define BUFSIZE 1024 459int SimpleTxn::doloop() 460{ 461 Db *dbp; 462 Dbt key, data; 463 char buf[BUFSIZE], *rbuf; 464 int ret; 465 466 dbp = NULL; 467 memset(&key, 0, sizeof(key)); 468 memset(&data, 0, sizeof(data)); 469 ret = 0; </pre> 470 <p> 471 Next, we begin the loop and we immediately open our 472 database if it has not already been opened. Notice that 473 we specify autocommit when we open the database. In 474 this case, autocommit is important because we will only 475 ever write to our database using it. There is no need 476 for explicit transaction handles and commit/abort code 477 in this application, because we are not combining 478 multiple database operations together under a single 479 transaction. 480 </p> 481 <p> 482 Autocommit is described in greater detail in the 483 <em class="citetitle">Berkeley DB Getting Started with Transaction Processing</em> guide. 484 </p> 485 <pre class="programlisting"> for (;;) { 486 if (dbp == NULL) { 487 dbp = new Db(&dbenv, 0); 488 489 try { 490 dbp->open(NULL, DATABASE, NULL, DB_BTREE, 491 DB_CREATE | DB_AUTO_COMMIT, 0); 492 } catch(DbException dbe) { 493 dbenv.err(ret, "DB->open"); 494 throw dbe; 495 } 496 } </pre> 497 <p> 498 Now we implement our command prompt. This is a simple and not 499 very robust implementation of a command prompt. 500 If the user enters the keywords <code class="literal">exit</code> 501 or <code class="literal">quit</code>, the loop is exited and the 502 application ends. If the user enters nothing and instead simply 503 presses <code class="literal">return</code>, the entire contents of the 504 database is displayed. We use our 505 <code class="function">print_stocks()</code> method to display the 506 database. (That implementation is shown next in this chapter.) 507 </p> 508 <p> 509 Notice that very little error checking is performed on the data 510 entered at this prompt. If the user fails to enter at least one 511 space in the value string, a simple help message is printed and 512 the prompt is returned to the user. That is the only error 513 checking performed here. In a real-world application, 514 at a minimum the application would probably check to ensure 515 that the price was in fact an integer or float value. 516 However, in order to keep this example code as simple as 517 possible, we refrain from implementing a thorough user interface. 518 </p> 519 <pre class="programlisting"> cout << "QUOTESERVER" ; 520 cout << "> " << flush; 521 522 if (fgets(buf, sizeof(buf), stdin) == NULL) 523 break; 524 if (strtok(&buf[0], " \t\n") == NULL) { 525 switch ((ret = print_stocks(dbp))) { 526 case 0: 527 continue; 528 default: 529 dbp->err(ret, "Error traversing data"); 530 goto err; 531 } 532 } 533 rbuf = strtok(NULL, " \t\n"); 534 if (rbuf == NULL || rbuf[0] == '\0') { 535 if (strncmp(buf, "exit", 4) == 0 || 536 strncmp(buf, "quit", 4) == 0) 537 break; 538 dbenv.errx("Format: TICKER VALUE"); 539 continue; 540 } </pre> 541 <p> 542 Now we assign data to the <code class="classname">Dbt</code>s that 543 we will use to write the new information to the database. 544 </p> 545 <pre class="programlisting"> key.set_data(buf); 546 key.set_size((u_int32_t)strlen(buf)); 547 548 data.set_data(rbuf); 549 data.set_size((u_int32_t)strlen(rbuf)); </pre> 550 <p> 551 Having done that, we can write the new information to the 552 database. Remember that this application uses autocommit, 553 so no explicit transaction management is required. Also, 554 the database is not configured for duplicate records, so 555 the data portion of a record is overwritten if the provided 556 key already exists in the database. However, in this case 557 DB returns <code class="literal">DB_KEYEXIST</code> — which 558 we ignore. 559 </p> 560 <pre class="programlisting"> if ((ret = dbp->put(NULL, &key, &data, 0)) != 0) 561 { 562 dbp->err(ret, "DB->put"); 563 if (ret != DB_KEYEXIST) 564 goto err; 565 } 566 } </pre> 567 <p> 568 Finally, we close our database before returning from the 569 method. 570 </p> 571 <pre class="programlisting">err: if (dbp != NULL) { 572 (void)dbp->close(DB_NOSYNC); 573 cout << "database closed" << endl; 574 } 575 576 return (ret); 577} </pre> 578 </div> 579 <div class="sect2" lang="en" xml:lang="en"> 580 <div class="titlepage"> 581 <div> 582 <div> 583 <h3 class="title"><a id="printstocks_c"></a> 584 585 <span>Method: SimpleTxn::print_stocks()</span> 586 587 </h3> 588 </div> 589 </div> 590 </div> 591 <p> 592 The <code class="function">print_stocks()</code> 593 594 595 <span>method</span> 596 simply takes a database handle, opens a cursor, and uses 597 it to display all the information it finds in a database. 598 This is trivial cursor operation that should hold 599 no surprises for you. We simply provide it here for 600 the sake of completeness. 601 </p> 602 <p> 603 If you are unfamiliar with basic cursor operations, 604 please see the <em class="citetitle">Getting Started with Berkeley DB</em> 605 guide. 606 </p> 607 <pre class="programlisting">int SimpleTxn::print_stocks(Db *dbp) 608{ 609 Dbc *dbc; 610 Dbt key, data; 611#define MAXKEYSIZE 10 612#define MAXDATASIZE 20 613 char keybuf[MAXKEYSIZE + 1], databuf[MAXDATASIZE + 1]; 614 int ret, t_ret; 615 u_int32_t keysize, datasize; 616 617 if ((ret = dbp->cursor(NULL, &dbc, 0)) != 0) { 618 dbp->err(ret, "can't open cursor"); 619 return (ret); 620 } 621 622 memset(&key, 0, sizeof(key)); 623 memset(&data, 0, sizeof(data)); 624 625 cout << "\tSymbol\tPrice" << endl 626 << "\t======\t=====" << endl; 627 628 for (ret = dbc->get(&key, &data, DB_FIRST); 629 ret == 0; 630 ret = dbc->get(&key, &data, DB_NEXT)) { 631 keysize = key.get_size() > MAXKEYSIZE ? MAXKEYSIZE : key.get_size(); 632 memcpy(keybuf, key.get_data(), keysize); 633 keybuf[keysize] = '\0'; 634 635 datasize = data.get_size() >= 636 MAXDATASIZE ? MAXDATASIZE : data.get_size(); 637 memcpy(databuf, data.get_data(), datasize); 638 databuf[datasize] = '\0'; 639 640 cout << "\t" << keybuf << "\t" << databuf << endl; 641 } 642 cout << endl << flush; 643 644 if ((t_ret = dbc->close()) != 0 && ret == 0) { 645 cout << "closed cursor" << endl; 646 ret = t_ret; 647 } 648 649 switch (ret) { 650 case 0: 651 case DB_NOTFOUND: 652 return (0); 653 default: 654 return (ret); 655 } 656} </pre> 657 </div> 658 </div> 659 <div class="navfooter"> 660 <hr /> 661 <table width="100%" summary="Navigation footer"> 662 <tr> 663 <td width="40%" align="left"><a accesskey="p" href="txnapp.html">Prev</a> </td> 664 <td width="20%" align="center"> 665 <a accesskey="u" href="txnapp.html">Up</a> 666 </td> 667 <td width="40%" align="right"> <a accesskey="n" href="repapp.html">Next</a></td> 668 </tr> 669 <tr> 670 <td width="40%" align="left" valign="top">Chapter 2. Transactional Application </td> 671 <td width="20%" align="center"> 672 <a accesskey="h" href="index.html">Home</a> 673 </td> 674 <td width="40%" align="right" valign="top"> Chapter 3. The DB Replication Manager</td> 675 </tr> 676 </table> 677 </div> 678 </body> 679</html> 680