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