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>Adding the Replication Framework to 7 8 SimpleTxn 9 </title> 10 <link rel="stylesheet" href="gettingStarted.css" type="text/css" /> 11 <meta name="generator" content="DocBook XSL Stylesheets V1.62.4" /> 12 <link rel="home" href="index.html" title="Getting Started with Replicated Berkeley DB Applications" /> 13 <link rel="up" href="repapp.html" title="Chapter��3.��The DB Replication Framework" /> 14 <link rel="previous" href="repapp.html" title="Chapter��3.��The DB Replication Framework" /> 15 <link rel="next" href="fwrkpermmessage.html" title="Permanent Message Handling" /> 16 </head> 17 <body> 18 <div class="navheader"> 19 <table width="100%" summary="Navigation header"> 20 <tr> 21 <th colspan="3" align="center">Adding the Replication Framework to 22 23 SimpleTxn 24 </th> 25 </tr> 26 <tr> 27 <td width="20%" align="left"><a accesskey="p" href="repapp.html">Prev</a>��</td> 28 <th width="60%" align="center">Chapter��3.��The DB Replication Framework</th> 29 <td width="20%" align="right">��<a accesskey="n" href="fwrkpermmessage.html">Next</a></td> 30 </tr> 31 </table> 32 <hr /> 33 </div> 34 <div class="sect1" lang="en" xml:lang="en"> 35 <div class="titlepage"> 36 <div> 37 <div> 38 <h2 class="title" style="clear: both"><a id="repmgr_init_example_c"></a>Adding the Replication Framework to 39 40 <span>SimpleTxn</span> 41 </h2> 42 </div> 43 </div> 44 <div></div> 45 </div> 46 <p> 47 We now use the methods described above to add partial 48 support to the 49 50 <tt class="literal">SimpleTxn</tt> 51 example that we presented in 52 <a href="txnapp.html">Transactional Application</a>. 53 That is, in this section we will: 54 </p> 55 <div class="itemizedlist"> 56 <ul type="disc"> 57 <li> 58 <p> 59 Enhance our command line options to 60 accept information of interest to a 61 replicated application. 62 </p> 63 </li> 64 <li> 65 <p> 66 Configure our environment handle to use 67 replication and the replication framework. 68 </p> 69 </li> 70 <li> 71 <p> 72 Minimally configure the replication framework. 73 </p> 74 </li> 75 <li> 76 <p> 77 Start replication. 78 </p> 79 </li> 80 </ul> 81 </div> 82 <p> 83 Note that when we are done with this section, we will be 84 only partially ready to run the application. Some critical 85 pieces will be missing; specifically, we will not yet be 86 handling the differences between a master and a 87 replica. (We do that in the next chapter). 88 </p> 89 <p> 90 Also, note that in the following code fragments, additions 91 and changes to the code are marked in <b class="userinput"><tt>bold</tt></b>. 92 </p> 93 <p> 94 To begin, we make some significant changes to our 95 <tt class="classname">RepConfig</tt> class because we will be 96 using it to maintain a lot more information that we needed 97 for our simple transactional example. 98 </p> 99 <p> 100 We begin by changing our package name and then importing a few new 101 classes. <tt class="classname">java.util.Vector</tt> is used to 102 organize a list of "other host" definitions (that is, the host and 103 port information for the other replication participants known to 104 this application). We also need a couple of classes used to manage 105 individual host and port information, as well as replication 106 startup policy information. 107</p> 108 <pre class="programlisting"><b class="userinput"><tt>package db.repquote; 109 110import java.util.Vector; 111 112import com.sleepycat.db.ReplicationHostAddress; 113import com.sleepycat.db.ReplicationManagerStartPolicy;</tt></b> 114 115public class RepConfig 116{ </pre> 117 <p> 118 Next we add considerably to the constants and data members used by 119 this class. All of this is used to manage information necessary for 120 replication purposes. We also at this point change the program's 121 name, since we will be doing that to the main class in our 122 application a little later in this description. 123</p> 124 <pre class="programlisting"> // Constant values used in the RepQuote application. 125 public static final String progname = <b class="userinput"><tt>"RepQuoteExample";</tt></b> 126 public static final int CACHESIZE = 10 * 1024 * 1024; 127 <b class="userinput"><tt>public static final int SLEEPTIME = 5000;</tt></b> 128 129 // member variables containing configuration information 130 public String home; // String specifying the home directory for rep files. 131 <b class="userinput"><tt>public Vector otherHosts; // stores an optional set of "other" hosts. 132 public int priority; // priority within the replication group. 133 public ReplicationManagerStartPolicy startPolicy; 134 public ReplicationHostAddress thisHost; // The host address to listen to. 135 // Optional parameter specifying the # of sites in the replication group. 136 public int totalSites; 137 138 // member variables used internally. 139 private int currOtherHost; 140 private boolean gotListenAddress;</tt></b></pre> 141 <p> 142 Now we update our class constructor to initialize all of these new 143 variables: 144</p> 145 <pre class="programlisting"> public RepConfig() 146 { 147 <b class="userinput"><tt>startPolicy = ReplicationManagerStartPolicy.REP_ELECTION;</tt></b> 148 home = "TESTDIR"; 149 <b class="userinput"><tt>gotListenAddress = false; 150 totalSites = 0; 151 priority = 100; 152 currOtherHost = 0; 153 thisHost = new ReplicationHostAddress(); 154 otherHosts = new Vector();</tt></b> 155 } </pre> 156 <p> 157 Finally, we finish updating this class by providing a series of new 158 getter and setter methods. These are used primarily for setting a 159 retrieving host information of interest to our replicated 160 application: 161</p> 162 <pre class="programlisting"> public java.io.File getHome() 163 { 164 return new java.io.File(home); 165 } 166 167 <b class="userinput"><tt>public void setThisHost(String host, int port) 168 { 169 gotListenAddress = true; 170 thisHost.port = port; 171 thisHost.host = host; 172 } 173 174 public ReplicationHostAddress getThisHost() 175 { 176 if (!gotListenAddress) { 177 System.err.println("Warning: no host specified."); 178 System.err.println("Returning default."); 179 } 180 return thisHost; 181 } 182 183 public boolean gotListenAddress() { 184 return gotListenAddress; 185 } 186 187 public void addOtherHost(String host, int port, boolean peer) 188 { 189 ReplicationHostAddress newInfo = 190 new ReplicationHostAddress(host, port, peer); 191 otherHosts.add(newInfo); 192 } 193 194 public ReplicationHostAddress getFirstOtherHost() 195 { 196 currOtherHost = 0; 197 if (otherHosts.size() == 0) 198 return null; 199 return (ReplicationHostAddress)otherHosts.get(currOtherHost); 200 } 201 202 public ReplicationHostAddress getNextOtherHost() 203 { 204 currOtherHost++; 205 if (currOtherHost >= otherHosts.size()) 206 return null; 207 return (ReplicationHostAddress)otherHosts.get(currOtherHost); 208 } 209 210 public ReplicationHostAddress getOtherHost(int i) 211 { 212 if (i >= otherHosts.size()) 213 return null; 214 return (ReplicationHostAddress)otherHosts.get(i); 215 }</tt></b> 216} </pre> 217 <p> 218 Having completed our update to the 219 220 <tt class="classname">RepConfig</tt> 221 class, we can now start making 222 changes to the main portion of our program. We begin by changing 223 the program's name. <span>(This, of course, means that we copy our 224<tt class="classname">SimpleTxn</tt> code to a file named <tt class="literal">RepQuoteExample.java</tt>.)</span> 225</p> 226 <pre class="programlisting">package <b class="userinput"><tt>db.repquote</tt></b>; 227 228import java.io.FileNotFoundException; 229import java.io.BufferedReader; 230import java.io.InputStreamReader; 231import java.io.IOException; 232import java.io.UnsupportedEncodingException; 233<b class="userinput"><tt>import java.lang.Thread; 234import java.lang.InterruptedException;</tt></b> 235 236import com.sleepycat.db.Cursor; 237import com.sleepycat.db.Database; 238import com.sleepycat.db.DatabaseConfig; 239import com.sleepycat.db.DatabaseEntry; 240import com.sleepycat.db.DatabaseException; 241import com.sleepycat.db.DatabaseType; 242import com.sleepycat.db.Environment; 243import com.sleepycat.db.EnvironmentConfig; 244import com.sleepycat.db.LockMode; 245import com.sleepycat.db.OperationStatus; 246import db.repquote.RepConfig; 247 248public class <b class="userinput"><tt>RepQuoteExample</tt></b> 249{ 250 private RepConfig repConfig; 251 private Environment dbenv; </pre> 252 <p> 253 Next we update our usage function. The application will continue to 254 accept the <tt class="literal">-h</tt> parameter so that we can identify 255 the environment home directory used by this application. However, 256 we also add the 257</p> 258 <div class="itemizedlist"> 259 <ul type="disc"> 260 <li> 261 <p> 262 <tt class="literal">-m</tt> parameter which allows us to 263 identify the host and port used by this application to 264 listen for replication messages. 265 </p> 266 </li> 267 <li> 268 <p> 269 <tt class="literal">-o</tt> parameter which allows us to 270 specify other replicas. 271 </p> 272 </li> 273 <li> 274 <p> 275 <tt class="literal">-n</tt> parameter which allows us to 276 identify the number of sites in this replication 277 group. 278 </p> 279 </li> 280 <li> 281 <p> 282 <tt class="literal">-p</tt> option, which is used to identify 283 this replica's priority (recall that the priority is 284 used as a tie breaker for elections) 285 </p> 286 </li> 287 </ul> 288 </div> 289 <pre class="programlisting"> 290 public <b class="userinput"><tt>RepQuoteExample()</tt></b> 291 throws DatabaseException 292 { 293 repConfig = null; 294 dbenv = null; 295 } 296 297 public static void usage() 298 { 299 System.err.println("usage: " + repConfig.progname); 300 System.err.println("[-h home]<b class="userinput"><tt>[-o host:port][-m host:port]" + 301 "[-f host:port][-n nsites][-p priority]</tt></b>"); 302 303 System.err.println(<b class="userinput"><tt>"\t -m host:port (required; m stands for me)\n" + 304 "\t -o host:port (optional; o stands for other; any " + 305 "number of these may be specified)\n" +</tt></b> 306 "\t -h home directory\n" + 307 <b class="userinput"><tt>"\t -n nsites (optional; number of sites in replication " + 308 "group; defaults to 0\n" + 309 "\t in which case we try to dynamically compute the " + 310 "number of sites in\n" + 311 "\t the replication group)\n" + 312 "\t -p priority (optional: defaults to 100)\n");</tt></b> 313 314 System.exit(1); 315 } </pre> 316 <p> 317 Now we can begin working on our <tt class="literal">main()</tt> function. 318 We begin by adding a couple of variables that we will use to 319 collect TCP/IP host and port information. 320 321 322 323</p> 324 <pre class="programlisting"> public static void main(String[] argv) 325 throws Exception 326 { 327 RepConfig config = new RepConfig(); 328 <b class="userinput"><tt>String tmpHost; 329 int tmpPort = 0;</tt></b> </pre> 330 <p> 331 Now we collect our command line arguments. As we do so, we will 332 configure host and port information as required, and we will 333 configure the application's election priority if necessary. 334</p> 335 <pre class="programlisting"> // Extract the command line parameters 336 for (int i = 0; i < argv.length; i++) 337 { 338 if (argv[i].compareTo("-h") == 0) { 339 // home - a string arg. 340 i++; 341 config.home = argv[i]; 342 <b class="userinput"><tt>} else if (argv[i].compareTo("-m") == 0) { 343 // "me" should be host:port 344 i++; 345 String[] words = argv[i].split(":"); 346 if (words.length != 2) { 347 System.err.println( 348 "Invalid host specification host:port needed."); 349 usage(); 350 } 351 try { 352 tmpPort = Integer.parseInt(words[1]); 353 } catch (NumberFormatException nfe) { 354 System.err.println("Invalid host specification, " + 355 "could not parse port number."); 356 usage(); 357 } 358 config.setThisHost(words[0], tmpPort); 359 } else if (argv[i].compareTo("-n") == 0) { 360 i++; 361 config.totalSites = Integer.parseInt(argv[i]); 362 } else if (argv[i].compareTo("-o") == 0) { 363 i++; 364 String[] words = argv[i].split(":"); 365 if (words.length != 2) { 366 System.err.println( 367 "Invalid host specification host:port needed."); 368 usage(); 369 } 370 try { 371 tmpPort = Integer.parseInt(words[1]); 372 } catch (NumberFormatException nfe) { 373 System.err.println("Invalid host specification, " + 374 "could not parse port number."); 375 usage(); 376 } 377 config.addOtherHost(words[0], tmpPort, isPeer); 378 } else if (argv[i].compareTo("-p") == 0) { 379 i++; 380 config.priority = Integer.parseInt(argv[i]); 381 }</tt></b> else { 382 System.err.println("Unrecognized option: " + argv[i]); 383 usage(); 384 } 385 } 386 387 // Error check command line. 388 if (<b class="userinput"><tt>(!config.gotListenAddress())</tt></b> || config.home.length() == 0) 389 usage(); </pre> 390 <p> 391 Having done that, the remainder of our <tt class="function">main()</tt> 392 function is left unchanged, with the exception of a few name changes required by the 393 new class name: 394</p> 395 <pre class="programlisting"> <b class="userinput"><tt>RepQuoteExample</tt></b> runner = null; 396 try { 397 runner = new <b class="userinput"><tt>RepQuoteExample();</tt></b> 398 runner.init(config); 399 400 runner.doloop(); 401 runner.terminate(); 402 } catch (DatabaseException dbe) { 403 System.err.println("Caught an exception during " + 404 "initialization or processing: " + dbe.toString()); 405 if (runner != null) 406 runner.terminate(); 407 } 408 System.exit(0); 409 } // end main </pre> 410 <p> 411 Now we need to update our 412 413 <tt class="methodname">RepQuoteExample.init()</tt> 414 method. Our updates are at first related to configuring 415 replication. First, we need to update the method so that we can 416 identify the local site to the environment handle (that is, the site identified by the 417<tt class="literal">-m</tt> command line option): 418</p> 419 <pre class="programlisting"> public int init(RepConfig config) 420 throws DatabaseException 421 { 422 int ret = 0; 423 appConfig = config; 424 EnvironmentConfig envConfig = new EnvironmentConfig(); 425 envConfig.setErrorStream(System.err); 426 envConfig.setErrorPrefix(RepConfig.progname); 427 428 <b class="userinput"><tt>envConfig.setReplicationManagerLocalSite(appConfig.getThisHost());</tt></b> </pre> 429 <p> 430 And we also add code to allow us to identify "other" sites to the environment handle (that is, 431the sites that we identify using the <tt class="literal">-o</tt> command line 432option). To do this, we iterate over each of the "other" sites provided to 433us using the <tt class="literal">-o</tt> command line option, and we add each one 434individually in turn: 435</p> 436 <pre class="programlisting"> <b class="userinput"><tt>for (ReplicationHostAddress host = appConfig.getFirstOtherHost(); 437 host != null; host = appConfig.getNextOtherHost()) 438 { 439 envConfig.replicationManagerAddRemoteSite(host); 440 }</tt></b> </pre> 441 <p> 442 And then we need code to allow us to identify the total number of sites 443 in this replication group, and to set the environment's priority. 444</p> 445 <pre class="programlisting"> <b class="userinput"><tt>if (appConfig.totalSites > 0) 446 envConfig.setReplicationNumSites(appConfig.totalSites); 447 envConfig.setReplicationPriority(appConfig.priority); </tt></b> </pre> 448 <p> 449 450 451 452 453 <span>We can now open our environment. Note that the options</span> 454 455 we use to open the environment are slightly different for a 456 replicated application than they are for a non-replicated 457 application. Namely, replication requires the 458 459 460 <span> 461 <tt class="methodname">EnvironmentConfig.setInitializeReplication()</tt> option. 462 </span> 463 </p> 464 <p> 465 Also, because we are using the replication framework, we must prepare 466 our environment for threaded usage. For this reason, we also 467 need the <tt class="literal">DB_THREAD</tt> flag. 468 </p> 469 <pre class="programlisting"> envConfig.setCacheSize(RepConfig.CACHESIZE); 470 envConfig.setTxnNoSync(true); 471 472 envConfig.setAllowCreate(true); 473 envConfig.setRunRecovery(true); 474 <b class="userinput"><tt>envConfig.setInitializeReplication(true);</tt></b> 475 envConfig.setInitializeLocking(true); 476 envConfig.setInitializeLogging(true); 477 envConfig.setInitializeCache(true); 478 envConfig.setTransactional(true); 479 try { 480 dbenv = new Environment(appConfig.getHome(), envConfig); 481 } catch(FileNotFoundException e) { 482 System.err.println("FileNotFound exception: " + e.toString()); 483 System.err.println( 484 "Ensure that the environment directory is pre-created."); 485 ret = 1; 486 } </pre> 487 <p> 488 Finally, we start replication before we exit this method. 489 Immediately after exiting this method, our application will go into 490 the 491 492 <tt class="methodname">RepQuoteExample.doloop()</tt> 493 method, which is where 494 the bulk of our application's work is performed. We update that 495 method in the next chapter. 496</p> 497 <pre class="programlisting"> // start replication manager 498 dbenv.replicationManagerStart(3, appConfig.startPolicy); 499 return ret; 500 } </pre> 501 <p> 502 This completes our replication updates for the moment. We are not as 503 yet ready to actually run this program; there remains a few 504 critical pieces left to add to it. However, the work that we 505 performed in this section represents a solid foundation for the 506 remainder of our replication work. 507 </p> 508 </div> 509 <div class="navfooter"> 510 <hr /> 511 <table width="100%" summary="Navigation footer"> 512 <tr> 513 <td width="40%" align="left"><a accesskey="p" href="repapp.html">Prev</a>��</td> 514 <td width="20%" align="center"> 515 <a accesskey="u" href="repapp.html">Up</a> 516 </td> 517 <td width="40%" align="right">��<a accesskey="n" href="fwrkpermmessage.html">Next</a></td> 518 </tr> 519 <tr> 520 <td width="40%" align="left" valign="top">Chapter��3.��The DB Replication Framework��</td> 521 <td width="20%" align="center"> 522 <a accesskey="h" href="index.html">Home</a> 523 </td> 524 <td width="40%" align="right" valign="top">��Permanent Message Handling</td> 525 </tr> 526 </table> 527 </div> 528 </body> 529</html> 530