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